[ivy] 01/06: Imported Upstream version 2.4.0
Markus Koschany
apo-guest at moszumanska.debian.org
Wed Dec 16 12:51:02 UTC 2015
This is an automated email from the git hooks/post-receive script.
apo-guest pushed a commit to annotated tag debian/2.4.0-3
in repository ivy.
commit 1bd8bb1dc55ffa5a2bf06e9843584a12ea418780
Author: Markus Koschany <apo at debian.org>
Date: Wed Dec 16 00:38:44 2015 +0100
Imported Upstream version 2.4.0
---
LICENSE | 258 ++++
META-INF/MANIFEST.MF | 112 ++
NOTICE | 16 +
README | 95 ++
ant.patterns | 24 +
build.properties | 56 +
build.xml | 690 +++++++++
ivy.xml | 70 +
ivy.xsd | 291 ++++
optional.patterns | 37 +
src/etc/checkstyle/RequiredHeader.txt | 17 +
src/etc/checkstyle/checkstyle-config | 152 ++
src/etc/checkstyle/checkstyle-frames.xsl | 293 ++++
src/etc/checkstyle/checkstyle-suppress.xml | 25 +
src/etc/checkstyle/checkstyle-text.xsl | 33 +
src/etc/checkstyle/checkstyle-xdoc.xsl | 129 ++
src/etc/license/license-header.xml | 18 +
src/etc/makepom/pom.template | 59 +
src/example/bintray/build.xml | 23 +
src/example/bintray/ivy.xml | 31 +
src/example/bintray/ivysettings.xml | 37 +
src/example/build-a-ivy-repository/build.xml | 120 ++
.../settings/ivysettings-advanced.xml | 161 ++
.../settings/ivysettings-basic.xml | 30 +
src/example/chained-resolvers/build.xml | 38 +
.../chainedresolvers-project/build.xml | 81 +
.../chainedresolvers-project/ivy.xml | 25 +
.../src/example/Hello.java | 37 +
.../chained-resolvers/settings/ivysettings.xml | 29 +
src/example/configurations/jdbc-example/build.xml | 97 ++
src/example/configurations/jdbc-example/ivy.xml | 44 +
.../configurations/jdbc-example/src/dev.properties | 19 +
.../src/example/ConfigurationsExample.java | 64 +
.../jdbc-example/src/prod.properties | 19 +
.../multi-projects/filter-framework/build.xml | 140 ++
.../multi-projects/filter-framework/ivy.xml | 36 +
.../src/filter/FilterProvider.java | 40 +
.../filter-framework/src/filter/IFilter.java | 22 +
.../src/filter/ccimpl/CCFilter.java | 45 +
.../src/filter/hmimpl/HMFilter.java | 42 +
.../configurations/multi-projects/myapp/build.xml | 93 ++
.../configurations/multi-projects/myapp/ivy.xml | 31 +
.../multi-projects/myapp/src/myapp/Main.java | 36 +
src/example/dependence/build.xml | 42 +
src/example/dependence/dependee/build.xml | 110 ++
src/example/dependence/dependee/ivy.xml | 24 +
.../dependence/dependee/src/standalone/Main.java | 64 +
src/example/dependence/depender/build.xml | 93 ++
src/example/dependence/depender/ivy.xml | 24 +
.../dependence/depender/src/depending/Main.java | 40 +
.../dependence/settings/ivysettings.properties | 19 +
src/example/dependence/settings/ivysettings.xml | 33 +
src/example/dual/build.xml | 38 +
src/example/dual/project/build.xml | 81 +
src/example/dual/project/ivy.xml | 25 +
src/example/dual/project/src/example/Hello.java | 51 +
.../repository/commons-httpclient-ivy-2.0.2.xml | 29 +
src/example/dual/settings/ivysettings.xml | 29 +
src/example/go-ivy/build.xml | 151 ++
src/example/hello-ivy/build.xml | 80 +
src/example/hello-ivy/ivy.xml | 25 +
src/example/hello-ivy/src/example/Hello.java | 51 +
src/example/multi-project/build.xml | 69 +
src/example/multi-project/common/build.properties | 30 +
src/example/multi-project/common/common.xml | 205 +++
.../projects/console/build.properties | 21 +
.../multi-project/projects/console/build.xml | 23 +
src/example/multi-project/projects/console/ivy.xml | 30 +
.../projects/console/src/console/Main.java | 82 ++
.../multi-project/projects/find/build.properties | 21 +
src/example/multi-project/projects/find/build.xml | 23 +
src/example/multi-project/projects/find/ivy.xml | 37 +
.../projects/find/src/find/FindFile.java | 48 +
.../multi-project/projects/find/src/find/Main.java | 76 +
.../multi-project/projects/list/build.properties | 21 +
src/example/multi-project/projects/list/build.xml | 23 +
src/example/multi-project/projects/list/ivy.xml | 35 +
.../projects/list/src/list/ListFile.java | 50 +
.../multi-project/projects/list/src/list/Main.java | 71 +
.../multi-project/projects/size/build.properties | 21 +
src/example/multi-project/projects/size/build.xml | 23 +
src/example/multi-project/projects/size/ivy.xml | 28 +
.../projects/size/src/size/FileSize.java | 45 +
.../projects/sizewhere/build.properties | 21 +
.../multi-project/projects/sizewhere/build.xml | 23 +
.../multi-project/projects/sizewhere/ivy.xml | 37 +
.../projects/sizewhere/src/sizewhere/Main.java | 71 +
.../sizewhere/src/sizewhere/SizeWhere.java | 37 +
.../projects/version/build.properties | 21 +
.../multi-project/projects/version/build.xml | 23 +
src/example/multi-project/projects/version/ivy.xml | 24 +
.../projects/version/src/version/Version.java | 51 +
src/example/packager-resolver/ivysettings.xml | 35 +
src/java/org/apache/ivy/Ivy.java | 975 +++++++++++++
src/java/org/apache/ivy/Ivy14.java | 502 +++++++
src/java/org/apache/ivy/Main.java | 591 ++++++++
src/java/org/apache/ivy/ant/AddPathTask.java | 103 ++
src/java/org/apache/ivy/ant/AntBuildTrigger.java | 172 +++
src/java/org/apache/ivy/ant/AntCallTrigger.java | 130 ++
src/java/org/apache/ivy/ant/AntMessageLogger.java | 150 ++
src/java/org/apache/ivy/ant/BuildOBRTask.java | 208 +++
.../org/apache/ivy/ant/ConvertManifestTask.java | 96 ++
src/java/org/apache/ivy/ant/FixDepsTask.java | 90 ++
src/java/org/apache/ivy/ant/IvyAntSettings.java | 391 +++++
.../apache/ivy/ant/IvyAntVariableContainer.java | 106 ++
.../org/apache/ivy/ant/IvyArtifactProperty.java | 97 ++
src/java/org/apache/ivy/ant/IvyArtifactReport.java | 281 ++++
src/java/org/apache/ivy/ant/IvyBuildList.java | 569 ++++++++
src/java/org/apache/ivy/ant/IvyBuildNumber.java | 386 +++++
src/java/org/apache/ivy/ant/IvyCacheFileset.java | 204 +++
src/java/org/apache/ivy/ant/IvyCachePath.java | 116 ++
src/java/org/apache/ivy/ant/IvyCacheTask.java | 101 ++
src/java/org/apache/ivy/ant/IvyCheck.java | 98 ++
src/java/org/apache/ivy/ant/IvyCleanCache.java | 85 ++
src/java/org/apache/ivy/ant/IvyConfigure.java | 157 ++
src/java/org/apache/ivy/ant/IvyConflict.java | 77 +
src/java/org/apache/ivy/ant/IvyConvertPom.java | 77 +
src/java/org/apache/ivy/ant/IvyDeliver.java | 452 ++++++
src/java/org/apache/ivy/ant/IvyDependency.java | 193 +++
.../org/apache/ivy/ant/IvyDependencyArtifact.java | 67 +
src/java/org/apache/ivy/ant/IvyDependencyConf.java | 64 +
.../org/apache/ivy/ant/IvyDependencyExclude.java | 75 +
.../org/apache/ivy/ant/IvyDependencyInclude.java | 62 +
src/java/org/apache/ivy/ant/IvyDependencyTree.java | 132 ++
.../apache/ivy/ant/IvyDependencyUpdateChecker.java | 214 +++
src/java/org/apache/ivy/ant/IvyExclude.java | 75 +
.../org/apache/ivy/ant/IvyExtractFromSources.java | 217 +++
src/java/org/apache/ivy/ant/IvyFindRevision.java | 104 ++
src/java/org/apache/ivy/ant/IvyInfo.java | 212 +++
src/java/org/apache/ivy/ant/IvyInstall.java | 228 +++
src/java/org/apache/ivy/ant/IvyListModules.java | 158 ++
src/java/org/apache/ivy/ant/IvyMakePom.java | 308 ++++
src/java/org/apache/ivy/ant/IvyOverride.java | 66 +
.../org/apache/ivy/ant/IvyPostResolveTask.java | 460 ++++++
src/java/org/apache/ivy/ant/IvyPublish.java | 481 ++++++
src/java/org/apache/ivy/ant/IvyReport.java | 434 ++++++
.../org/apache/ivy/ant/IvyRepositoryReport.java | 328 +++++
src/java/org/apache/ivy/ant/IvyResolve.java | 517 +++++++
src/java/org/apache/ivy/ant/IvyResources.java | 118 ++
src/java/org/apache/ivy/ant/IvyRetrieve.java | 234 +++
src/java/org/apache/ivy/ant/IvyTask.java | 297 ++++
src/java/org/apache/ivy/ant/IvyVar.java | 132 ++
src/java/org/apache/ivy/ant/MapperAdapter.java | 42 +
src/java/org/apache/ivy/ant/PackageMapping.java | 69 +
src/java/org/apache/ivy/ant/antlib.xml | 52 +
.../ivy/core/ExecutionRelativeUrlResolver.java | 34 +
src/java/org/apache/ivy/core/IvyContext.java | 386 +++++
src/java/org/apache/ivy/core/IvyPatternHelper.java | 526 +++++++
src/java/org/apache/ivy/core/IvyThread.java | 70 +
src/java/org/apache/ivy/core/LogOptions.java | 57 +
.../apache/ivy/core/NormalRelativeUrlResolver.java | 32 +
.../org/apache/ivy/core/RelativeUrlResolver.java | 67 +
.../org/apache/ivy/core/cache/ArtifactOrigin.java | 174 +++
.../ivy/core/cache/CacheDownloadOptions.java | 43 +
.../ivy/core/cache/CacheMetadataOptions.java | 88 ++
.../ivy/core/cache/CacheResourceOptions.java | 32 +
src/java/org/apache/ivy/core/cache/CacheUtil.java | 50 +
.../core/cache/DefaultRepositoryCacheManager.java | 1543 ++++++++++++++++++++
.../core/cache/DefaultResolutionCacheManager.java | 304 ++++
.../apache/ivy/core/cache/DownloadListener.java | 34 +
.../core/cache/ModuleDescriptorMemoryCache.java | 134 ++
.../ivy/core/cache/ModuleDescriptorProvider.java | 31 +
.../ivy/core/cache/ModuleDescriptorWriter.java | 30 +
.../ivy/core/cache/ParserSettingsMonitor.java | 154 ++
.../ivy/core/cache/RepositoryCacheManager.java | 194 +++
.../ivy/core/cache/ResolutionCacheManager.java | 48 +
.../org/apache/ivy/core/check/CheckEngine.java | 135 ++
.../apache/ivy/core/check/CheckEngineSettings.java | 29 +
.../core/deliver/DefaultPublishingDRResolver.java | 28 +
.../org/apache/ivy/core/deliver/DeliverEngine.java | 214 +++
.../ivy/core/deliver/DeliverEngineSettings.java | 26 +
.../apache/ivy/core/deliver/DeliverOptions.java | 270 ++++
.../PublishingDependencyRevisionResolver.java | 41 +
.../org/apache/ivy/core/event/EventManager.java | 95 ++
.../apache/ivy/core/event/FilteredIvyListener.java | 46 +
src/java/org/apache/ivy/core/event/IvyEvent.java | 127 ++
.../org/apache/ivy/core/event/IvyEventFilter.java | 159 ++
.../org/apache/ivy/core/event/IvyListener.java | 24 +
.../ivy/core/event/download/DownloadEvent.java | 42 +
.../event/download/EndArtifactDownloadEvent.java | 63 +
.../ivy/core/event/download/NeedArtifactEvent.java | 38 +
.../core/event/download/PrepareDownloadEvent.java | 36 +
.../event/download/StartArtifactDownloadEvent.java | 49 +
.../event/publish/EndArtifactPublishEvent.java | 56 +
.../ivy/core/event/publish/PublishEvent.java | 77 +
.../event/publish/StartArtifactPublishEvent.java | 42 +
.../event/resolve/EndResolveDependencyEvent.java | 75 +
.../ivy/core/event/resolve/EndResolveEvent.java | 43 +
.../core/event/resolve/ResolveDependencyEvent.java | 55 +
.../ivy/core/event/resolve/ResolveEvent.java | 36 +
.../event/resolve/StartResolveDependencyEvent.java | 32 +
.../ivy/core/event/resolve/StartResolveEvent.java | 29 +
.../event/retrieve/EndRetrieveArtifactEvent.java | 30 +
.../ivy/core/event/retrieve/EndRetrieveEvent.java | 84 ++
.../core/event/retrieve/RetrieveArtifactEvent.java | 55 +
.../ivy/core/event/retrieve/RetrieveEvent.java | 47 +
.../event/retrieve/StartRetrieveArtifactEvent.java | 30 +
.../core/event/retrieve/StartRetrieveEvent.java | 29 +
.../org/apache/ivy/core/install/InstallEngine.java | 211 +++
.../ivy/core/install/InstallEngineSettings.java | 46 +
.../apache/ivy/core/install/InstallOptions.java | 101 ++
.../core/module/descriptor/AbstractArtifact.java | 81 +
.../descriptor/AbstractIncludeExcludeRule.java | 94 ++
.../ivy/core/module/descriptor/Artifact.java | 100 ++
.../ivy/core/module/descriptor/Configuration.java | 247 ++++
.../core/module/descriptor/ConfigurationAware.java | 39 +
.../core/module/descriptor/ConfigurationGroup.java | 71 +
.../descriptor/ConfigurationIntersection.java | 72 +
.../core/module/descriptor/DefaultArtifact.java | 146 ++
.../DefaultDependencyArtifactDescriptor.java | 121 ++
.../descriptor/DefaultDependencyDescriptor.java | 706 +++++++++
.../core/module/descriptor/DefaultExcludeRule.java | 34 +
.../descriptor/DefaultExtendsDescriptor.java | 93 ++
.../core/module/descriptor/DefaultIncludeRule.java | 34 +
.../module/descriptor/DefaultModuleDescriptor.java | 883 +++++++++++
.../descriptor/DependencyArtifactDescriptor.java | 70 +
.../module/descriptor/DependencyDescriptor.java | 141 ++
.../descriptor/DependencyDescriptorMediator.java | 44 +
.../ivy/core/module/descriptor/ExcludeRule.java | 50 +
.../core/module/descriptor/ExtendsDescriptor.java | 68 +
.../core/module/descriptor/ExtraInfoHolder.java | 76 +
.../ivy/core/module/descriptor/IncludeRule.java | 51 +
.../core/module/descriptor/InheritableItem.java | 34 +
.../apache/ivy/core/module/descriptor/License.java | 38 +
.../ivy/core/module/descriptor/MDArtifact.java | 126 ++
.../core/module/descriptor/ModuleDescriptor.java | 293 ++++
.../OverrideDependencyDescriptorMediator.java | 85 ++
.../org/apache/ivy/core/module/id/ArtifactId.java | 107 ++
.../ivy/core/module/id/ArtifactRevisionId.java | 121 ++
.../apache/ivy/core/module/id/MatcherLookup.java | 153 ++
.../org/apache/ivy/core/module/id/ModuleId.java | 230 +++
.../ivy/core/module/id/ModuleRevisionId.java | 343 +++++
.../org/apache/ivy/core/module/id/ModuleRules.java | 250 ++++
.../org/apache/ivy/core/module/id/package.html | 61 +
.../org/apache/ivy/core/module/status/Status.java | 48 +
.../ivy/core/module/status/StatusManager.java | 151 ++
.../org/apache/ivy/core/pack/ArchivePacking.java | 32 +
.../apache/ivy/core/pack/OsgiBundlePacking.java | 48 +
.../org/apache/ivy/core/pack/Pack200Packing.java | 55 +
.../org/apache/ivy/core/pack/PackagingManager.java | 115 ++
.../org/apache/ivy/core/pack/PackingRegistry.java | 43 +
.../org/apache/ivy/core/pack/StreamPacking.java | 35 +
src/java/org/apache/ivy/core/pack/ZipPacking.java | 99 ++
.../org/apache/ivy/core/publish/PublishEngine.java | 290 ++++
.../ivy/core/publish/PublishEngineSettings.java | 32 +
.../apache/ivy/core/publish/PublishOptions.java | 183 +++
.../ivy/core/report/ArtifactDownloadReport.java | 195 +++
.../core/report/ConfigurationResolveReport.java | 365 +++++
.../org/apache/ivy/core/report/DownloadReport.java | 57 +
.../org/apache/ivy/core/report/DownloadStatus.java | 62 +
.../report/MetadataArtifactDownloadReport.java | 63 +
.../org/apache/ivy/core/report/ResolveReport.java | 436 ++++++
.../repository/RepositoryManagementEngine.java | 339 +++++
.../RepositoryManagementEngineSettings.java | 24 +
.../apache/ivy/core/resolve/DownloadOptions.java | 27 +
src/java/org/apache/ivy/core/resolve/IvyNode.java | 1344 +++++++++++++++++
.../apache/ivy/core/resolve/IvyNodeBlacklist.java | 68 +
.../apache/ivy/core/resolve/IvyNodeCallers.java | 296 ++++
.../apache/ivy/core/resolve/IvyNodeEviction.java | 439 ++++++
.../org/apache/ivy/core/resolve/IvyNodeUsage.java | 326 +++++
.../org/apache/ivy/core/resolve/ResolveData.java | 351 +++++
.../org/apache/ivy/core/resolve/ResolveEngine.java | 1219 ++++++++++++++++
.../ivy/core/resolve/ResolveEngineSettings.java | 45 +
.../apache/ivy/core/resolve/ResolveOptions.java | 312 ++++
.../ivy/core/resolve/ResolveProcessException.java | 46 +
.../ivy/core/resolve/ResolvedModuleRevision.java | 143 ++
.../ivy/core/resolve/RestartResolveProcess.java | 34 +
.../org/apache/ivy/core/resolve/VisitData.java | 72 +
.../org/apache/ivy/core/resolve/VisitNode.java | 519 +++++++
.../apache/ivy/core/retrieve/FileNameMapper.java | 24 +
.../apache/ivy/core/retrieve/RetrieveEngine.java | 496 +++++++
.../ivy/core/retrieve/RetrieveEngineSettings.java | 31 +
.../apache/ivy/core/retrieve/RetrieveOptions.java | 210 +++
.../apache/ivy/core/retrieve/RetrieveReport.java | 101 ++
.../org/apache/ivy/core/search/ModuleEntry.java | 51 +
.../apache/ivy/core/search/OrganisationEntry.java | 43 +
.../org/apache/ivy/core/search/RevisionEntry.java | 59 +
.../org/apache/ivy/core/search/SearchEngine.java | 437 ++++++
.../org/apache/ivy/core/settings/IvyPattern.java | 33 +
.../org/apache/ivy/core/settings/IvySettings.java | 1516 +++++++++++++++++++
.../ivy/core/settings/IvyVariableContainer.java | 39 +
.../core/settings/IvyVariableContainerImpl.java | 101 ++
.../org/apache/ivy/core/settings/Validatable.java | 32 +
.../ivy/core/settings/XmlSettingsParser.java | 648 ++++++++
.../org/apache/ivy/core/settings/ivy.properties | 43 +
.../apache/ivy/core/settings/ivysettings-1.4.xml | 28 +
.../core/settings/ivysettings-default-chain.xml | 26 +
.../apache/ivy/core/settings/ivysettings-local.xml | 29 +
.../ivy/core/settings/ivysettings-main-chain.xml | 26 +
.../ivy/core/settings/ivysettings-public.xml | 23 +
.../ivy/core/settings/ivysettings-shared.xml | 29 +
.../org/apache/ivy/core/settings/ivysettings.xml | 26 +
.../apache/ivy/core/settings/repository.properties | 24 +
.../apache/ivy/core/settings/typedef.properties | 67 +
.../ivy/core/sort/CollectionOfModulesToSort.java | 111 ++
.../MessageBasedNonMatchingVersionReporter.java | 46 +
.../ivy/core/sort/ModuleDescriptorSorter.java | 105 ++
.../org/apache/ivy/core/sort/ModuleInSort.java | 187 +++
.../ivy/core/sort/NonMatchingVersionReporter.java | 36 +
.../sort/SilentNonMatchingVersionReporter.java | 32 +
.../ivy/core/sort/SimpleSortEngineSettings.java | 45 +
src/java/org/apache/ivy/core/sort/SortEngine.java | 127 ++
.../apache/ivy/core/sort/SortEngineSettings.java | 31 +
src/java/org/apache/ivy/core/sort/SortOptions.java | 56 +
.../sort/WarningNonMatchingVersionReporter.java | 32 +
src/java/org/apache/ivy/logo.png | Bin 0 -> 9085 bytes
.../org/apache/ivy/osgi/core/BundleArtifact.java | 48 +
.../org/apache/ivy/osgi/core/BundleCapability.java | 92 ++
src/java/org/apache/ivy/osgi/core/BundleInfo.java | 381 +++++
.../apache/ivy/osgi/core/BundleInfoAdapter.java | 370 +++++
.../apache/ivy/osgi/core/BundleRequirement.java | 111 ++
.../ivy/osgi/core/ExecutionEnvironmentProfile.java | 80 +
.../core/ExecutionEnvironmentProfileProvider.java | 135 ++
.../org/apache/ivy/osgi/core/ExportPackage.java | 70 +
.../ivy/osgi/core/ManifestHeaderElement.java | 113 ++
.../apache/ivy/osgi/core/ManifestHeaderValue.java | 416 ++++++
.../org/apache/ivy/osgi/core/ManifestParser.java | 302 ++++
.../apache/ivy/osgi/core/OSGiManifestParser.java | 103 ++
.../apache/ivy/osgi/core/OsgiLatestStrategy.java | 96 ++
.../apache/ivy/osgi/core/jvm-packages.properties | 220 +++
src/java/org/apache/ivy/osgi/filter/AndFilter.java | 45 +
.../org/apache/ivy/osgi/filter/CompareFilter.java | 138 ++
.../ivy/osgi/filter/MultiOperatorFilter.java | 90 ++
src/java/org/apache/ivy/osgi/filter/NotFilter.java | 36 +
.../org/apache/ivy/osgi/filter/OSGiFilter.java | 34 +
.../apache/ivy/osgi/filter/OSGiFilterParser.java | 217 +++
src/java/org/apache/ivy/osgi/filter/OrFilter.java | 45 +
.../apache/ivy/osgi/filter/UniOperatorFilter.java | 68 +
src/java/org/apache/ivy/osgi/obr/OBRResolver.java | 147 ++
.../org/apache/ivy/osgi/obr/xml/Capability.java | 55 +
.../apache/ivy/osgi/obr/xml/CapabilityAdapter.java | 103 ++
.../ivy/osgi/obr/xml/CapabilityProperty.java | 51 +
.../org/apache/ivy/osgi/obr/xml/OBRXMLParser.java | 407 ++++++
.../org/apache/ivy/osgi/obr/xml/OBRXMLWriter.java | 288 ++++
.../org/apache/ivy/osgi/obr/xml/Requirement.java | 61 +
.../ivy/osgi/obr/xml/RequirementAdapter.java | 184 +++
.../osgi/obr/xml/UnsupportedFilterException.java | 25 +
src/java/org/apache/ivy/osgi/p2/P2Artifact.java | 53 +
.../org/apache/ivy/osgi/p2/P2ArtifactParser.java | 248 ++++
.../org/apache/ivy/osgi/p2/P2CompositeParser.java | 123 ++
src/java/org/apache/ivy/osgi/p2/P2Descriptor.java | 152 ++
.../org/apache/ivy/osgi/p2/P2MetadataParser.java | 922 ++++++++++++
.../org/apache/ivy/osgi/p2/PropertiesParser.java | 80 +
.../org/apache/ivy/osgi/p2/XMLInputParser.java | 30 +
.../ivy/osgi/repo/AbstractFSManifestIterable.java | 155 ++
.../apache/ivy/osgi/repo/AbstractOSGiResolver.java | 540 +++++++
.../ivy/osgi/repo/AggregatedOSGiResolver.java | 40 +
.../ivy/osgi/repo/AggregatedRepoDescriptor.java | 112 ++
.../osgi/repo/ArtifactReportManifestIterable.java | 172 +++
.../ivy/osgi/repo/BundleCapabilityAndLocation.java | 57 +
.../apache/ivy/osgi/repo/BundleRepoDescriptor.java | 71 +
.../ivy/osgi/repo/EditableRepoDescriptor.java | 173 +++
.../apache/ivy/osgi/repo/FSManifestIterable.java | 126 ++
.../apache/ivy/osgi/repo/ManifestAndLocation.java | 54 +
.../ivy/osgi/repo/ModuleDescriptorWrapper.java | 99 ++
.../ivy/osgi/repo/RelativeURLRepository.java | 73 +
.../org/apache/ivy/osgi/repo/RepoDescriptor.java | 33 +
.../ivy/osgi/repo/RepositoryManifestIterable.java | 73 +
.../ivy/osgi/repo/ResolverManifestIterable.java | 205 +++
.../apache/ivy/osgi/updatesite/PluginAdapter.java | 81 +
.../ivy/osgi/updatesite/UpdateSiteDescriptor.java | 41 +
.../ivy/osgi/updatesite/UpdateSiteLoader.java | 338 +++++
.../ivy/osgi/updatesite/UpdateSiteResolver.java | 140 ++
.../apache/ivy/osgi/updatesite/xml/Archive.java | 32 +
.../ivy/osgi/updatesite/xml/CategoryDef.java | 37 +
.../ivy/osgi/updatesite/xml/EclipseFeature.java | 176 +++
.../ivy/osgi/updatesite/xml/EclipsePlugin.java | 59 +
.../updatesite/xml/EclipseUpdateSiteParser.java | 277 ++++
.../ivy/osgi/updatesite/xml/FeatureParser.java | 390 +++++
.../apache/ivy/osgi/updatesite/xml/Require.java | 73 +
.../apache/ivy/osgi/updatesite/xml/UpdateSite.java | 72 +
.../updatesite/xml/UpdateSiteDigestParser.java | 65 +
.../apache/ivy/osgi/util/DelegatingHandler.java | 636 ++++++++
src/java/org/apache/ivy/osgi/util/ParseUtil.java | 82 ++
src/java/org/apache/ivy/osgi/util/Version.java | 223 +++
.../apache/ivy/osgi/util/VersionComparator.java | 39 +
.../org/apache/ivy/osgi/util/VersionRange.java | 372 +++++
src/java/org/apache/ivy/osgi/util/ZipUtil.java | 66 +
.../org/apache/ivy/plugins/IvySettingsAware.java | 24 +
.../AbstractCircularDependencyStrategy.java | 34 +
.../AbstractLogCircularDependencyStrategy.java | 56 +
.../circular/CircularDependencyException.java | 43 +
.../plugins/circular/CircularDependencyHelper.java | 86 ++
.../circular/CircularDependencyStrategy.java | 31 +
.../circular/ErrorCircularDependencyStrategy.java | 38 +
.../circular/IgnoreCircularDependencyStrategy.java | 40 +
.../circular/WarnCircularDependencyStrategy.java | 39 +
.../plugins/conflict/AbstractConflictManager.java | 53 +
.../ivy/plugins/conflict/ConflictManager.java | 60 +
.../ivy/plugins/conflict/FixedConflictManager.java | 51 +
.../conflict/LatestCompatibleConflictManager.java | 312 ++++
.../plugins/conflict/LatestConflictManager.java | 171 +++
.../ivy/plugins/conflict/NoConflictManager.java | 32 +
.../plugins/conflict/RegexpConflictManager.java | 120 ++
.../plugins/conflict/StrictConflictException.java | 49 +
.../plugins/conflict/StrictConflictManager.java | 53 +
.../ivy/plugins/latest/AbstractLatestStrategy.java | 52 +
.../apache/ivy/plugins/latest/ArtifactInfo.java | 24 +
.../plugins/latest/ComparatorLatestStrategy.java | 51 +
.../latest/LatestLexicographicStrategy.java | 55 +
.../ivy/plugins/latest/LatestRevisionStrategy.java | 198 +++
.../apache/ivy/plugins/latest/LatestStrategy.java | 45 +
.../ivy/plugins/latest/LatestTimeStrategy.java | 36 +
.../ivy/plugins/lock/AbstractLockStrategy.java | 47 +
.../ivy/plugins/lock/ArtifactLockStrategy.java | 38 +
.../ivy/plugins/lock/CreateFileLockStrategy.java | 27 +
.../apache/ivy/plugins/lock/DeleteOnExitHook.java | 54 +
.../ivy/plugins/lock/FileBasedLockStrategy.java | 356 +++++
.../org/apache/ivy/plugins/lock/LockStrategy.java | 73 +
.../ivy/plugins/lock/NIOFileLockStrategy.java | 27 +
.../apache/ivy/plugins/lock/NoLockStrategy.java | 35 +
.../plugins/matcher/AbstractPatternMatcher.java | 63 +
.../org/apache/ivy/plugins/matcher/AnyMatcher.java | 40 +
.../matcher/ExactOrRegexpPatternMatcher.java | 64 +
.../ivy/plugins/matcher/ExactPatternMatcher.java | 56 +
.../ivy/plugins/matcher/GlobPatternMatcher.java | 104 ++
.../org/apache/ivy/plugins/matcher/MapMatcher.java | 70 +
.../org/apache/ivy/plugins/matcher/Matcher.java | 44 +
.../apache/ivy/plugins/matcher/MatcherHelper.java | 68 +
.../org/apache/ivy/plugins/matcher/NoMatcher.java | 41 +
.../apache/ivy/plugins/matcher/PatternMatcher.java | 72 +
.../ivy/plugins/matcher/RegexpPatternMatcher.java | 90 ++
.../org/apache/ivy/plugins/namespace/MRIDRule.java | 73 +
.../plugins/namespace/MRIDTransformationRule.java | 147 ++
.../ivy/plugins/namespace/NameSpaceHelper.java | 113 ++
.../apache/ivy/plugins/namespace/Namespace.java | 109 ++
.../ivy/plugins/namespace/NamespaceRule.java | 66 +
.../plugins/namespace/NamespaceTransformer.java | 26 +
.../parser/AbstractModuleDescriptorParser.java | 366 +++++
.../ivy/plugins/parser/ModuleDescriptorParser.java | 68 +
.../parser/ModuleDescriptorParserRegistry.java | 104 ++
.../apache/ivy/plugins/parser/ParserSettings.java | 62 +
.../plugins/parser/m2/DefaultPomDependencyMgt.java | 61 +
.../ivy/plugins/parser/m2/PomDependencyMgt.java | 33 +
.../parser/m2/PomModuleDescriptorBuilder.java | 736 ++++++++++
.../parser/m2/PomModuleDescriptorParser.java | 380 +++++
.../parser/m2/PomModuleDescriptorWriter.java | 392 +++++
.../apache/ivy/plugins/parser/m2/PomReader.java | 629 ++++++++
.../ivy/plugins/parser/m2/PomWriterOptions.java | 214 +++
.../apache/ivy/plugins/parser/m2/m2-entities.ent | 114 ++
.../org/apache/ivy/plugins/parser/m2/pom.template | 33 +
.../ivy/plugins/parser/xml/UpdateOptions.java | 222 +++
.../parser/xml/XmlModuleDescriptorParser.java | 1387 ++++++++++++++++++
.../parser/xml/XmlModuleDescriptorUpdater.java | 1340 +++++++++++++++++
.../parser/xml/XmlModuleDescriptorWriter.java | 576 ++++++++
src/java/org/apache/ivy/plugins/parser/xml/ivy.xsd | 291 ++++
.../ivy/plugins/report/LogReportOutputter.java | 199 +++
.../apache/ivy/plugins/report/ReportOutputter.java | 38 +
.../ivy/plugins/report/XmlReportOutputter.java | 75 +
.../apache/ivy/plugins/report/XmlReportParser.java | 333 +++++
.../apache/ivy/plugins/report/XmlReportWriter.java | 319 ++++
.../ivy/plugins/report/ivy-report-dot-all.xsl | 34 +
.../apache/ivy/plugins/report/ivy-report-dot.xsl | 49 +
.../ivy/plugins/report/ivy-report-graph-all.xsl | 64 +
.../apache/ivy/plugins/report/ivy-report-graph.xsl | 85 ++
.../org/apache/ivy/plugins/report/ivy-report.css | 279 ++++
.../org/apache/ivy/plugins/report/ivy-report.xsl | 514 +++++++
.../ivy/plugins/repository/AbstractRepository.java | 136 ++
.../repository/ArtifactResourceResolver.java | 29 +
.../ivy/plugins/repository/BasicResource.java | 76 +
.../ivy/plugins/repository/LazyResource.java | 97 ++
.../plugins/repository/LocalizableResource.java | 38 +
.../apache/ivy/plugins/repository/Repository.java | 153 ++
.../repository/RepositoryCopyProgressListener.java | 56 +
.../apache/ivy/plugins/repository/Resource.java | 100 ++
.../ivy/plugins/repository/ResourceDownloader.java | 38 +
.../ivy/plugins/repository/ResourceHelper.java | 61 +
.../ivy/plugins/repository/TransferEvent.java | 357 +++++
.../ivy/plugins/repository/TransferListener.java | 27 +
.../plugins/repository/file/FileRepository.java | 150 ++
.../ivy/plugins/repository/file/FileResource.java | 76 +
.../ivy/plugins/repository/jar/JarRepository.java | 89 ++
.../ivy/plugins/repository/jar/JarResource.java | 73 +
.../plugins/repository/sftp/SFTPRepository.java | 289 ++++
.../ivy/plugins/repository/sftp/SFTPResource.java | 87 ++
.../repository/ssh/AbstractSshBasedRepository.java | 320 ++++
.../plugins/repository/ssh/RemoteScpException.java | 53 +
.../org/apache/ivy/plugins/repository/ssh/Scp.java | 611 ++++++++
.../ivy/plugins/repository/ssh/SshCache.java | 453 ++++++
.../ivy/plugins/repository/ssh/SshRepository.java | 473 ++++++
.../ivy/plugins/repository/ssh/SshResource.java | 139 ++
.../plugins/repository/url/ChainedRepository.java | 115 ++
.../ivy/plugins/repository/url/URLRepository.java | 141 ++
.../ivy/plugins/repository/url/URLResource.java | 116 ++
.../ivy/plugins/repository/vfs/VfsRepository.java | 216 +++
.../ivy/plugins/repository/vfs/VfsResource.java | 206 +++
.../apache/ivy/plugins/repository/vfs/ivy_vfs.xml | 50 +
.../plugins/repository/vsftp/VsftpRepository.java | 745 ++++++++++
.../plugins/repository/vsftp/VsftpResource.java | 57 +
.../resolver/AbstractPatternsBasedResolver.java | 366 +++++
.../ivy/plugins/resolver/AbstractResolver.java | 606 ++++++++
.../plugins/resolver/AbstractSshBasedResolver.java | 147 ++
.../apache/ivy/plugins/resolver/BasicResolver.java | 1164 +++++++++++++++
.../ivy/plugins/resolver/BintrayResolver.java | 81 +
.../apache/ivy/plugins/resolver/CacheResolver.java | 185 +++
.../apache/ivy/plugins/resolver/ChainResolver.java | 349 +++++
.../ivy/plugins/resolver/DependencyResolver.java | 210 +++
.../apache/ivy/plugins/resolver/DualResolver.java | 218 +++
.../ivy/plugins/resolver/FileSystemResolver.java | 323 ++++
.../ivy/plugins/resolver/IBiblioResolver.java | 554 +++++++
.../ivy/plugins/resolver/IvyRepResolver.java | 309 ++++
.../apache/ivy/plugins/resolver/JarResolver.java | 101 ++
.../ivy/plugins/resolver/MirroredURLResolver.java | 115 ++
.../ivy/plugins/resolver/RepositoryResolver.java | 347 +++++
.../ivy/plugins/resolver/ResolverSettings.java | 58 +
.../apache/ivy/plugins/resolver/SFTPResolver.java | 41 +
.../apache/ivy/plugins/resolver/SshResolver.java | 89 ++
.../apache/ivy/plugins/resolver/URLResolver.java | 34 +
.../apache/ivy/plugins/resolver/VfsResolver.java | 61 +
.../apache/ivy/plugins/resolver/VsftpResolver.java | 74 +
.../resolver/packager/BuiltFileResource.java | 85 ++
.../resolver/packager/PackagerCacheEntry.java | 242 +++
.../resolver/packager/PackagerResolver.java | 255 ++++
.../apache/ivy/plugins/resolver/packager/build.xml | 58 +
.../ivy/plugins/resolver/packager/packager-1.0.xsd | 101 ++
.../ivy/plugins/resolver/packager/packager.xsl | 499 +++++++
.../plugins/resolver/util/ApacheHttpURLLister.java | 40 +
.../ivy/plugins/resolver/util/FileURLLister.java | 69 +
.../plugins/resolver/util/HasLatestStrategy.java | 28 +
.../plugins/resolver/util/MDResolvedResource.java | 35 +
.../plugins/resolver/util/ResolvedResource.java | 48 +
.../ivy/plugins/resolver/util/ResolverHelper.java | 332 +++++
.../plugins/resolver/util/ResourceMDParser.java | 34 +
.../ivy/plugins/resolver/util/URLLister.java | 35 +
.../ivy/plugins/signer/SignatureGenerator.java | 31 +
.../bouncycastle/OpenPGPSignatureGenerator.java | 185 +++
.../ivy/plugins/trigger/AbstractTrigger.java | 83 ++
.../org/apache/ivy/plugins/trigger/LogTrigger.java | 133 ++
.../org/apache/ivy/plugins/trigger/Trigger.java | 25 +
.../plugins/version/AbstractVersionMatcher.java | 78 +
.../ivy/plugins/version/ChainVersionMatcher.java | 155 ++
.../ivy/plugins/version/ExactVersionMatcher.java | 35 +
.../ivy/plugins/version/LatestVersionMatcher.java | 62 +
src/java/org/apache/ivy/plugins/version/Match.java | 136 ++
.../ivy/plugins/version/PatternVersionMatcher.java | 99 ++
.../ivy/plugins/version/SubVersionMatcher.java | 46 +
.../apache/ivy/plugins/version/VersionMatcher.java | 110 ++
.../ivy/plugins/version/VersionRangeMatcher.java | 246 ++++
.../ivy/tools/analyser/DependencyAnalyser.java | 24 +
.../tools/analyser/JarJarDependencyAnalyser.java | 95 ++
.../org/apache/ivy/tools/analyser/JarModule.java | 46 +
.../apache/ivy/tools/analyser/JarModuleFinder.java | 80 +
.../ivy/tools/analyser/RepositoryAnalyser.java | 60 +
.../org/apache/ivy/util/AbstractMessageLogger.java | 199 +++
src/java/org/apache/ivy/util/Checks.java | 61 +
src/java/org/apache/ivy/util/ChecksumHelper.java | 168 +++
src/java/org/apache/ivy/util/CollectionUtils.java | 33 +
.../org/apache/ivy/util/ConfigurationUtils.java | 96 ++
src/java/org/apache/ivy/util/Configurator.java | 764 ++++++++++
.../org/apache/ivy/util/ContextualSAXHandler.java | 64 +
.../org/apache/ivy/util/CopyProgressEvent.java | 60 +
.../org/apache/ivy/util/CopyProgressListener.java | 29 +
src/java/org/apache/ivy/util/Credentials.java | 102 ++
src/java/org/apache/ivy/util/CredentialsUtil.java | 167 +++
src/java/org/apache/ivy/util/DateUtil.java | 42 +
.../org/apache/ivy/util/DefaultMessageLogger.java | 51 +
.../org/apache/ivy/util/EncrytedProperties.java | 65 +
src/java/org/apache/ivy/util/FileResolver.java | 44 +
src/java/org/apache/ivy/util/FileUtil.java | 731 ++++++++++
src/java/org/apache/ivy/util/HexEncoder.java | 39 +
src/java/org/apache/ivy/util/HostUtil.java | 53 +
src/java/org/apache/ivy/util/MemoryUtil.java | 90 ++
src/java/org/apache/ivy/util/Message.java | 218 +++
src/java/org/apache/ivy/util/MessageLogger.java | 108 ++
.../org/apache/ivy/util/MessageLoggerEngine.java | 207 +++
.../org/apache/ivy/util/MessageLoggerHelper.java | 55 +
src/java/org/apache/ivy/util/PropertiesFile.java | 75 +
src/java/org/apache/ivy/util/StringUtils.java | 192 +++
src/java/org/apache/ivy/util/XMLHelper.java | 243 +++
src/java/org/apache/ivy/util/cli/CommandLine.java | 58 +
.../org/apache/ivy/util/cli/CommandLineParser.java | 141 ++
src/java/org/apache/ivy/util/cli/Option.java | 124 ++
.../org/apache/ivy/util/cli/OptionBuilder.java | 69 +
.../org/apache/ivy/util/cli/ParseException.java | 24 +
.../ivy/util/extendable/DefaultExtendableItem.java | 38 +
.../apache/ivy/util/extendable/ExtendableItem.java | 78 +
.../ivy/util/extendable/ExtendableItemHelper.java | 74 +
.../extendable/UnmodifiableExtendableItem.java | 97 ++
src/java/org/apache/ivy/util/filter/AndFilter.java | 41 +
.../apache/ivy/util/filter/ArtifactTypeFilter.java | 39 +
src/java/org/apache/ivy/util/filter/Filter.java | 22 +
.../org/apache/ivy/util/filter/FilterHelper.java | 82 ++
src/java/org/apache/ivy/util/filter/NoFilter.java | 33 +
src/java/org/apache/ivy/util/filter/NotFilter.java | 34 +
src/java/org/apache/ivy/util/filter/OrFilter.java | 41 +
.../apache/ivy/util/url/AbstractURLHandler.java | 176 +++
.../org/apache/ivy/util/url/ApacheURLLister.java | 209 +++
.../org/apache/ivy/util/url/BasicURLHandler.java | 330 +++++
.../org/apache/ivy/util/url/CredentialsStore.java | 64 +
.../org/apache/ivy/util/url/HttpClientHandler.java | 431 ++++++
.../org/apache/ivy/util/url/IvyAuthenticator.java | 157 ++
src/java/org/apache/ivy/util/url/URLHandler.java | 173 +++
.../apache/ivy/util/url/URLHandlerDispatcher.java | 109 ++
.../apache/ivy/util/url/URLHandlerRegistry.java | 73 +
594 files changed, 87354 insertions(+)
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..eb06170
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,258 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+------------------------------------------------------------------------------
+License for JCraft JSch package
+------------------------------------------------------------------------------
+Copyright (c) 2002,2003,2004,2005,2006,2007 Atsuhiko Yamanaka, JCraft,Inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the distribution.
+
+ 3. The names of the authors may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
+INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
+------------------------------------------------------------------------------
+License for jQuery
+------------------------------------------------------------------------------
+Copyright (c) 2007 John Resig, http://jquery.com/
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
\ No newline at end of file
diff --git a/META-INF/MANIFEST.MF b/META-INF/MANIFEST.MF
new file mode 100644
index 0000000..c4986fc
--- /dev/null
+++ b/META-INF/MANIFEST.MF
@@ -0,0 +1,112 @@
+Manifest-Version: 1.0
+Main-Class: org.apache.ivy.Main
+Bundle-Version: 2.4.0
+Bundle-Name: Ivy
+Bundle-ManifestVersion: 2
+Bundle-SymbolicName: org.apache.ivy
+Bundle-Vendor: Apache Software Foundation
+Bundle-DocURL: http://ant.apache.org/ivy/
+Import-Package: com.jcraft.jsch;resolution:=optional,
+ javax.crypto;resolution:=optional,
+ javax.swing;resolution:=optional,
+ javax.swing.event;resolution:=optional,
+ javax.xml.parsers,
+ javax.xml.transform,
+ javax.xml.transform.sax,
+ javax.xml.transform.stream,
+ org.apache.commons.httpclient;resolution:=optional,
+ org.apache.commons.httpclient.auth;resolution:=optional,
+ org.apache.commons.httpclient.methods;resolution:=optional,
+ org.apache.commons.httpclient.params;resolution:=optional,
+ org.apache.commons.httpclient.protocol;resolution:=optional,
+ org.apache.commons.net.ftp;resolution:=optional,
+ org.apache.commons.vfs;resolution:=optional,
+ org.apache.commons.vfs.impl;resolution:=optional,
+ org.apache.commons.vfs.provider;resolution:=optional,
+ org.apache.commons.vfs.provider.ftp;resolution:=optional,
+ org.apache.commons.vfs.provider.local;resolution:=optional,
+ org.apache.commons.vfs.provider.sftp;resolution:=optional,
+ org.apache.commons.vfs.provider.url;resolution:=optional,
+ org.apache.oro.text;resolution:=optional,
+ org.apache.oro.text.regex;resolution:=optional,
+ org.apache.tools.ant;resolution:=optional,
+ org.apache.tools.ant.filters;resolution:=optional,
+ org.apache.tools.ant.taskdefs;resolution:=optional,
+ org.apache.tools.ant.types;resolution:=optional,
+ org.apache.tools.ant.types.resources;resolution:=optional,
+ org.apache.tools.ant.util;resolution:=optional,
+ org.apache.webdav;resolution:=optional,
+ org.bouncycastle.bcpg;resolution:=optional,
+ org.bouncycastle.jce.provider;resolution:=optional,
+ org.bouncycastle.openpgp;resolution:=optional,
+ org.w3c.dom;resolution:=optional,
+ org.xml.sax,
+ org.xml.sax.ext,
+ org.xml.sax.helpers
+Export-Package: org.apache.ivy;version="2.0.0",
+ org.apache.ivy.ant;version="2.0.0",
+ org.apache.ivy.core;version="2.0.0",
+ org.apache.ivy.core.cache;version="2.0.0",
+ org.apache.ivy.core.check;version="2.0.0",
+ org.apache.ivy.core.deliver;version="2.0.0",
+ org.apache.ivy.core.event;version="2.0.0",
+ org.apache.ivy.core.event.download;version="2.0.0",
+ org.apache.ivy.core.event.publish;version="2.0.0",
+ org.apache.ivy.core.event.resolve;version="2.0.0",
+ org.apache.ivy.core.event.retrieve;version="2.0.0",
+ org.apache.ivy.core.install;version="2.0.0",
+ org.apache.ivy.core.module.descriptor;version="2.0.0",
+ org.apache.ivy.core.module.id;version="2.0.0",
+ org.apache.ivy.core.module.status;version="2.0.0",
+ org.apache.ivy.core.pack;version="2.4.0",
+ org.apache.ivy.core.publish;version="2.0.0",
+ org.apache.ivy.core.report;version="2.0.0",
+ org.apache.ivy.core.repository;version="2.0.0",
+ org.apache.ivy.core.resolve;version="2.0.0",
+ org.apache.ivy.core.retrieve;version="2.0.0",
+ org.apache.ivy.core.search;version="2.0.0",
+ org.apache.ivy.core.settings;version="2.0.0",
+ org.apache.ivy.core.sort;version="2.0.0",
+ org.apache.ivy.osgi.core;version="2.3.0",
+ org.apache.ivy.osgi.filter;version="2.3.0",
+ org.apache.ivy.osgi.obr;version="2.3.0",
+ org.apache.ivy.osgi.obr.xml;version="2.3.0",
+ org.apache.ivy.osgi.p2;version="2.3.0",
+ org.apache.ivy.osgi.repo;version="2.3.0",
+ org.apache.ivy.osgi.updatesite;version="2.3.0",
+ org.apache.ivy.osgi.updatesite.xml;version="2.3.0",
+ org.apache.ivy.osgi.util;version="2.3.0",
+ org.apache.ivy.plugins;version="2.0.0",
+ org.apache.ivy.plugins.circular;version="2.0.0",
+ org.apache.ivy.plugins.conflict;version="2.0.0",
+ org.apache.ivy.plugins.latest;version="2.0.0",
+ org.apache.ivy.plugins.lock;version="2.0.0",
+ org.apache.ivy.plugins.matcher;version="2.0.0",
+ org.apache.ivy.plugins.namespace;version="2.0.0",
+ org.apache.ivy.plugins.parser;version="2.0.0",
+ org.apache.ivy.plugins.parser.m2;version="2.0.0",
+ org.apache.ivy.plugins.parser.xml;version="2.0.0",
+ org.apache.ivy.plugins.report;version="2.0.0",
+ org.apache.ivy.plugins.repository;version="2.0.0",
+ org.apache.ivy.plugins.repository.file;version="2.0.0",
+ org.apache.ivy.plugins.repository.jar;version="2.3.0",
+ org.apache.ivy.plugins.repository.sftp;version="2.0.0",
+ org.apache.ivy.plugins.repository.ssh;version="2.0.0",
+ org.apache.ivy.plugins.repository.url;version="2.0.0",
+ org.apache.ivy.plugins.repository.vfs;version="2.0.0",
+ org.apache.ivy.plugins.repository.vsftp;version="2.0.0",
+ org.apache.ivy.plugins.resolver;version="2.0.0",
+ org.apache.ivy.plugins.resolver.packager;version="2.0.0",
+ org.apache.ivy.plugins.resolver.util;version="2.0.0",
+ org.apache.ivy.plugins.signer;version="2.2.0",
+ org.apache.ivy.plugins.signer.bouncycastle;version="2.2.0",
+ org.apache.ivy.plugins.trigger;version="2.0.0",
+ org.apache.ivy.plugins.version;version="2.0.0",
+ org.apache.ivy.tools.analyser;version="2.0.0",
+ org.apache.ivy.util;version="2.0.0",
+ org.apache.ivy.util.cli;version="2.0.0",
+ org.apache.ivy.util.extendable;version="2.0.0",
+ org.apache.ivy.util.filter;version="2.0.0",
+ org.apache.ivy.util.url;version="2.0.0"
+Bundle-ClassPath: .
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
diff --git a/NOTICE b/NOTICE
new file mode 100644
index 0000000..14eef16
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,16 @@
+Apache Ivy (TM)
+Copyright 2007-2014 The Apache Software Foundation
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+
+Portions of Ivy were originally developed at
+Jayasoft SARL (http://www.jayasoft.fr/)
+and are licensed to the Apache Software Foundation under the
+"Software Grant License Agreement"
+
+SSH and SFTP support is provided by the JCraft JSch package,
+which is open source software, available under
+the terms of a BSD style license.
+The original software and related information is available
+at http://www.jcraft.com/jsch/.
diff --git a/README b/README
new file mode 100644
index 0000000..242a2c2
--- /dev/null
+++ b/README
@@ -0,0 +1,95 @@
+ Apache Ivy (TM)
+-----------------------------------------------------------------------
+
+
+What is Apache Ivy?
+--------------------
+
+Apache Ivy is a tool for managing (recording, tracking, resolving and reporting)
+project dependencies.
+It is characterized by the following:
+
+ 1. flexibility and configurability
+ Apache Ivy is essentially process agnostic and is not tied to any
+ methodology or structure.
+ Instead it provides the necessary flexibility and configurability
+ to be adapted to a broad range of dependency management and build
+ processes.
+ 2. tight integration with Apache Ant
+ while available as a standalone tool, Apache Ivy works particularly well
+ with Apache Ant providing a number of powerful Ant tasks ranging
+ from dependency resolution to dependency reporting and publication.
+
+
+Latest Changes:
+----------------
+
+See the list of the changes since the last release in the file doc/release-notes.html
+
+
+Supported Platforms
+--------------------
+
+Please see the the detailed documentation about Ivy compatibility there: doc/compatibility.html
+
+
+Installation
+-------------
+
+Please read doc/install.html for installation instructions.
+
+
+Licensing
+---------
+
+This software is licensed under the terms you may find in the file
+named "LICENSE" in this directory.
+
+
+Crypto Notice
+-------------
+This distribution includes cryptographic software. The country in
+which you currently reside may have restrictions on the import,
+possession, use, and/or re-export to another country, of
+encryption software. BEFORE using any encryption software, please
+check your country's laws, regulations and policies concerning the
+import, possession, or use, and re-export of encryption software, to
+see if this is permitted. See <http://www.wassenaar.org/> for more
+information.
+
+The U.S. Government Department of Commerce, Bureau of Industry and
+Security (BIS), has classified this software as Export Commodity
+Control Number (ECCN) 5D002.C.1, which includes information security
+software using or performing cryptographic functions with asymmetric
+algorithms. The form and manner of this Apache Software Foundation
+distribution makes it eligible for export under the License Exception
+ENC Technology Software Unrestricted (TSU) exception (see the BIS
+Export Administration Regulations, Section 740.13) for both object
+code and source code.
+
+The following provides more details on the included cryptographic
+software:
+
+The Ivy ssh resolver requires the JSch library
+<http://www.jcraft.com/jsch/index.html>.
+The sftp and https resolvers requires the Java Cryptography extensions
+<http://java.sun.com/javase/technologies/security/>.
+The PGP signature generator requires the BouncyCastle Java cryptography APIs
+<http://www.bouncycastle.org/java.html>.
+
+
+How to Get Involved
+--------------------
+
+The Apache Ivy project really needs and appreciates any contributions,
+including documentation help, source code and feedback. If you are interested
+in contributing, please visit http://ant.apache.org/ivy/get-involved.html.
+
+
+How to Report Issues
+--------------------
+
+The Apache Ivy project uses JIRA for issue tracking. Please report any
+issues you find at http://issues.apache.org/jira/browse/IVY
+
+
diff --git a/ant.patterns b/ant.patterns
new file mode 100644
index 0000000..f87adf9
--- /dev/null
+++ b/ant.patterns
@@ -0,0 +1,24 @@
+# ***************************************************************
+# * Licensed to the Apache Software Foundation (ASF) under one
+# * or more contributor license agreements. See the NOTICE file
+# * distributed with this work for additional information
+# * regarding copyright ownership. The ASF licenses this file
+# * to you under the Apache License, Version 2.0 (the
+# * "License"); you may not use this file except in compliance
+# * with the License. You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing,
+# * software distributed under the License is distributed on an
+# * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# * KIND, either express or implied. See the License for the
+# * specific language governing permissions and limitations
+# * under the License.
+# ***************************************************************
+
+#This section defines the sources to compile for ivy-ant.jar
+org/apache/ivy/ant/*.java
+
+#This section defines the resources to copy for ivy-ant.jar
+org/apache/ivy/ant/*.xml
diff --git a/build.properties b/build.properties
new file mode 100644
index 0000000..cc08112
--- /dev/null
+++ b/build.properties
@@ -0,0 +1,56 @@
+# ***************************************************************
+# * Licensed to the Apache Software Foundation (ASF) under one
+# * or more contributor license agreements. See the NOTICE file
+# * distributed with this work for additional information
+# * regarding copyright ownership. The ASF licenses this file
+# * to you under the Apache License, Version 2.0 (the
+# * "License"); you may not use this file except in compliance
+# * with the License. You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing,
+# * software distributed under the License is distributed on an
+# * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# * KIND, either express or implied. See the License for the
+# * specific language governing permissions and limitations
+# * under the License.
+# ***************************************************************
+lib.dir=${basedir}/lib
+src.dir=${basedir}/src/java
+test.dir=${basedir}/test/java
+example.dir=${basedir}/src/example
+build.dir=${basedir}/build
+classes.build.dir=${basedir}/build/classes
+core.classes.build.dir=${classes.build.dir}/core
+bootstrap.classes.build.dir=${classes.build.dir}/bootstrap
+ant.classes.build.dir=${classes.build.dir}/ant
+optional.classes.build.dir=${classes.build.dir}/optional
+all.classes.build.dir=${classes.build.dir}/all
+test.build.dir=${basedir}/build/test
+artifacts.build.dir=${basedir}/build/artifact
+distrib.dir=${basedir}/build/distrib
+doc.build.dir=${basedir}/build/doc
+reports.dir=${doc.build.dir}/reports
+test.xml.dir=${build.dir}/test-report
+test.report.dir=${reports.dir}/test
+coverage.report.dir=${reports.dir}/coverage
+javadoc.build.dir=${reports.dir}/api
+ivy.report.dir=${reports.dir}/ivy
+doc.src.dir=${basedir}/doc
+checkstyle.report.dir=${reports.dir}/checkstyle
+checkstyle.src.dir=${basedir}/src/etc/checkstyle
+rat.report.dir=${reports.dir}/rat
+
+ivy.minimum.javaversion=1.5
+debug.mode=on
+ivy.install.version=1.4.1
+
+#status=integration
+
+test.class.pattern = *Test
+
+source.. = src/java/
+output.. = bin/
+bin.includes = META-INF/,\
+ .
diff --git a/build.xml b/build.xml
new file mode 100644
index 0000000..a9b4c12
--- /dev/null
+++ b/build.xml
@@ -0,0 +1,690 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project name="ivy" default="coverage-report" xmlns:ivy="antlib:org.apache.ivy.ant">
+
+ <property environment="env" />
+ <property file="version.properties" />
+ <property file="build.properties" />
+
+ <property name="final.name" value="ivy.jar" />
+
+ <target name="init-ivy-user-home" unless="ivy.use.local.home">
+ <condition property="ivy.home" value="${env.IVY_HOME}">
+ <isset property="env.IVY_HOME" />
+ </condition>
+ <property name="ivy.home" value="${user.home}/.ivy2" />
+ </target>
+
+ <target name="init-ivy-local-home" if="ivy.use.local.home">
+ <property name="ivy.home" value="${basedir}/.ivy2" />
+ </target>
+
+ <target name="init-ivy-home" depends="init-ivy-user-home, init-ivy-local-home" />
+
+ <target name="init-ivy" depends="compile-bootstrap, init-ivy-home">
+ <taskdef resource="org/apache/ivy/ant/antlib.xml"
+ uri="antlib:org.apache.ivy.ant">
+ <classpath>
+ <pathelement location="${core.classes.build.dir}" />
+ <pathelement location="${bootstrap.classes.build.dir}" />
+ </classpath>
+ </taskdef>
+
+ <ivy:configure override="true" />
+ </target>
+
+ <target name="install" depends="init-ivy-home, jar"
+ description="build Ivy and install it in Ivy user home for builds using Ivy user home to load Ivy jar">
+ <property name="ivy.jar.file" value="${ivy.home}/jars/ivy.jar" />
+ <copy file="${artifacts.build.dir}/jars/${final.name}" tofile="${ivy.jar.file}" />
+ </target>
+
+ <target name="install-ant" depends="init-ivy-home, jar"
+ description="build Ivy and install it in Ant home lib">
+ <condition property="ant.home" value="${env.ANT_HOME}">
+ <isset property="env.ANT_HOME" />
+ </condition>
+ <fail unless="ant.home" message="ANT_HOME environment variable or ant.home property required" />
+ <copy file="${artifacts.build.dir}/jars/${final.name}" tofile="${ant.home}/lib/ivy.jar" />
+ </target>
+
+ <!-- =================================================================
+ PREPARATION, CLEANING AND FLAGS TASKS
+ ================================================================= -->
+ <target name="init">
+ <path id="lib.classpath">
+ <fileset dir="${lib.dir}">
+ <include name="*.jar" />
+ </fileset>
+ </path>
+ <path id="build.bootstrap.classpath">
+ <pathelement location="${core.classes.build.dir}" />
+ </path>
+ <path id="build.ant.classpath">
+ <pathelement location="${core.classes.build.dir}" />
+ <path refid="lib.classpath" />
+ </path>
+ <path id="build.optional.classpath">
+ <path refid="build.ant.classpath" />
+ </path>
+ <path id="run.classpath">
+ <pathelement location="${core.classes.build.dir}" />
+ <pathelement location="${ant.classes.build.dir}" />
+ <pathelement location="${optional.classes.build.dir}" />
+ <path refid="lib.classpath" />
+ </path>
+ <path id="test.classpath">
+ <pathelement location="${coverage.classes.dir}" />
+ <fileset dir="${lib.dir}">
+ <include name="*.jar" />
+ <exclude name="ant.jar" />
+ <exclude name="ant-launcher.jar" />
+ <exclude name="ant-nodeps.jar"/>
+ <exclude name="ant-trax.jar"/>
+ </fileset>
+ <pathelement location="${core.classes.build.dir}" />
+ <pathelement location="${ant.classes.build.dir}" />
+ <pathelement location="${optional.classes.build.dir}" />
+ <pathelement path="${test.build.dir}" />
+ </path>
+ </target>
+
+ <target name="prepare" depends="init">
+ <mkdir dir="${classes.build.dir}" />
+ <mkdir dir="${core.classes.build.dir}" />
+ <mkdir dir="${bootstrap.classes.build.dir}" />
+ <mkdir dir="${ant.classes.build.dir}" />
+ <mkdir dir="${optional.classes.build.dir}" />
+ <mkdir dir="${all.classes.build.dir}" />
+ <mkdir dir="${test.build.dir}" />
+ <mkdir dir="${artifacts.build.dir}" />
+ <mkdir dir="${test.report.dir}" />
+ <mkdir dir="${ivy.report.dir}" />
+ </target>
+
+ <target name="clean" description="delete all generated files keeping sources only">
+ <delete dir="${classes.build.dir}" />
+ <delete dir="${test.build.dir}" />
+ <delete dir="${artifacts.build.dir}" />
+ <delete dir="${test.report.dir}" />
+ <delete dir="${javadoc.build.dir}" />
+ <delete dir="${doc.build.dir}" />
+ <delete dir="${build.dir}" />
+ </target>
+
+ <target name="clean-lib">
+ <delete dir="${lib.dir}" />
+ </target>
+
+ <target name="clean-ivy-cache" depends="init-ivy-home">
+ <delete dir="${ivy.home}/cache" />
+ </target>
+
+ <target name="clean-ivy-home" depends="init-ivy-home">
+ <delete dir="${ivy.home}" />
+ </target>
+
+ <target name="clean-examples" description="clean all examples">
+ <subant target="clean" failonerror="false">
+ <fileset dir="${example.dir}" includes="**/build.xml" />
+ </subant>
+ </target>
+
+ <target name="clean-all" depends="clean, clean-lib, clean-examples" />
+
+ <target name="/noresolve" description="use to skip dependency resolution">
+ <property name="no.resolve" value="true" />
+ </target>
+
+ <target name="/notest" description="use to skip tests">
+ <property name="skip.test" value="true" />
+ </target>
+
+ <target name="/nojavadoc" description="use to skip javadoc">
+ <property name="skip.javadoc" value="true" />
+ </target>
+
+ <target name="/localivy" description="use a local ivy home">
+ <property name="ivy.use.local.home" value="true" />
+ </target>
+
+ <target name="/offline" depends="/noresolve" description="use to indicate no internet connection is available">
+ <property name="offline" value="true" />
+ </target>
+
+ <target name="default-version">
+ <tstamp>
+ <format property="pubdate" pattern="yyyyMMddHHmmss" />
+ </tstamp>
+ <property name="version.prefix" value="${target.ivy.version}-local-" />
+ <property name="build.version" value="${version.prefix}${pubdate}" />
+ <property name="bundle.version" value="${target.ivy.bundle.version}.${target.ivy.bundle.version.qualifier}${pubdate}" />
+ </target>
+
+ <!-- =================================================================
+ DEPENDENCY MNGT, COMPILATION AND JAR
+ ================================================================= -->
+ <target name="resolve" depends="init-ivy" unless="no.resolve">
+ <ivy:retrieve conf="default,test" pattern="${lib.dir}/[artifact].[ext]" sync="yes"/>
+ </target>
+
+ <target name="compile-core" depends="prepare">
+ <javac srcdir="${src.dir}"
+ destdir="${core.classes.build.dir}"
+ sourcepath=""
+ source="${ivy.minimum.javaversion}"
+ target="${ivy.minimum.javaversion}"
+ debug="${debug.mode}"
+ includeantruntime="no">
+ <excludesfile name="ant.patterns" />
+ <excludesfile name="optional.patterns" />
+ </javac>
+ <copy todir="${core.classes.build.dir}" includeEmptyDirs="false">
+ <fileset dir="${src.dir}">
+ <exclude name="**/*.java" />
+ <excludesfile name="ant.patterns" />
+ <excludesfile name="optional.patterns" />
+ </fileset>
+ </copy>
+
+ <!-- copy settings files for backward compatibility with ivyconf naming -->
+ <copy file="${core.classes.build.dir}/org/apache/ivy/core/settings/ivysettings-local.xml"
+ tofile="${core.classes.build.dir}/org/apache/ivy/core/settings/ivyconf-local.xml" />
+ <copy file="${core.classes.build.dir}/org/apache/ivy/core/settings/ivysettings-default-chain.xml"
+ tofile="${core.classes.build.dir}/org/apache/ivy/core/settings/ivyconf-default-chain.xml" />
+ <copy file="${core.classes.build.dir}/org/apache/ivy/core/settings/ivysettings-main-chain.xml"
+ tofile="${core.classes.build.dir}/org/apache/ivy/core/settings/ivyconf-main-chain.xml" />
+ <copy file="${core.classes.build.dir}/org/apache/ivy/core/settings/ivysettings-public.xml"
+ tofile="${core.classes.build.dir}/org/apache/ivy/core/settings/ivyconf-public.xml" />
+ <copy file="${core.classes.build.dir}/org/apache/ivy/core/settings/ivysettings-shared.xml"
+ tofile="${core.classes.build.dir}/org/apache/ivy/core/settings/ivyconf-shared.xml" />
+ <copy file="${core.classes.build.dir}/org/apache/ivy/core/settings/ivysettings.xml"
+ tofile="${core.classes.build.dir}/org/apache/ivy/core/settings/ivyconf.xml" />
+ </target>
+
+ <!-- Build the Ant tasks with the current Ant runtime -->
+ <target name="compile-bootstrap" depends="compile-core">
+ <javac srcdir="${src.dir}"
+ destdir="${bootstrap.classes.build.dir}"
+ sourcepath=""
+ classpathref="build.bootstrap.classpath"
+ source="${ivy.minimum.javaversion}"
+ target="${ivy.minimum.javaversion}"
+ debug="${debug.mode}"
+ includeantruntime="yes">
+ <includesfile name="ant.patterns" />
+ </javac>
+ <copy todir="${bootstrap.classes.build.dir}" includeEmptyDirs="false">
+ <fileset dir="${src.dir}">
+ <includesfile name="ant.patterns" />
+ <exclude name="**/*.java" />
+ </fileset>
+ </copy>
+ </target>
+
+ <!-- Build the Ant tasks with the minimal Ant runtime -->
+ <target name="compile-ant" depends="compile-core, resolve">
+ <javac srcdir="${src.dir}"
+ destdir="${ant.classes.build.dir}"
+ sourcepath=""
+ classpathref="build.ant.classpath"
+ source="${ivy.minimum.javaversion}"
+ target="${ivy.minimum.javaversion}"
+ debug="${debug.mode}"
+ includeantruntime="no">
+ <includesfile name="ant.patterns" />
+ </javac>
+ <copy todir="${ant.classes.build.dir}" includeEmptyDirs="false">
+ <fileset dir="${src.dir}">
+ <includesfile name="ant.patterns" />
+ <exclude name="**/*.java" />
+ </fileset>
+ </copy>
+
+ <!-- copy antlib for backward compatibility with fr.jayasoft.ivy package -->
+ <copy file="${ant.classes.build.dir}/org/apache/ivy/ant/antlib.xml"
+ todir="${ant.classes.build.dir}/fr/jayasoft/ivy/ant" />
+ </target>
+
+ <target name="compile-optional" depends="compile-ant, resolve">
+ <javac srcdir="${src.dir}"
+ destdir="${optional.classes.build.dir}"
+ sourcepath=""
+ classpathref="build.optional.classpath"
+ source="${ivy.minimum.javaversion}"
+ target="${ivy.minimum.javaversion}"
+ debug="${debug.mode}"
+ includeantruntime="no">
+ <includesfile name="optional.patterns" />
+ <includesfile name="ant.patterns" />
+ </javac>
+ <copy todir="${core.classes.build.dir}" includeEmptyDirs="false">
+ <fileset dir="${src.dir}">
+ <includesfile name="optional.patterns" />
+ <exclude name="**/*.java" />
+ </fileset>
+ </copy>
+ </target>
+
+ <!-- =================================================================
+ Create the two jar files (Ivy core and Ivy core + Ant tasks)
+ ================================================================= -->
+ <target name="jar" depends="compile-optional, default-version" description="Create Jar files">
+ <!-- identify compiled ivy version -->
+ <echo message="version=${build.version}${line.separator}" file="${core.classes.build.dir}/module.properties" append="true" />
+ <echo message="date=${pubdate}${line.separator}" file="${core.classes.build.dir}/module.properties" append="true" />
+
+ <mkdir dir="${artifacts.build.dir}/jars/"/>
+
+ <!--
+ there is a default Bundle-Version attribute in the source MANIFEST, used to ease
+ development in eclipse.
+ We remove this line to make sure we get the Bundle-Version as set in the jar task
+ -->
+ <copy file="${basedir}/META-INF/MANIFEST.MF" tofile="${artifacts.build.dir}/MANIFEST.MF">
+ <filterchain>
+ <replaceregex pattern="Bundle-Version:.*" replace="Bundle-Version: ${bundle.version}" byline="true" />
+ </filterchain>
+ </copy>
+
+ <copy todir="${all.classes.build.dir}">
+ <fileset dir="${core.classes.build.dir}" />
+ <fileset dir="${ant.classes.build.dir}" />
+ <fileset dir="${optional.classes.build.dir}" />
+ </copy>
+
+ <jar destfile="${artifacts.build.dir}/jars/${final.name}"
+ manifest="${artifacts.build.dir}/MANIFEST.MF">
+ <metainf dir="${basedir}" includes="LICENSE,NOTICE" />
+ <manifest>
+ <attribute name="Specification-Title" value="Apache Ivy with Ant tasks" />
+ <attribute name="Specification-Version" value="${build.version}" />
+ <attribute name="Specification-Vendor" value="Apache Software Foundation" />
+ <attribute name="Implementation-Title" value="org.apache.ivy" />
+ <attribute name="Implementation-Version" value="${build.version}" />
+ <attribute name="Implementation-Vendor" value="Apache Software Foundation" />
+ <attribute name="Implementation-Vendor-Id" value="org.apache" />
+ <attribute name="Extension-name" value="org.apache.ivy" />
+ <attribute name="Build-Version" value="${build.version}" />
+ </manifest>
+ <fileset dir="${all.classes.build.dir}" />
+ </jar>
+ <!-- copy main jar to ease its use as an OSGi bundle -->
+ <copy file="${artifacts.build.dir}/jars/${final.name}"
+ tofile="${artifacts.build.dir}/org.apache.ivy_${bundle.version}.jar" />
+
+ <!-- clean generated module properties file -->
+ <delete file="${core.classes.build.dir}/module.properties" />
+ </target>
+
+ <!-- =================================================================
+ PUBLISH LOCAL
+ ================================================================= -->
+ <target name="publish-local" depends="jar,sources" description="publishes Ivy to Ivy local repository">
+ <ivy:publish resolver="local" pubrevision="${build.version}"
+ artifactsPattern="${artifacts.build.dir}/[type]s/[artifact].[ext]"
+ forcedeliver="true" />
+ </target>
+
+ <!-- =================================================================
+ TESTS
+ ================================================================= -->
+ <target name="build-custom-resolver-jar" depends="jar">
+ <mkdir dir="${build.dir}/custom-classpath" />
+ <javac srcdir="${basedir}/test/custom-classpath"
+ destdir="${build.dir}/custom-classpath"
+ classpathref="run.classpath"
+ source="${ivy.minimum.javaversion}"
+ target="${ivy.minimum.javaversion}"
+ debug="${debug.mode}"
+ includeantruntime="no" />
+ <jar destfile="${test.dir}/org/apache/ivy/core/settings/custom-resolver.jar"
+ basedir="${build.dir}/custom-classpath" />
+ </target>
+
+ <target name="init-tests-offline" if="offline">
+ <fileset id="test.fileset" dir="${test.dir}">
+ <include name="**/${test.class.pattern}.java" />
+ <exclude name="**/Abstract*Test.java" />
+ <not><contains text="remote.test" /></not>
+ </fileset>
+ </target>
+
+ <target name="init-tests-online" unless="offline">
+ <fileset id="test.fileset" dir="${test.dir}">
+ <include name="**/${test.class.pattern}.java" />
+ <exclude name="**/Abstract*Test.java" />
+ </fileset>
+ </target>
+
+ <target name="init-tests" depends="init-tests-offline, init-tests-online" />
+
+ <target name="emma" depends="jar" unless="skip.test">
+ <ivy:cachepath organisation="emma" module="emma" revision="2.0.5312"
+ inline="true" conf="default" pathid="emma.classpath"
+ log="download-only" />
+ <ivy:cachepath organisation="emma" module="emma_ant" revision="2.0.5312"
+ inline="true" conf="default" pathid="emma.ant.classpath" transitive="false"
+ log="download-only" />
+ <taskdef resource="emma_ant.properties">
+ <classpath refid="emma.classpath" />
+ <classpath refid="emma.ant.classpath" />
+ </taskdef>
+ <property name="emma.enabled" value="true" />
+ <property name="coverage.dir" value="${build.dir}/coverage" />
+ <property name="coverage.classes.dir" value="${coverage.dir}/classes" />
+ <mkdir dir="${coverage.dir}" />
+ <mkdir dir="${coverage.classes.dir}" />
+ <emma enabled="${emma.enabled}">
+ <instr mode="copy"
+ destdir="${coverage.dir}/classes"
+ metadatafile="${coverage.dir}/metadata.emma">
+ <instrpath>
+ <pathelement location="${core.classes.build.dir}" />
+ <pathelement location="${ant.classes.build.dir}" />
+ <pathelement location="${optional.classes.build.dir}" />
+ </instrpath>
+ </instr>
+ </emma>
+ <delete file="${coverage.dir}/coverage.emma" />
+ <!-- add emma path to test path, because emma jars need to be available when running
+ instrumented classes -->
+ <ivy:addpath topath="test.classpath" first="true">
+ <pathelement location="${coverage.dir}/classes" />
+ <path refid="emma.classpath" />
+ </ivy:addpath>
+ </target>
+
+ <target name="build-test" depends="jar">
+ <javac srcdir="${test.dir}"
+ destdir="${test.build.dir}"
+ classpathref="run.classpath"
+ source="${ivy.minimum.javaversion}"
+ target="${ivy.minimum.javaversion}"
+ debug="${debug.mode}"
+ encoding="ISO-8859-1"
+ includeantruntime="no" />
+ <copy todir="${test.build.dir}">
+ <fileset dir="${test.dir}">
+ <exclude name="**/*.java" />
+ </fileset>
+ </copy>
+ </target>
+
+ <target name="prepare-osgi-tests" depends="resolve" unless="skip.test">
+ <ant dir="${basedir}/test/test-repo" target="generate-bundles" />
+ </target>
+
+ <target name="prepare-test-jar-repositories" unless="skip.test">
+ <mkdir dir="${basedir}/test/jar-repos" />
+ <jar destfile="${basedir}/test/jar-repos/jarrepo1.jar" >
+ <fileset dir="${basedir}/test/repositories/1" />
+ </jar>
+ <jar destfile="${basedir}/test/jar-repos/jarrepo1_subdir.jar">
+ <fileset dir="${basedir}/test/repositories" includes="1/**/*" />
+ </jar>
+ </target>
+
+ <target name="test-internal" depends="build-test, init-tests, prepare-osgi-tests, prepare-test-jar-repositories" unless="skip.test">
+ <mkdir dir="${test.xml.dir}" />
+
+ <junit
+ haltonfailure="off"
+ haltonerror="off"
+ errorproperty="test.failed"
+ failureproperty="test.failed"
+ showoutput="no"
+ printsummary="yes"
+ includeantruntime="yes"
+ dir="${basedir}"
+ fork="true">
+ <classpath>
+ <path refid="test.classpath" />
+ <pathelement path="${ant.home}/lib/ant-nodeps.jar"/>
+ <pathelement path="${ant.home}/lib/ant-trax.jar"/>
+ </classpath>
+
+ <!-- pass the proxy properties to the forked junit process to use correct proxy -->
+ <syspropertyset>
+ <propertyref prefix="http" />
+ </syspropertyset>
+ <jvmarg value="-Demma.coverage.out.file=${coverage.dir}/coverage.emma" />
+ <jvmarg value="-Demma.coverage.out.merge=true" />
+
+ <!-- Added this to test IVY-65 -->
+ <jvmarg value="-Duser.region=TR" />
+ <jvmarg value="-Duser.language=tr" />
+
+ <formatter type="xml"/>
+ <batchtest todir="${test.xml.dir}">
+ <fileset refid="test.fileset" />
+ </batchtest>
+ </junit>
+ </target>
+
+ <target name="test" depends="test-internal" description="Run the test">
+ <fail if="test.failed"
+ message="At least one test has failed. See logs (in ${test.xml.dir}) for details (use the target test-report to run the test with a report)" />
+ </target>
+
+ <!-- =================================================================
+ REPORTS AND DOCUMENTATION
+ ================================================================= -->
+ <target name="test-report" depends="test-internal" unless="skip.test">
+ <junitreport todir="${test.xml.dir}">
+ <fileset dir="${test.xml.dir}">
+ <include name="TEST-*.xml" />
+ </fileset>
+ <report format="frames" todir="${test.report.dir}" />
+ </junitreport>
+ <fail if="test.failed"
+ message="At least one test has failed. See logs (in ${test.xml.dir}) or report (in ${test.report.dir})" />
+ </target>
+
+ <target name="coverage-report" depends="emma, test-report" unless="skip.test"
+ description="run tests with instrumentation and generate coverage report">
+ <mkdir dir="${coverage.report.dir}" />
+ <emma>
+ <report sourcepath="${src.dir}">
+ <fileset dir="${coverage.dir}">
+ <include name="*.emma" />
+ </fileset>
+
+ <txt outfile="${coverage.report.dir}/coverage.txt" />
+ <html outfile="${coverage.report.dir}/coverage.html" />
+ </report>
+ </emma>
+ </target>
+
+ <target name="ivy-report" depends="resolve">
+ <ivy:report todir="${ivy.report.dir}"/>
+ </target>
+
+ <target name="javadoc" unless="skip.javadoc">
+ <javadoc destdir="${javadoc.build.dir}" useexternalfile="true">
+ <fileset dir="${src.dir}" includes="**/*.java" />
+ </javadoc>
+ </target>
+
+ <target name="sources" depends="default-version" description="Create source archive files">
+ <mkdir dir="${artifacts.build.dir}/sources/"/>
+ <jar destfile="${artifacts.build.dir}/sources/${final.name}">
+ <metainf dir="${basedir}" includes="LICENSE,NOTICE" />
+ <manifest>
+ <attribute name="Specification-Title" value="Apache Ivy Sources" />
+ <attribute name="Specification-Version" value="${build.version}" />
+ <attribute name="Specification-Vendor" value="Apache Software Foundation" />
+ </manifest>
+ <fileset dir="${src.dir}" />
+ </jar>
+ </target>
+
+ <target name="fixcrlf">
+ <property name="eol.native.includes"
+ value="**/*.html,**/*.json,**/*.java,**/*.xml,**/*.txt,**/*.MF,**/*.properties,**/*.patterns,**/*.pom,**/*.xsl,**/*.css" />
+ <property name="eol.native.excludes"
+ value="build/**,bin/**,lib/**" />
+
+ <fileset id="eol.native.fileset"
+ dir="${basedir}"
+ includes="${eol.native.includes}"
+ excludes="${eol.native.excludes}" />
+
+ <fixcrlf srcdir="${basedir}"
+ includes="${eol.native.includes}"
+ excludes="${eol.native.excludes}" />
+ <apply executable="svn">
+ <fileset refid="eol.native.fileset" />
+ <arg value="propset" />
+ <arg value="svn:eol-style" />
+ <arg value='"native"' />
+ </apply>
+ </target>
+
+ <!-- Checks Ivy codebase according to ${checkstyle.src.dir}/checkstyle-config -->
+ <target name="checkstyle-internal" depends="jar">
+ <ivy:cachepath organisation="checkstyle" module="checkstyle" revision="5.0"
+ inline="true" conf="default" pathid="checkstyle.classpath" transitive="true"
+ log="download-only"/>
+ <taskdef resource="checkstyletask.properties" classpathref="checkstyle.classpath" />
+
+ <mkdir dir="${checkstyle.report.dir}" />
+ <checkstyle config="${checkstyle.src.dir}/checkstyle-config"
+ failOnViolation="false" failureProperty="checkstyle.failed">
+ <classpath>
+ <path refid="run.classpath" />
+ </classpath>
+ <formatter type="xml" toFile="${checkstyle.report.dir}/checkstyle.xml" />
+ <fileset dir="${src.dir}">
+ <include name="**/*.java" />
+ </fileset>
+ <fileset dir="${example.dir}">
+ <include name="**/*.java" />
+ </fileset>
+ </checkstyle>
+ </target>
+
+ <target name="checkstyle" depends="checkstyle-internal" description="checks Ivy codebase according to ${checkstyle.src.dir}/checkstyle-config">
+ <fail if="checkstyle.failed"
+ message="Checkstyle has errors. See report in ${checkstyle.report.dir}" />
+ </target>
+
+ <target name="checkstyle-report" depends="checkstyle-internal">
+ <property name="checkstyle.basedir" location="${src.dir}" />
+ <xslt in="${checkstyle.report.dir}/checkstyle.xml"
+ style="${checkstyle.src.dir}/checkstyle-frames.xsl"
+ out="${checkstyle.report.dir}/output.txt">
+ <param name="basedir" expression="${checkstyle.basedir}" />
+ </xslt>
+ </target>
+
+ <target name="init-findbugs" unless="findbugs.home">
+ <!-- Findbugs: Getting Findbugs -->
+ <property name="findbugs.download.name"
+ value="findbugs-1.3.5"
+ description="Name of the download file without suffix. Also the internal root directory of the ZIP."/>
+ <property name="findbugs.download.file"
+ value="${findbugs.download.name}.zip"
+ description="The filename of the ZIP."/>
+ <property name="findbugs.download.url"
+ value="http://garr.dl.sourceforge.net/sourceforge/findbugs/${findbugs.download.file}"
+ description="The download adress at a mirror of Sourceforge."/>
+ <property name="findbugs.download.to"
+ value="${build.dir}/.downloads"
+ description="Where to store the download and 'install' Findbugs."/>
+ <available
+ property="findbugs.home"
+ value="${findbugs.download.to}/${findbugs.download.name}"
+ file="${findbugs.download.to}/${findbugs.download.name}/lib/findbugs.jar"
+ description="Check if Findbugs is already installed."
+ />
+
+ <!-- Findbugs: Running Findbugs -->
+ <property name="findbugs.reportdir"
+ location="${reports.dir}/findbugs"
+ description="Where to store Findbugs results"/>
+ <property name="findbugs.raw"
+ value="raw.xml"
+ description="Findbugs Output xml-file"/>
+ <property name="findbugs.xsl"
+ value="fancy.xsl"
+ description="Which XSL to use for generating Output: default, fancy, plain, summary"/>
+ <property name="findbugs.jvmargs"
+ value="-Xms128m -Xmx512m"
+ description="JVMArgs for invoking Findbugs"/>
+
+ <mkdir dir="${findbugs.download.to}"/>
+ <get src="${findbugs.download.url}" dest="${findbugs.download.to}/${findbugs.download.file}"/>
+ <unzip src="${findbugs.download.to}/${findbugs.download.file}" dest="${findbugs.download.to}"/>
+ <property name="findbugs.home" location="${findbugs.download.to}/${findbugs.download.name}"/>
+ <mkdir dir="${findbugs.home}/plugin"/>
+ </target>
+
+ <target name="findbugs" description="checks Ivy codebase with Findbugs" depends="init-findbugs,compile-core" xmlns:fb="http://findbugs.sourceforge.net/">
+ <path id="findbugs.real.classpath">
+ <fileset dir="${findbugs.home}/lib" includes="*.jar"/>
+ </path>
+
+ <!-- Load the Findbugs AntTasks -->
+ <taskdef uri="http://findbugs.sourceforge.net/" resource="edu/umd/cs/findbugs/anttask/tasks.properties" classpathref="findbugs.real.classpath" />
+
+ <!-- Start Findbugs -->
+ <mkdir dir="${findbugs.reportdir}"/>
+ <fb:findbugs home="${findbugs.home}"
+ classpathref="findbugs.real.classpath"
+ output="xml:withMessages"
+ outputFile="${findbugs.reportdir}/${findbugs.raw}"
+ jvmargs="${findbugs.jvmargs}"
+ projectName="${Name} ${project.version}">
+ <class location="${core.classes.build.dir}" />
+ <sourcePath path="${src.dir}" />
+ </fb:findbugs>
+
+ <!-- Generate (human) readable output -->
+ <xslt basedir="${findbugs.reportdir}" includes="${findbugs.raw}" destdir="${findbugs.reportdir}">
+ <style>
+ <javaresource name="${findbugs.xsl}" classpathref="findbugs.real.classpath"/>
+ </style>
+ </xslt>
+ </target>
+
+ <!-- =================================================================
+ IDE SPECIFIC
+ ================================================================= -->
+ <available file="${basedir}/.classpath" property="eclipse.classpath.exists" />
+ <target name="check-eclipse-classpath-overwrite" if="eclipse.classpath.exists">
+ <input message=".classpath file already exists.${line.separator}Are you sure you want to overwrite it and loose your original file?"
+ validargs="Y,N,y,n" addproperty="eclipse.classpath.confirm" />
+ <condition property="eclipse.classpath.abort">
+ <equals arg1="${eclipse.classpath.confirm}" arg2="N" casesensitive="false" />
+ </condition>
+ </target>
+
+ <target name="eclipse-default" depends="resolve, check-eclipse-classpath-overwrite"
+ unless="eclipse.classpath.abort"
+ description="creates a default .classpath for eclipse, using jars resolved by this ant build">
+ <copy file="${basedir}/.classpath.default" tofile="${basedir}/.classpath" overwrite="true" />
+ </target>
+
+ <target name="eclipse-ivyde" depends="check-eclipse-classpath-overwrite"
+ unless="eclipse.classpath.abort"
+ description="creates a .classpath for eclipse using Apache IvyDE version">
+ <copy file="${basedir}/.classpath.ivyde" tofile="${basedir}/.classpath" overwrite="true" />
+ </target>
+</project>
diff --git a/ivy.xml b/ivy.xml
new file mode 100644
index 0000000..d448897
--- /dev/null
+++ b/ivy.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivy-module version="1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ant.apache.org/ivy/schemas/ivy.xsd">
+ <info organisation="org.apache.ivy" module="ivy" revision="2.4.0" status="release" publication="20141213170938">
+ <description homepage="http://ant.apache.org/ivy/">
+ Apache Ivy is a tool for managing (recording, tracking, resolving and reporting) project dependencies.
+ </description>
+ </info>
+ <configurations>
+ <conf name="core" description="only ivy jar, without any dependencies"/>
+ <conf name="httpclient" extends="core" description="core + optional httpclient for better http handling"/>
+ <conf name="oro" extends="core" description="to use optional glob matcher"/>
+ <conf name="vfs" extends="core" description="core + optional VirtualFileSystem(VFS) support"/>
+ <conf name="sftp" extends="core" description="core + optional SFTP support"/>
+ <conf name="standalone" extends="core" description="to launch in standalone mode (from command line)"/>
+ <conf name="ant" extends="core" description="core + ant jar provided as a dependency"/>
+ <conf name="default" extends="core" description="full ivy with all dependencies"/>
+ <conf name="test" description="dependencies used for junit testing ivy" visibility="private"/>
+ <conf name="source" description="ivy sources"/>
+ </configurations>
+ <publications>
+ <artifact name="ivy" type="jar" conf="core"/>
+ <artifact name="ivy" type="source" ext="jar" conf="source"/>
+ </publications>
+ <dependencies>
+ <dependency org="org.apache.ant" name="ant" rev="1.7.1" conf="default,ant->default"/>
+ <dependency org="org.apache.ant" name="ant-nodeps" rev="1.7.1" conf="default"/>
+ <dependency org="org.apache.ant" name="ant-trax" rev="1.7.1" conf="default"/>
+ <dependency org="commons-httpclient" name="commons-httpclient" rev="3.0" conf="default,httpclient->runtime,master"/>
+ <dependency org="oro" name="oro" rev="2.0.8" conf="default,oro->default"/>
+ <dependency org="commons-vfs" name="commons-vfs" rev="1.0" conf="default,vfs->default"/>
+ <dependency org="com.jcraft" name="jsch" rev="0.1.50" conf="default,sftp->default"/>
+ <dependency org="com.jcraft" name="jsch.agentproxy" rev="0.0.6" conf="default,sftp->default"/>
+ <dependency org="com.jcraft" name="jsch.agentproxy.connector-factory" rev="0.0.6" conf="default,sftp->default"/>
+ <dependency org="com.jcraft" name="jsch.agentproxy.jsch" rev="0.0.6" conf="default,sftp->default"/>
+ <dependency org="org.bouncycastle" name="bcpg-jdk14" rev="1.45" conf="default"/>
+ <dependency org="org.bouncycastle" name="bcprov-jdk14" rev="1.45" conf="default"/>
+
+ <!-- Test dependencies -->
+ <dependency org="junit" name="junit" rev="3.8.2" conf="test->default"/>
+ <dependency org="commons-lang" name="commons-lang" rev="2.6" conf="test->default"/>
+ <dependency org="org.apache.ant" name="ant-testutil" rev="1.7.0" conf="test->default" transitive="false"/>
+ <dependency org="ant" name="ant-launcher" rev="1.6.2" conf="test->default" transitive="false"/>
+ <dependency org="ant-contrib" name="ant-contrib" rev="1.0b3" conf="test->default" transitive="false"/>
+
+ <!-- This dependency is necessary for having validation in junit tests when running with JDK1.4 -->
+ <dependency org="xerces" name="xercesImpl" rev="2.6.2" conf="test->default"/>
+ <dependency org="xerces" name="xmlParserAPIs" rev="2.6.2" conf="test->default"/>
+
+ <!-- Global exclude for junit -->
+ <exclude org="junit" module="junit" conf="core,default,httpclient,oro,vfs,sftp,standalone,ant"/>
+ </dependencies>
+</ivy-module>
diff --git a/ivy.xsd b/ivy.xsd
new file mode 100644
index 0000000..fabad55
--- /dev/null
+++ b/ivy.xsd
@@ -0,0 +1,291 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
+
+ <xs:complexType name="configurations-conf">
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ <xs:attribute name="transitive" type="xs:boolean"/>
+ <xs:attribute name="extends" type="xs:string"/>
+ <xs:attribute name="description" type="xs:string"/>
+ <xs:attribute name="deprecated" type="xs:string"/>
+ <xs:attribute name="visibility" default="public">
+ <xs:simpleType>
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="private"/>
+ <xs:enumeration value="public"/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ <xs:anyAttribute namespace="##other" processContents="lax" />
+ </xs:complexType>
+
+ <xs:complexType name="global-exclude">
+ <xs:sequence>
+ <xs:element name="conf" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ <xs:attribute name="org" type="xs:string"/>
+ <xs:attribute name="module" type="xs:string"/>
+ <xs:attribute name="artifact" type="xs:string"/>
+ <xs:attribute name="type" type="xs:string"/>
+ <xs:attribute name="ext" type="xs:string"/>
+ <xs:attribute name="conf" type="xs:string"/>
+ <xs:attribute name="matcher" type="xs:string"/>
+ <xs:anyAttribute namespace="##other" processContents="lax" />
+ </xs:complexType>
+
+ <xs:element name="ivy-module">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="info">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="extends" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:attribute name="organisation" type="xs:string" use="required"/>
+ <xs:attribute name="module" type="xs:string" use="required"/>
+ <xs:attribute name="revision" type="xs:string" use="required"/>
+ <xs:attribute name="location" type="xs:string" />
+ <xs:attribute name="extendType" type="xs:string" />
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="license" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ <xs:attribute name="url" type="xs:string"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="ivyauthor" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ <xs:attribute name="url" type="xs:string"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="repository" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ <xs:attribute name="url" type="xs:string"/>
+ <xs:attribute name="pattern" type="xs:string"/>
+ <xs:attribute name="ivys" type="xs:boolean"/>
+ <xs:attribute name="artifacts" type="xs:boolean"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="description" minOccurs="0" maxOccurs="1">
+ <xs:complexType mixed="true">
+ <xs:sequence>
+ <xs:any minOccurs="0" maxOccurs="unbounded" processContents="lax"/>
+ </xs:sequence>
+ <xs:attribute name="homepage" type="xs:string"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other" processContents="lax"/>
+ </xs:sequence>
+ <xs:attribute name="organisation" type="xs:string"/>
+ <xs:attribute name="module" type="xs:string" use="required"/>
+ <xs:attribute name="branch" type="xs:string"/>
+ <xs:attribute name="revision" type="xs:string"/>
+ <xs:attribute name="status" type="xs:string"/>
+ <xs:attribute name="publication" type="xs:string"/>
+ <xs:attribute name="resolver" type="xs:string"/>
+ <xs:attribute name="namespace" type="xs:string"/>
+ <xs:attribute name="default" type="xs:boolean"/>
+ <xs:anyAttribute namespace="##other" processContents="lax" />
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="configurations" minOccurs="0">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="conf" type="configurations-conf"/>
+ <xs:element name="include">
+ <xs:complexType>
+ <xs:attribute name="file" type="xs:string"/>
+ <xs:attribute name="url" type="xs:string"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ </xs:sequence>
+ <xs:attribute name="defaultconf" type="xs:string"/>
+ <xs:attribute name="defaultconfmapping" type="xs:string"/>
+ <xs:attribute name="confmappingoverride" type="xs:boolean" />
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="publications" minOccurs="0">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="artifact" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="conf" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ <xs:attribute name="name" type="xs:string"/>
+ <xs:attribute name="type" type="xs:string"/>
+ <xs:attribute name="ext" type="xs:string"/>
+ <xs:attribute name="conf" type="xs:string"/>
+ <xs:attribute name="url" type="xs:string"/>
+ <xs:attribute name="packaging" type="xs:string"/>
+ <xs:anyAttribute namespace="##other" processContents="lax" />
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ <xs:attribute name="defaultconf" type="xs:string"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="dependencies" minOccurs="0">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="dependency" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="conf" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="mapped" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ <xs:attribute name="mapped" type="xs:string"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="artifact" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="conf" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ <xs:attribute name="type" type="xs:string"/>
+ <xs:attribute name="ext" type="xs:string"/>
+ <xs:attribute name="conf" type="xs:string"/>
+ <xs:attribute name="url" type="xs:string"/>
+ <xs:anyAttribute namespace="##other" processContents="lax" />
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="include" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="conf" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ <xs:attribute name="name" type="xs:string"/>
+ <xs:attribute name="type" type="xs:string"/>
+ <xs:attribute name="ext" type="xs:string"/>
+ <xs:attribute name="conf" type="xs:string"/>
+ <xs:attribute name="matcher" type="xs:string"/>
+ <xs:anyAttribute namespace="##other" processContents="lax" />
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="exclude" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="conf" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ <xs:attribute name="org" type="xs:string"/>
+ <xs:attribute name="module" type="xs:string"/>
+ <xs:attribute name="name" type="xs:string"/>
+ <xs:attribute name="type" type="xs:string"/>
+ <xs:attribute name="ext" type="xs:string"/>
+ <xs:attribute name="conf" type="xs:string"/>
+ <xs:attribute name="matcher" type="xs:string"/>
+ <xs:anyAttribute namespace="##other" processContents="lax" />
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ <xs:attribute name="org" type="xs:string"/>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ <xs:attribute name="branch" type="xs:string"/>
+ <xs:attribute name="branchConstraint" type="xs:string"/>
+ <xs:attribute name="rev" type="xs:string" use="required"/>
+ <xs:attribute name="revConstraint" type="xs:string"/>
+ <xs:attribute name="force" type="xs:boolean"/>
+ <xs:attribute name="changing" type="xs:boolean" default="false"/>
+ <xs:attribute name="transitive" type="xs:boolean" default="true"/>
+ <xs:attribute name="conf" type="xs:string"/>
+ <xs:anyAttribute namespace="##other" processContents="lax" />
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="exclude" type="global-exclude" minOccurs="0" maxOccurs="unbounded" />
+ <xs:element name="override" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:attribute name="org" type="xs:string"/>
+ <xs:attribute name="module" type="xs:string"/>
+ <xs:attribute name="matcher" type="xs:string"/>
+ <xs:attribute name="rev" type="xs:string"/>
+ <xs:attribute name="branch" type="xs:string"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="conflict" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:attribute name="org" type="xs:string"/>
+ <xs:attribute name="module" type="xs:string"/>
+ <xs:attribute name="manager" type="xs:string"/>
+ <xs:attribute name="rev" type="xs:string"/>
+ <xs:attribute name="matcher" type="xs:string"/>
+ <xs:anyAttribute namespace="##other" processContents="lax" />
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ <xs:attribute name="defaultconf" type="xs:string"/>
+ <xs:attribute name="defaultconfmapping" type="xs:string"/>
+ <xs:attribute name="confmappingoverride" type="xs:boolean" />
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="conflicts" minOccurs="0">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="manager" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:attribute name="org" type="xs:string"/>
+ <xs:attribute name="module" type="xs:string"/>
+ <xs:attribute name="name" type="xs:string"/>
+ <xs:attribute name="rev" type="xs:string"/>
+ <xs:attribute name="matcher" type="xs:string"/>
+ <xs:anyAttribute namespace="##other" processContents="lax" />
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ <xs:attribute name="version" type="xs:string" use="required"/>
+ </xs:complexType>
+ </xs:element>
+
+</xs:schema>
diff --git a/optional.patterns b/optional.patterns
new file mode 100644
index 0000000..6c119be
--- /dev/null
+++ b/optional.patterns
@@ -0,0 +1,37 @@
+# ***************************************************************
+# * Licensed to the Apache Software Foundation (ASF) under one
+# * or more contributor license agreements. See the NOTICE file
+# * distributed with this work for additional information
+# * regarding copyright ownership. The ASF licenses this file
+# * to you under the Apache License, Version 2.0 (the
+# * "License"); you may not use this file except in compliance
+# * with the License. You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing,
+# * software distributed under the License is distributed on an
+# * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# * KIND, either express or implied. See the License for the
+# * specific language governing permissions and limitations
+# * under the License.
+# ***************************************************************
+
+#This file defines the sources to compile for ivy-optional.jar
+org/apache/ivy/Main.java
+org/apache/ivy/plugins/matcher/GlobPatternMatcher.java
+org/apache/ivy/plugins/repository/sftp/**/*.java
+org/apache/ivy/plugins/repository/ssh/**/*.java
+org/apache/ivy/plugins/repository/vfs/**/*.java
+org/apache/ivy/plugins/repository/vsftp/**/*.java
+org/apache/ivy/plugins/resolver/AbstractSshBasedResolver.java
+org/apache/ivy/plugins/resolver/SFTPResolver.java
+org/apache/ivy/plugins/resolver/SshResolver.java
+org/apache/ivy/plugins/resolver/VfsResolver.java
+org/apache/ivy/plugins/resolver/VsftpResolver.java
+org/apache/ivy/plugins/resolver/packager/*.java
+org/apache/ivy/plugins/signer/bouncycastle/**/*.java
+org/apache/ivy/util/url/HttpClientHandler.java
+
+#This section defines the resources to copy for ivy-optional.jar
+org/apache/ivy/plugins/repository/vfs/*.xml
diff --git a/src/etc/checkstyle/RequiredHeader.txt b/src/etc/checkstyle/RequiredHeader.txt
new file mode 100644
index 0000000..6a5bb9a
--- /dev/null
+++ b/src/etc/checkstyle/RequiredHeader.txt
@@ -0,0 +1,17 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
diff --git a/src/etc/checkstyle/checkstyle-config b/src/etc/checkstyle/checkstyle-config
new file mode 100644
index 0000000..ae186c8
--- /dev/null
+++ b/src/etc/checkstyle/checkstyle-config
@@ -0,0 +1,152 @@
+<?xml version="1.0"?>
+<!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.1//EN" "http://www.puppycrawl.com/dtds/configuration_1_1.dtd">
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<module name="Checker">
+
+ <!-- required license file -->
+ <module name="Header">
+ <property name="headerFile" value="${checkstyle.src.dir}/RequiredHeader.txt"/>
+ <property name="ignoreLines" value="2"/>
+ </module>
+
+ <!-- Items moved out of TreeWalker for new versions of Checkstyle -->
+ <module name="FileLength"/>
+ <module name="FileTabCharacter"/>
+
+ <module name="TreeWalker">
+ <!-- Javadoc requirements -->
+ <!-- TODO uncomment this when javadoc will be improved
+ <module name="JavadocType">
+ <property name="scope" value="protected"/>
+ </module>
+ <module name="JavadocMethod">
+ <property name="scope" value="protected"/>
+ <property name="allowUndeclaredRTE" value="true"/>
+ </module>
+ <module name="JavadocVariable">
+ <property name="scope" value="public"/>
+ </module>
+ -->
+
+ <!-- element naming -->
+ <module name="PackageName"/>
+ <module name="TypeName"/>
+ <module name="ConstantName"/>
+ <module name="LocalFinalVariableName"/>
+ <module name="LocalVariableName"/>
+ <module name="MemberName"/>
+ <module name="MethodName"/>
+ <module name="ParameterName"/>
+ <module name="StaticVariableName"/>
+
+ <!-- Import conventions -->
+ <module name="AvoidStarImport"/>
+ <!-- <module name="IllegalImport"/> -->
+ <module name="RedundantImport"/>
+ <module name="UnusedImports"/>
+
+ <!-- size limits -->
+ <module name="LineLength">
+ <property name="max" value="100"/>
+ <property name="ignorePattern" value="^ *\* *[^ ]+$"/>
+ <property name="tabWidth" value="4"/>
+ </module>
+ <module name="MethodLength"/>
+ <module name="ParameterNumber"/>
+
+ <!-- whitespace checks -->
+ <module name="EmptyForIteratorPad"/>
+ <module name="NoWhitespaceAfter"/>
+ <module name="NoWhitespaceBefore"/>
+ <module name="OperatorWrap"/>
+ <module name="ParenPad"/>
+ <module name="WhitespaceAfter"/>
+ <module name="WhitespaceAround"/>
+
+ <!-- Modifier Checks -->
+ <module name="ModifierOrder"/>
+
+
+ <!-- Checks for blocks -->
+ <module name="AvoidNestedBlocks"/>
+ <module name="EmptyBlock">
+ <property name="option" value="text"/>
+ </module>
+ <module name="LeftCurly"/>
+ <module name="NeedBraces"/>
+ <module name="RightCurly"/>
+
+
+ <!-- Checks for common coding problems -->
+ <!--<module name="AvoidInlineConditionals"/> -->
+ <!--<module name="DoubleCheckedLocking"/>--> <!-- removed in checkstyle 5.6 -->
+ <module name="EmptyStatement"/>
+ <module name="EqualsHashCode"/>
+ <module name="IllegalInstantiation">
+ <property name="classes" value="java.lang.Boolean"/>
+ </module>
+ <module name="InnerAssignment"/>
+ <module name="MagicNumber"/>
+ <module name="MissingSwitchDefault"/>
+ <module name="RedundantThrows">
+ <property name="allowUnchecked" value="true"/>
+ </module>
+ <module name="SimplifyBooleanExpression"/>
+ <module name="SimplifyBooleanReturn"/>
+
+ <!-- Checks for class design -->
+ <!-- <module name="DesignForExtension"/> -->
+ <module name="FinalClass"/>
+ <module name="HideUtilityClassConstructor"/>
+ <module name="InterfaceIsType"/>
+ <module name="VisibilityModifier"/>
+
+ <!-- Miscellaneous other checks. -->
+ <module name="ArrayTypeStyle"/>
+ <!--
+ <module name="GenericIllegalRegexp">
+ <property name="format" value="\s+$"/>
+ <property name="message" value="Line has trailing spaces."/>
+ </module>
+ -->
+ <!--
+ <module name="TodoComment"/>
+ -->
+ <module name="UpperEll"/>
+ <!-- allow comment suppression of checks -->
+ <module name="FileContentsHolder"/>
+ </module>
+
+ <!--TODO: comment this out, if Simian is not present -->
+ <!--
+ <module name="au.com.redhillconsulting.simian.SimianCheck"/>
+ -->
+
+ <module name="SuppressionCommentFilter">
+ <property name="offCommentFormat" value="CheckStyle\:([\w\|]+) *OFF"/>
+ <property name="onCommentFormat" value="CheckStyle\:([\w\|]+) *ON"/>
+ <property name="checkFormat" value="$1"/>
+ </module>
+
+ <module name="SuppressionFilter">
+ <property name="file" value="${basedir}/src/etc/checkstyle/checkstyle-suppress.xml"/>
+ </module>
+
+</module>
diff --git a/src/etc/checkstyle/checkstyle-frames.xsl b/src/etc/checkstyle/checkstyle-frames.xsl
new file mode 100644
index 0000000..7fe4954
--- /dev/null
+++ b/src/etc/checkstyle/checkstyle-frames.xsl
@@ -0,0 +1,293 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
+ xmlns:lxslt="http://xml.apache.org/xslt"
+ xmlns:redirect="org.apache.xalan.lib.Redirect"
+ extension-element-prefixes="redirect">
+
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+ <xsl:output method="html" indent="yes" encoding="US-ASCII"/>
+ <xsl:decimal-format decimal-separator="." grouping-separator="," />
+
+ <xsl:param name="output.dir" select="'.'"/>
+ <xsl:param name="basedir" select="'.'"/>
+
+ <xsl:template match="checkstyle">
+ <!-- create the index.html -->
+ <redirect:write file="{$output.dir}/index.html">
+ <xsl:call-template name="index.html"/>
+ </redirect:write>
+
+ <!-- create the stylesheet.css -->
+ <redirect:write file="{$output.dir}/stylesheet.css">
+ <xsl:call-template name="stylesheet.css"/>
+ </redirect:write>
+
+ <!-- create the overview-summary.html at the root -->
+ <redirect:write file="{$output.dir}/overview-frame.html">
+ <xsl:apply-templates select="." mode="overview"/>
+ </redirect:write>
+
+ <!-- create the all-classes.html at the root -->
+ <redirect:write file="{$output.dir}/allclasses-frame.html">
+ <xsl:apply-templates select="." mode="all.classes"/>
+ </redirect:write>
+
+ <!-- process all files -->
+ <xsl:apply-templates select="file[count(error) != 0]"/>
+ </xsl:template>
+
+ <xsl:template name="index.html">
+ <html>
+ <head>
+ <title>CheckStyle Audit</title>
+ </head>
+ <frameset cols="20%,80%">
+ <frame src="allclasses-frame.html" name="fileListFrame"/>
+ <frame src="overview-frame.html" name="fileFrame"/>
+ </frameset>
+ <noframes>
+ <h2>Frame Alert</h2>
+ <p>
+ This document is designed to be viewed using the frames feature.
+ If you see this message, you are using a non-frame-capable web client.
+ </p>
+ </noframes>
+ </html>
+ </xsl:template>
+
+ <xsl:template name="pageHeader">
+ <table border="0" cellpadding="0" cellspacing="0" width="100%">
+ <tr>
+ <td class="text-align:right"><h2>CheckStyle Audit</h2></td>
+ </tr>
+ <tr>
+ <td class="text-align:right">Designed for use with
+ <a href='http://checkstyle.sourceforge.net/'>CheckStyle</a> and
+ <a href='http://ant.apache.org/'>Ant</a>.</td>
+ </tr>
+ </table>
+ <hr size="1"/>
+ </xsl:template>
+
+ <xsl:template match="checkstyle" mode="overview">
+ <html>
+ <head>
+ <link rel="stylesheet" type="text/css" href="stylesheet.css"/>
+ </head>
+ <body>
+ <!-- page header -->
+ <xsl:call-template name="pageHeader"/>
+
+ <!-- Summary part -->
+ <xsl:apply-templates select="." mode="summary"/>
+ <hr size="1" width="100%" align="left"/>
+
+ <!-- File list part -->
+ <xsl:apply-templates select="." mode="filelist"/>
+ </body>
+ </html>
+ </xsl:template>
+
+ <xsl:template name="stylesheet.css">
+ .bannercell {
+ border: 0px;
+ padding: 0px;
+ }
+ body {
+ margin-left: 10;
+ margin-right: 10;
+ font:normal 80% arial,helvetica,sanserif;
+ background-color:#FFFFFF;
+ color:#000000;
+ }
+ .oddrow td {
+ background: #efefef;
+ }
+ .evenrow td {
+ background: #fff;
+ }
+ th, td {
+ text-align: left;
+ vertical-align: top;
+ }
+ th {
+ font-weight:bold;
+ background: #ccc;
+ color: black;
+ }
+ table, th, td {
+ font-size:100%;
+ border: none
+ }
+ table.log tr td, tr th {
+
+ }
+ h2 {
+ font-weight:bold;
+ font-size:140%;
+ margin-bottom: 5;
+ }
+ h3 {
+ font-size:100%;
+ font-weight:bold;
+ background: #525D76;
+ color: white;
+ text-decoration: none;
+ padding: 5px;
+ margin-right: 2px;
+ margin-left: 2px;
+ margin-bottom: 0;
+ }
+ </xsl:template>
+
+ <!--
+ Creates an all-classes.html file that contains a link to all files.
+ -->
+ <xsl:template match="checkstyle" mode="all.classes">
+ <html>
+ <head>
+ <link rel="stylesheet" type="text/css" href="stylesheet.css"/>
+ </head>
+ <body>
+ <h2>Files</h2>
+ <p>
+ <table width="100%">
+ <!-- For each file create its part -->
+ <xsl:apply-templates select="file[count(error) != 0]" mode="all.classes">
+ <xsl:sort select="substring-after(@name, $basedir)"/>
+ </xsl:apply-templates>
+ </table>
+ </p>
+ </body>
+ </html>
+ </xsl:template>
+
+ <xsl:template match="checkstyle" mode="filelist">
+ <h3>Files</h3>
+ <table class="log" border="0" cellpadding="5" cellspacing="2" width="100%">
+ <tr>
+ <th>Name</th>
+ <th>Errors</th>
+ </tr>
+ <xsl:apply-templates select="file[count(error) != 0]" mode="filelist">
+ <xsl:sort select="count(error)" order="descending" data-type="number"/>
+ </xsl:apply-templates>
+ </table>
+ </xsl:template>
+
+ <xsl:template match="file" mode="filelist">
+ <tr>
+ <xsl:call-template name="alternated-row"/>
+ <td nowrap="nowrap">
+ <a>
+ <xsl:attribute name="href">
+ <xsl:text>files/</xsl:text><xsl:value-of select="substring-after(@name, $basedir)"/><xsl:text>.html</xsl:text>
+ </xsl:attribute>
+ <xsl:value-of select="substring-after(@name, $basedir)"/>
+ </a>
+ </td>
+ <td><xsl:value-of select="count(error)"/></td>
+ </tr>
+ </xsl:template>
+
+ <xsl:template match="file" mode="all.classes">
+ <tr>
+ <td nowrap="nowrap">
+ <a target="fileFrame">
+ <xsl:attribute name="href">
+ <xsl:text>files/</xsl:text><xsl:value-of select="substring-after(@name, $basedir)"/><xsl:text>.html</xsl:text>
+ </xsl:attribute>
+ <xsl:value-of select="substring-after(@name, $basedir)"/>
+ </a>
+ </td>
+ </tr>
+ </xsl:template>
+
+ <!--
+ transform string like a/b/c to ../../../
+ @param path the path to transform into a descending directory path
+ -->
+ <xsl:template name="path">
+ <xsl:param name="path"/>
+ <xsl:if test="contains($path,'/')">
+ <xsl:text>../</xsl:text>
+ <xsl:call-template name="path">
+ <xsl:with-param name="path"><xsl:value-of select="substring-after($path,'/')"/></xsl:with-param>
+ </xsl:call-template>
+ </xsl:if>
+ <xsl:if test="not(contains($path,'/')) and not($path = '')">
+ <xsl:text>../</xsl:text>
+ </xsl:if>
+ </xsl:template>
+
+ <xsl:template match="file">
+ <redirect:write file="{$output.dir}/files/{substring-after(@name, $basedir)}.html">
+ <html>
+ <head>
+ <link rel="stylesheet" type="text/css">
+ <xsl:attribute name="href"><xsl:call-template name="path"><xsl:with-param name="path" select="substring-after(@name, $basedir)"/></xsl:call-template><xsl:text>stylesheet.css</xsl:text></xsl:attribute>
+ </link>
+ </head>
+ <body>
+ <xsl:call-template name="pageHeader"/>
+ <h3>File <xsl:value-of select="substring-after(@name, $basedir)"/></h3>
+ <table class="log" border="0" cellpadding="5" cellspacing="2" width="100%">
+ <tr>
+ <th>Error Description</th>
+ <th>Line:Column</th>
+ </tr>
+ <xsl:for-each select="error">
+ <tr>
+ <xsl:call-template name="alternated-row"/>
+ <td><a title="{@source}"><xsl:value-of select="@message"/></a></td>
+ <td align="center"><xsl:value-of select="@line"/><xsl:if test="@column">:<xsl:value-of select="@column"/></xsl:if></td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ </body>
+ </html>
+ </redirect:write>
+ </xsl:template>
+
+ <xsl:template match="checkstyle" mode="summary">
+ <h3>Summary</h3>
+ <xsl:variable name="fileCount" select="count(file)"/>
+ <xsl:variable name="errorCount" select="count(file/error)"/>
+ <xsl:variable name="fileErrorCount" select="count(file[count(error) != 0])"/>
+ <table class="log" border="0" cellpadding="5" cellspacing="2" width="100%">
+ <tr>
+ <th>Total Files</th>
+ <th>Files With Errors</th>
+ <th>Errors</th>
+ </tr>
+ <tr>
+ <xsl:call-template name="alternated-row"/>
+ <td><xsl:value-of select="$fileCount"/></td>
+ <td><xsl:value-of select="$fileErrorCount"/></td>
+ <td><xsl:value-of select="$errorCount"/></td>
+ </tr>
+ </table>
+ </xsl:template>
+
+ <xsl:template name="alternated-row">
+ <xsl:attribute name="class">
+ <xsl:if test="position() mod 2 = 1">oddrow</xsl:if>
+ <xsl:if test="position() mod 2 = 0">evenrow</xsl:if>
+ </xsl:attribute>
+ </xsl:template>
+</xsl:stylesheet>
diff --git a/src/etc/checkstyle/checkstyle-suppress.xml b/src/etc/checkstyle/checkstyle-suppress.xml
new file mode 100644
index 0000000..2565f28
--- /dev/null
+++ b/src/etc/checkstyle/checkstyle-suppress.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE suppressions PUBLIC
+ "-//Puppy Crawl//DTD Suppressions 1.0//EN"
+ "http://www.puppycrawl.com/dtds/suppressions_1_0.dtd">
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<suppressions>
+ <suppress checks="MagicNumber" files=".*[\\/]test[\\/]java[\\/].*"/>
+</suppressions>
\ No newline at end of file
diff --git a/src/etc/checkstyle/checkstyle-text.xsl b/src/etc/checkstyle/checkstyle-text.xsl
new file mode 100644
index 0000000..333aecf
--- /dev/null
+++ b/src/etc/checkstyle/checkstyle-text.xsl
@@ -0,0 +1,33 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+ <xsl:strip-space elements="checkstyle"/>
+ <xsl:preserve-space elements="file"/>
+ <xsl:output method="text"/>
+ <xsl:template match="checkstyle/file/error">
+ <xsl:value-of select="../@name"/>
+ <xsl:text>:</xsl:text>
+ <xsl:value-of select="@line"/>
+ <xsl:text>:</xsl:text>
+ <xsl:value-of select="@column"/>
+ <xsl:text> </xsl:text>
+ <xsl:value-of select="@message"/>
+ </xsl:template>
+</xsl:stylesheet>
+
diff --git a/src/etc/checkstyle/checkstyle-xdoc.xsl b/src/etc/checkstyle/checkstyle-xdoc.xsl
new file mode 100644
index 0000000..4d7ab86
--- /dev/null
+++ b/src/etc/checkstyle/checkstyle-xdoc.xsl
@@ -0,0 +1,129 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
+ xmlns:lxslt="http://xml.apache.org/xslt"
+ xmlns:redirect="org.apache.xalan.lib.Redirect"
+ extension-element-prefixes="redirect">
+
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+ <xsl:output method="xml" indent="yes"/>
+ <xsl:decimal-format decimal-separator="." grouping-separator="," />
+
+ <xsl:param name="output.dir" select="'.'"/>
+ <xsl:param name="basedir" select="'.'"/>
+
+ <xsl:template match="checkstyle">
+ <document>
+ <properties>
+ <title>Checkstyle Audit</title>
+ </properties>
+
+ <body>
+ <xsl:apply-templates select="." mode="summary"/>
+ <!-- File list part -->
+ <xsl:apply-templates select="." mode="filelist"/>
+ <xsl:apply-templates select="file[count(error) != 0]"/>
+ </body>
+ </document>
+ </xsl:template>
+
+ <xsl:template match="checkstyle" mode="filelist">
+ <section name="Files">
+ <table>
+ <tr>
+ <th>Name</th>
+ <th>Errors</th>
+ </tr>
+ <xsl:apply-templates select="file[count(error) != 0]" mode="filelist">
+ <xsl:sort select="count(error)" order="descending" data-type="number"/>
+ </xsl:apply-templates>
+ </table>
+ </section>
+ </xsl:template>
+
+ <xsl:template match="file" mode="filelist">
+ <tr>
+ <xsl:call-template name="alternated-row"/>
+ <td nowrap="nowrap">
+ <a>
+ <xsl:attribute name="href">
+ <xsl:text>files</xsl:text><xsl:value-of select="substring-after(@name, $basedir)"/><xsl:text>.html</xsl:text>
+ </xsl:attribute>
+ <xsl:value-of select="substring-after(@name, $basedir)"/>
+ </a>
+ </td>
+ <td><xsl:value-of select="count(error)"/></td>
+ </tr>
+ </xsl:template>
+
+ <xsl:template match="file">
+ <redirect:write file="{$output.dir}/files{substring-after(@name, $basedir)}.xml">
+ <document>
+ <properties>
+ <title>Checkstyle Audit</title>
+ </properties>
+
+ <body>
+ <section name="Details for {substring-after(@name, $basedir)}">
+ <table>
+ <tr>
+ <th>Error Description</th>
+ <th>Line</th>
+ </tr>
+ <xsl:for-each select="error">
+ <tr>
+ <xsl:call-template name="alternated-row"/>
+ <td><a title="{@source}"><xsl:value-of select="@message"/></a></td>
+ <td><xsl:value-of select="@line"/></td>
+ </tr>
+ </xsl:for-each>
+ </table>
+ </section>
+ </body>
+ </document>
+ </redirect:write>
+ </xsl:template>
+
+ <xsl:template match="checkstyle" mode="summary">
+ <section name="Summary">
+ <xsl:variable name="fileCount" select="count(file)"/>
+ <xsl:variable name="errorCount" select="count(file/error)"/>
+ <xsl:variable name="fileErrorCount" select="count(file[count(error) != 0])"/>
+ <table>
+ <tr>
+ <th>Files</th>
+ <th>Files With Errors</th>
+ <th>Errors</th>
+ </tr>
+ <tr>
+ <xsl:call-template name="alternated-row"/>
+ <td><xsl:value-of select="$fileCount"/></td>
+ <td><xsl:value-of select="$fileErrorCount"/></td>
+ <td><xsl:value-of select="$errorCount"/></td>
+ </tr>
+ </table>
+ </section>
+ </xsl:template>
+
+ <xsl:template name="alternated-row">
+ <xsl:attribute name="class">
+ <xsl:if test="position() mod 2 = 1">oddrow</xsl:if>
+ <xsl:if test="position() mod 2 = 0">evenrow</xsl:if>
+ </xsl:attribute>
+ </xsl:template>
+</xsl:stylesheet>
+
diff --git a/src/etc/license/license-header.xml b/src/etc/license/license-header.xml
new file mode 100644
index 0000000..1c1b8cf
--- /dev/null
+++ b/src/etc/license/license-header.xml
@@ -0,0 +1,18 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
diff --git a/src/etc/makepom/pom.template b/src/etc/makepom/pom.template
new file mode 100644
index 0000000..1fe333d
--- /dev/null
+++ b/src/etc/makepom/pom.template
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.apache</groupId>
+ <artifactId>apache</artifactId>
+ <version>7</version>
+ </parent>
+ <groupId>${ivy.pom.groupId}</groupId>
+ <artifactId>${ivy.pom.artifactId}</artifactId>
+ <version>${ivy.pom.version}</version>
+ <name>Apache Ivy</name>
+ <url>${ivy.pom.url}</url>
+ <scm>
+ <connection>scm:svn:http://svn.apache.org/repos/asf/ant/ivy/core/trunk/</connection>
+ <developerConnection>scm:svn:https://svn.apache.org/repos/asf/ant/ivy/core/trunk</developerConnection>
+ <url>http://svn.apache.org/repos/asf/ant/ivy/core/trunk</url>
+ </scm>
+ <mailingLists>
+ <mailingList>
+ <name>Ant/Ivy Developers List</name>
+ <subscribe>dev-subscribe at ant.apache.org</subscribe>
+ <unsubscribe>dev-unsubscribe at ant.apache.org</unsubscribe>
+ <post>dev at ant.apache.org</post>
+ <archive>http://mail-archives.apache.org/mod_mbox/ant-dev</archive>
+ </mailingList>
+ <mailingList>
+ <name>Ivy Users List</name>
+ <subscribe>ivy-user-subscribe at ant.apache.org</subscribe>
+ <unsubscribe>ivy-user-unsubscribe at ant.apache.org</unsubscribe>
+ <post>ivy-user at ant.apache.org</post>
+ <archive>http://mail-archives.apache.org/mod_mbox/ant-ivy-user</archive>
+ </mailingList>
+ </mailingLists>
+ <issueManagement>
+ <system>jira</system>
+ <url>http://issues.apache.org/jira/browse/IVY</url>
+ </issueManagement>
+</project>
diff --git a/src/example/bintray/build.xml b/src/example/bintray/build.xml
new file mode 100644
index 0000000..c82359e
--- /dev/null
+++ b/src/example/bintray/build.xml
@@ -0,0 +1,23 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project xmlns:ivy="antlib:org.apache.ivy.ant" name="ivy-bintray-example" default="retrieve">
+ <target name="retrieve">
+ <ivy:retrieve />
+ </target>
+</project>
diff --git a/src/example/bintray/ivy.xml b/src/example/bintray/ivy.xml
new file mode 100644
index 0000000..120a43b
--- /dev/null
+++ b/src/example/bintray/ivy.xml
@@ -0,0 +1,31 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivy-module version="2.0">
+ <info organisation="org.apache" module="hello-ivy"/>
+ <dependencies>
+ <!-- https://jcenter.bintray.com/ -->
+ <dependency org="org.jfrog.artifactory.client" name="artifactory-cli" rev="1.0" />
+ <dependency org="org.jfrog.artifactory.client" name="artifactory-cli" rev="1.0" />
+ <dependency org="org.jfrog" name="build-info-api" rev="1.3.1" />
+ <!-- https://dl.bintray.com/dsowerby/maven/ -->
+ <dependency org="uk.q3c.krail" name="krail" rev="0.7.0" />
+ <!-- https://dl.bintray.com/igelgrun/batrak/ -->
+ <dependency org="igel.batrak" name="batrak-core" rev="0.1" />
+ </dependencies>
+</ivy-module>
diff --git a/src/example/bintray/ivysettings.xml b/src/example/bintray/ivysettings.xml
new file mode 100644
index 0000000..15adc16
--- /dev/null
+++ b/src/example/bintray/ivysettings.xml
@@ -0,0 +1,37 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivysettings>
+ <settings defaultResolver="chain"/>
+ <resolvers>
+ <chain name="chain">
+
+ <!-- https://jcenter.bintray.com/ -->
+ <bintray />
+
+ <!-- https://bintray.com/dsowerby/maven -->
+ <!-- https://dl.bintray.com/dsowerby/maven/ -->
+ <bintray subject="dsowerby" repo="maven"/>
+
+ <!-- https://bintray.com/igelgrun/batrak -->
+ <!-- https://dl.bintray.com/igelgrun/batrak/ -->
+ <bintray subject="igelgrun" repo="batrak"/>
+
+ </chain>
+ </resolvers>
+</ivysettings>
diff --git a/src/example/build-a-ivy-repository/build.xml b/src/example/build-a-ivy-repository/build.xml
new file mode 100644
index 0000000..ce83957
--- /dev/null
+++ b/src/example/build-a-ivy-repository/build.xml
@@ -0,0 +1,120 @@
+<?xml version="1.0"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<!-- ======================================================================
+ This is a sample project to build our own ivy repository.
+ ====================================================================== -->
+<project name="ivy-repository" default="maven2" xmlns:ivy="antlib:org.apache.ivy.ant">
+ <property name="settings.dir" value="settings"/>
+ <property name="from.resolver" value="libraries"/>
+ <property name="to.resolver" value="my-repository"/>
+
+ <property name="ivy.cache.dir" value="${basedir}/cache" />
+ <property name="dest.repo.dir" value="${basedir}/myrepository" />
+
+ <property name="ivy.jar.dir" value="${user.home}/.ivy2/jars" />
+ <property name="ivy.jar.file" value="${ivy.jar.dir}/ivy.jar" />
+
+ <!-- =================================
+ target: load-ivy
+ this target is not necessary if you put ivy.jar in your ant lib directory
+ if you already have ivy 1.4 in your ant lib, you can simply remove this
+ target
+ ================================= -->
+ <target name="load-ivy">
+ <!-- try to load ivy here from home ivy dir, in case the user has not already dropped
+ it into ant's lib dir (note that the latter copy will always take precedence).
+ We will not fail as long as ivy home lib dir exists (it may be empty) and
+ ivy is in at least one of ant's lib dir or the ivy home lib dir. -->
+ <path id="ivy.lib.path">
+ <pathelement location="${ivy.jar.file}"/>
+ </path>
+ <taskdef resource="org/apache/ivy/ant/antlib.xml"
+ uri="antlib:org.apache.ivy.ant" classpathref="ivy.lib.path"/>
+ </target>
+
+ <!-- - - - - - - - - - - - - - - - - -
+ target: init-ivy
+ - - - - - - - - - - - - - - - - - -->
+ <target name="init-ivy" depends="load-ivy">
+ <ivy:settings id="basic.settings" file="${settings.dir}/ivysettings-basic.xml"/>
+ <ivy:settings id="advanced.settings" file="${settings.dir}/ivysettings-advanced.xml"/>
+ </target>
+
+
+ <!-- =================================
+ target: maven2
+ maven 2 no namespace and no dependencies
+ ================================= -->
+ <target name="maven2" depends="init-ivy"
+ description="--> install module from maven 2 repository">
+ <ivy:install settingsRef="basic.settings"
+ organisation="commons-lang" module="commons-lang" revision="1.0"
+ from="${from.resolver}" to="${to.resolver}" />
+ </target>
+
+ <!-- =================================
+ target: maven2-deps
+ maven 2 no namespace with dependencies
+ ================================= -->
+ <target name="maven2-deps" depends="init-ivy"
+ description="--> install module from maven 2 repository with dependencies">
+ <ivy:install settingsRef="basic.settings"
+ organisation="org.hibernate" module="hibernate" revision="3.2.5.ga"
+ from="${from.resolver}" to="${to.resolver}" transitive="true" />
+ </target>
+
+ <!-- =================================
+ target: maven2-namespace
+ maven 2 with namespace no dependencies
+ ================================= -->
+ <target name="maven2-namespace" depends="init-ivy"
+ description="--> install module from maven 2 using namespaces">
+ <ivy:install settingsRef="advanced.settings"
+ organisation="apache" module="commons-lang" revision="1.0"
+ from="${from.resolver}" to="${to.resolver}" />
+ </target>
+
+ <!-- =================================
+ target: maven2-namespace-deps
+ maven 2 with namespace and dependencies
+ ================================= -->
+ <target name="maven2-namespace-deps" depends="init-ivy"
+ description="--> install module with dependencies from maven2 repo using namespaces">
+ <ivy:install settingsRef="advanced.settings"
+ organisation="hibernate" module="hibernate" revision="3.2.5.ga"
+ from="${from.resolver}" to="${to.resolver}" transitive="true" />
+ </target>
+
+ <!-- =================================
+ target: clean-cache
+ ================================= -->
+ <target name="clean-cache" depends="init-ivy" description="--> clean the cache">
+ <ivy:cleancache settingsRef="basic.settings" />
+ <ivy:cleancache settingsRef="advanced.settings" />
+ <delete dir="${ivy.cache.dir}" failonerror="true" />
+ </target>
+
+ <!-- =================================
+ target: clean-repo
+ ================================= -->
+ <target name="clean-repo" description="--> clean the destination repository">
+ <delete dir="${dest.repo.dir}" failonerror="true" />
+ </target>
+</project>
diff --git a/src/example/build-a-ivy-repository/settings/ivysettings-advanced.xml b/src/example/build-a-ivy-repository/settings/ivysettings-advanced.xml
new file mode 100644
index 0000000..09ca0d4
--- /dev/null
+++ b/src/example/build-a-ivy-repository/settings/ivysettings-advanced.xml
@@ -0,0 +1,161 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivysettings>
+ <settings defaultResolver="my-repository"
+ defaultConflictManager="all" /> <!-- in order to get all revisions without any eviction -->
+ <caches defaultCacheDir="${ivy.cache.dir}/advanced" />
+
+ <!--
+ You can override this property to use one of the mirrors listed on
+ http://docs.codehaus.org/display/MAVENUSER/Mirrors+Repositories
+ -->
+ <property name="ibiblio-maven2-root" value="https://repo1.maven.org/maven2/" override="false" />
+
+ <namespaces>
+ <namespace name="maven2">
+ <rule> <!-- imported apache maven1 projects -->
+ <fromsystem>
+ <src org="apache" module=".+"/>
+
+ <dest org="$m0" module="$m0"/>
+ </fromsystem>
+ <tosystem>
+ <src org="commons-.+" module="commons-.+" />
+ <src org="ant" module=".*" />
+ <src org="avalon-.+" module="avalon-.+" />
+ <src org="avalon" module="avalon" />
+ <src org="axis" module="axis" />
+ <src org="axis2" module="axis2" />
+ <src org="batik" module="batik" />
+ <src org="bcel" module="bcel" />
+ <src org="beehive" module="beehive" />
+ <src org="bsf" module="bsf" />
+ <src org="cactus" module="cactus" />
+ <src org="cocoon" module="cocoon" />
+ <src org="cornerstone-.+" module="cornerstone-.+" />
+ <src org="create-checksums" module="create-checksums" />
+ <src org="crimson" module="crimson" />
+ <src org="directory-.+" module="directory-.+" />
+ <src org="directory" module="directory" />
+ <src org="ecs" module="ecs" />
+ <src org="excalibur-.+" module="excalibur-.+" />
+ <src org="excalibur" module="excalibur" />
+ <src org="fop" module="fop" />
+ <src org="fulcrum" module="fulcrum" />
+ <src org="geronimo-.+" module="geronimo-.+" />
+ <src org="geronimo" module="geronimo" />
+ <src org="hivemind" module="hivemind" />
+ <src org="jakarta-regexp" module="jakarta-regexp" />
+ <src org="james" module="james" />
+ <src org="jaxme" module="jaxme" />
+ <src org="jcs-javagroups" module="jcs-javagroups" />
+ <src org="jcs" module="jcs" />
+ <src org="jspapi" module="jspapi" />
+ <src org="jstl" module="jstl" />
+ <src org="juddi" module="juddi" />
+ <src org="log4j" module="log4j" />
+ <src org="logkit" module="logkit" />
+ <src org="lucene" module="lucene" />
+ <src org="magicGball" module="magicGball" />
+ <src org="maven" module="maven" />
+ <src org="merlin-developer" module="merlin-developer" />
+ <src org="merlin" module="merlin" />
+ <src org="muse" module="muse" />
+ <src org="myfaces" module="myfaces" />
+ <src org="nekohtml" module="nekohtml" />
+ <src org="ojb" module="ojb" />
+ <src org="oro" module="oro" />
+ <src org="pluto-container" module="pluto-container" />
+ <src org="poi" module="poi" />
+ <src org="pubscribe" module="pubscribe" />
+ <src org="sandesha" module="sandesha" />
+ <src org="servletapi" module="servletapi" />
+ <src org="slide" module=".*" />
+ <src org="stratum" module="stratum" />
+ <src org="struts" module="struts" />
+ <src org="taglibs" module="taglibs" />
+ <src org="tapestry" module="tapestry" />
+ <src org="tomcat-util" module="tomcat-util" />
+ <src org="tomcat" module="tomcat" />
+ <src org="torque" module="torque" />
+ <src org="turbine" module="turbine" />
+ <src org="velocity-.+" module="velocity-.+" />
+ <src org="velocity" module="velocity" />
+ <src org="ws-commons.*" module="ws-commons.*" />
+ <src org="wsdl4j" module="wsdl4j" />
+ <src org="wsrf" module="wsrf" />
+ <src org="xalan" module="xalan" />
+ <src org="xerces" module=".*" />
+ <src org="xercesjarv" module="xercesjarv" />
+ <src org="xml-apis" module="xml-apis" />
+ <src org="xml-resolver" module="xml-resolver" />
+ <src org="xml-security" module="xml-security" />
+ <src org="xmlbeans" module="xmlbeans" />
+ <src org="xmlrpc" module="xmlrpc" />
+
+ <dest org="apache" module="$m0"/>
+ </tosystem>
+ </rule>
+
+ <rule> <!-- new apache projects -->
+ <fromsystem>
+ <src org="apache" />
+ <dest org="org.apache"/>
+ </fromsystem>
+ <tosystem>
+ <src org="org.apache" />
+ <dest org="apache" />
+ </tosystem>
+ </rule>
+ <rule> <!-- hibernate -->
+ <fromsystem>
+ <src org="hibernate" />
+ <dest org="org.hibernate"/>
+ </fromsystem>
+ <tosystem>
+ <src org="org.hibernate" />
+ <dest org="hibernate" />
+ </tosystem>
+ </rule>
+ <rule> <!-- net.sf projects -->
+ <fromsystem>
+ <src org="ehcache" />
+ <dest org="net.sf.$o0"/>
+ </fromsystem>
+ <tosystem>
+ <src org="net.sf.(.+)" />
+ <dest org="$o1" />
+ </tosystem>
+ </rule>
+ </namespace>
+ </namespaces>
+
+ <resolvers>
+ <filesystem name="my-repository">
+ <ivy pattern="${dest.repo.dir}/advanced/[organisation]/[module]/ivys/ivy-[revision].xml"/>
+ <artifact pattern="${dest.repo.dir}/advanced/[organisation]/[module]/[type]s/[artifact]-[revision].[ext]"/>
+ </filesystem>
+
+ <ibiblio name="libraries"
+ root="${ibiblio-maven2-root}"
+ m2compatible="true"
+ namespace="maven2"
+ />
+ </resolvers>
+</ivysettings>
diff --git a/src/example/build-a-ivy-repository/settings/ivysettings-basic.xml b/src/example/build-a-ivy-repository/settings/ivysettings-basic.xml
new file mode 100644
index 0000000..2538916
--- /dev/null
+++ b/src/example/build-a-ivy-repository/settings/ivysettings-basic.xml
@@ -0,0 +1,30 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivysettings>
+ <settings defaultResolver="libraries"
+ defaultConflictManager="all" /> <!-- in order to get all revisions without any eviction -->
+ <caches defaultCacheDir="${ivy.cache.dir}/no-namespace" />
+ <resolvers>
+ <ibiblio name="libraries" m2compatible="true" />
+ <filesystem name="my-repository">
+ <ivy pattern="${dest.repo.dir}/no-namespace/[organisation]/[module]/ivys/ivy-[revision].xml"/>
+ <artifact pattern="${dest.repo.dir}/no-namespace/[organisation]/[module]/[type]s/[artifact]-[revision].[ext]"/>
+ </filesystem>
+ </resolvers>
+</ivysettings>
diff --git a/src/example/chained-resolvers/build.xml b/src/example/chained-resolvers/build.xml
new file mode 100644
index 0000000..b8e0122
--- /dev/null
+++ b/src/example/chained-resolvers/build.xml
@@ -0,0 +1,38 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project default="clean-all" xmlns:ivy="antlib:org.apache.ivy.ant">
+
+ <!-- =================================
+ target: clean
+ ================================= -->
+ <target name="clean" description="--> clean directories">
+ <ant dir="chainedresolvers-project" antfile="build.xml" target="clean" inheritall="false" inheritrefs="false" />
+ </target>
+
+ <!-- =================================
+ target: clean-cache
+ ================================= -->
+ <target name="clean-cache" description="--> clean the ivy cache">
+ <property name="ivy.settings.dir" value="settings" />
+ <ivy:settings file="${ivy.settings.dir}/ivysettings.xml" />
+ <ivy:cleancache />
+ </target>
+
+ <target name="clean-all" depends="clean, clean-cache" description="--> clean directories and ivy cache"/>
+</project>
diff --git a/src/example/chained-resolvers/chainedresolvers-project/build.xml b/src/example/chained-resolvers/chainedresolvers-project/build.xml
new file mode 100644
index 0000000..53dcaee
--- /dev/null
+++ b/src/example/chained-resolvers/chainedresolvers-project/build.xml
@@ -0,0 +1,81 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project name="chainedresolvers-project" default="run" xmlns:ivy="antlib:org.apache.ivy.ant">
+ <!-- some variables used -->
+ <property name="lib.dir" value="${basedir}/lib" />
+ <property name="build.dir" value="${basedir}/build" />
+ <property name="src.dir" value="${basedir}/src" />
+
+ <!-- ivy properties used -->
+ <property name="ivy.settings.dir" value="../settings" />
+
+ <!-- paths used for compilation and run -->
+ <path id="lib.path.id">
+ <fileset dir="${lib.dir}" />
+ </path>
+ <path id="run.path.id">
+ <path refid="lib.path.id" />
+ <path location="${build.dir}" />
+ </path>
+
+ <ivy:settings file="${ivy.settings.dir}/ivysettings.xml" />
+
+ <!-- =================================
+ target: resolve
+ ================================= -->
+ <target name="resolve" description="--> resolve and retrieve dependencies with ivy">
+ <ivy:retrieve/>
+ </target>
+
+ <!-- =================================
+ target: report
+ ================================= -->
+ <target name="report" depends="resolve" description="--> generates a report of dependencies">
+ <ivy:report todir="${build.dir}"/>
+ </target>
+
+ <!-- =================================
+ target: run
+ ================================= -->
+ <target name="run" depends="resolve" description="--> compile and run the project">
+ <mkdir dir="${build.dir}" />
+ <javac srcdir="${src.dir}" destdir="${build.dir}" classpathref="lib.path.id" />
+ <java classpathref="run.path.id" classname="example.Hello"/>
+ </target>
+
+ <!-- =================================
+ target: clean
+ ================================= -->
+ <target name="clean" description="--> clean the project">
+ <delete includeemptydirs="true">
+ <fileset dir="${basedir}">
+ <exclude name="src/**" />
+ <exclude name="build.xml" />
+ <exclude name="ivy.xml" />
+ </fileset>
+ </delete>
+ </target>
+
+ <!-- =================================
+ target: clean-cache
+ ================================= -->
+ <target name="clean-cache" description="--> clean the ivy cache">
+ <ivy:cleancache />
+ </target>
+</project>
diff --git a/src/example/chained-resolvers/chainedresolvers-project/ivy.xml b/src/example/chained-resolvers/chainedresolvers-project/ivy.xml
new file mode 100644
index 0000000..450d0a6
--- /dev/null
+++ b/src/example/chained-resolvers/chainedresolvers-project/ivy.xml
@@ -0,0 +1,25 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivy-module version="1.0">
+ <info organisation="org.apache" module="chained-resolvers"/>
+ <dependencies>
+ <dependency org="commons-lang" name="commons-lang" rev="2.0" conf="default"/>
+ <dependency name="test" rev="1.0"/>
+ </dependencies>
+</ivy-module>
diff --git a/src/example/chained-resolvers/chainedresolvers-project/src/example/Hello.java b/src/example/chained-resolvers/chainedresolvers-project/src/example/Hello.java
new file mode 100644
index 0000000..d9bc0ac
--- /dev/null
+++ b/src/example/chained-resolvers/chainedresolvers-project/src/example/Hello.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package example;
+
+import org.apache.commons.lang.WordUtils;
+
+/**
+ * Simple example world to show how easy it is to retreive libs with ivy !!!
+ */
+public final class Hello {
+ public static void main(String[] args) {
+ String message = "example world !";
+ System.out.println("standard message :" + message);
+ System.out.println("capitalized by " + WordUtils.class.getName()
+ + " : " + WordUtils.capitalizeFully(message));
+ System.out.println("upperCased by " + test.StringUtils.class.getName()
+ + " : " + test.StringUtils.upperCase(message));
+ }
+
+ private Hello() {
+ }
+}
diff --git a/src/example/chained-resolvers/settings/ivysettings.xml b/src/example/chained-resolvers/settings/ivysettings.xml
new file mode 100644
index 0000000..b5790ee
--- /dev/null
+++ b/src/example/chained-resolvers/settings/ivysettings.xml
@@ -0,0 +1,29 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivysettings>
+ <settings defaultResolver="chain-example"/>
+ <resolvers>
+ <chain name="chain-example">
+ <filesystem name="libraries">
+ <artifact pattern="${ivy.settings.dir}/repository/[artifact]-[revision].[ext]" />
+ </filesystem>
+ <ibiblio name="ibiblio" m2compatible="true" />
+ </chain>
+ </resolvers>
+</ivysettings>
diff --git a/src/example/configurations/jdbc-example/build.xml b/src/example/configurations/jdbc-example/build.xml
new file mode 100644
index 0000000..df5eaca
--- /dev/null
+++ b/src/example/configurations/jdbc-example/build.xml
@@ -0,0 +1,97 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project name="configurations" default="run.dev" xmlns:ivy="antlib:org.apache.ivy.ant">
+ <!-- some variables used -->
+ <property name="lib.dir" value="lib" />
+ <property name="build.dir" value="build" />
+ <property name="src.dir" value="src" />
+
+ <!-- paths used for compilation and run -->
+ <path id="compile.path.id">
+ <fileset dir="${lib.dir}/compile" />
+ </path>
+ <path id="lib.run.dev.id">
+ <path location="${build.dir}" />
+ <fileset dir="${lib.dir}/rundev" />
+ </path>
+ <path id="lib.run.prod.id">
+ <path location="${build.dir}" />
+ <fileset dir="${lib.dir}/runprod" />
+ </path>
+
+ <!-- =================================
+ target: resolve
+ ================================= -->
+ <target name="resolve" description="--> retreive dependencies with ivy">
+ <!-- conf="*" will copie artifacts defined for each conf in a dir matching conf name -->
+ <ivy:retrieve pattern="${ivy.lib.dir}/[conf]/[artifact]-[revision].[ext]"/>
+ </target>
+
+ <!-- =================================
+ target: report
+ ================================= -->
+ <target name="report" depends="resolve" description="--> generates a report of dependencies">
+ <ivy:report todir="${build.dir}"/>
+ </target>
+
+ <!-- =================================
+ target: run.dev
+ ================================= -->
+ <target name="run.dev" depends="resolve" description="--> compile and run the project">
+ <mkdir dir="${build.dir}" />
+ <javac srcdir="${src.dir}" destdir="${build.dir}" classpathref="lib.run.dev.id" includeAntRuntime="false"/>
+ <copy todir="${build.dir}">
+ <fileset dir="${src.dir}" includes="**/*.properties"></fileset>
+ </copy>
+ <java classpathref="lib.run.dev.id" classname="example.ConfigurationsExample" fork="true">
+ <arg value="--dev"/>
+ </java>
+ </target>
+
+ <!-- =================================
+ target: run.prod
+ ================================= -->
+ <target name="run.prod" depends="resolve" description="--> compile and run the project">
+ <mkdir dir="${build.dir}" />
+ <javac srcdir="${src.dir}" destdir="${build.dir}" classpathref="lib.run.prod.id" includeAntRuntime="false"/>
+ <copy todir="${build.dir}">
+ <fileset dir="${src.dir}" includes="**/*.properties"></fileset>
+ </copy>
+ <java classpathref="lib.run.prod.id" classname="example.ConfigurationsExample" fork="true" />
+ </target>
+ <!-- =================================
+ target: clean
+ ================================= -->
+ <target name="clean" description="--> clean the project">
+ <delete includeemptydirs="true">
+ <fileset dir="${basedir}">
+ <exclude name="src/**" />
+ <exclude name="build.xml" />
+ <exclude name="ivy.xml" />
+ </fileset>
+ </delete>
+ </target>
+
+ <!-- =================================
+ target: clean-cache
+ ================================= -->
+ <target name="clean-cache" description="--> clean the ivy cache">
+ <ivy:cleancache />
+ </target>
+</project>
diff --git a/src/example/configurations/jdbc-example/ivy.xml b/src/example/configurations/jdbc-example/ivy.xml
new file mode 100644
index 0000000..41f080e
--- /dev/null
+++ b/src/example/configurations/jdbc-example/ivy.xml
@@ -0,0 +1,44 @@
+<?xml-stylesheet type="text/xsl" href="http://www.ivyrep.org/ivy-doc.xsl"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivy-module version="1.0">
+ <info organisation="org.apache" module="configurations" >
+ <description>
+ This is an example project that aims to demonstrate the usage of the configuration in ivy.
+ This project provide 4 configurations. Each configurations describe the requirement to build or run the project
+ </description>
+ </info>
+ <configurations>
+ <conf name="compile" description="This is this configuration that describes modules need to build our project"/>
+ <conf name="test" extends="compile" description="This is this configuration that describes modules need to run test on our project"/>
+ <conf name="rundev" extends="compile" description="This is this configuration that describes modules need to execute our project in a dev environement"/>
+ <conf name="runprod" extends="compile" description="This is this configuration that describes modules need to execute our project in a production environement"/>
+ </configurations>
+
+ <dependencies>
+ <!-- this dependency is needed for all configuration -->
+ <dependency org="commons-cli" name="commons-cli" rev="1.0" />
+ <!-- when launching our app in dev mode we use mckoi db and mckoi jdbc client conf="run.dev->embedded, client"-->
+ <dependency org="mckoi" name="mckoi" rev="1.0.2" conf="rundev->default"/>
+ <!-- when launching our app in production environement we needs other jdbc driver -->
+ <dependency org="mm-mysql" name="mm-mysql" rev="2.0.7" conf="runprod->default"/>
+ <!-- junit is only need in the test configuration-->
+ <dependency org="junit" name="junit" rev="3.8" conf="test->default"/>
+ </dependencies>
+</ivy-module>
diff --git a/src/example/configurations/jdbc-example/src/dev.properties b/src/example/configurations/jdbc-example/src/dev.properties
new file mode 100644
index 0000000..ca2146a
--- /dev/null
+++ b/src/example/configurations/jdbc-example/src/dev.properties
@@ -0,0 +1,19 @@
+# ***************************************************************
+# * Licensed to the Apache Software Foundation (ASF) under one
+# * or more contributor license agreements. See the NOTICE file
+# * distributed with this work for additional information
+# * regarding copyright ownership. The ASF licenses this file
+# * to you under the Apache License, Version 2.0 (the
+# * "License"); you may not use this file except in compliance
+# * with the License. You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing,
+# * software distributed under the License is distributed on an
+# * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# * KIND, either express or implied. See the License for the
+# * specific language governing permissions and limitations
+# * under the License.
+# ***************************************************************
+driver.class=com.mckoi.JDBCDriver
diff --git a/src/example/configurations/jdbc-example/src/example/ConfigurationsExample.java b/src/example/configurations/jdbc-example/src/example/ConfigurationsExample.java
new file mode 100644
index 0000000..fba4535
--- /dev/null
+++ b/src/example/configurations/jdbc-example/src/example/ConfigurationsExample.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package example;
+
+import java.io.IOException;
+import java.util.Properties;
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+import org.apache.commons.cli.PosixParser;
+
+public final class ConfigurationsExample {
+
+ public static void main(String[] args) {
+ String jdbcPropToLoad = "prod.properties";
+ CommandLineParser parser = new PosixParser();
+ Options options = new Options();
+ options.addOption("d", "dev", false,
+ "Dev tag to launch app in dev mode. Means that app will launch embedded mckoi db.");
+ try {
+ CommandLine line = parser.parse(options, args);
+ if (line.hasOption("d")) {
+ System.err.println("App is in DEV mode");
+ jdbcPropToLoad = "dev.properties";
+ }
+ } catch (ParseException exp) {
+ System.err.println("Parsing failed. Reason: " + exp.getMessage());
+ }
+ Properties p = new Properties();
+ try {
+ p.load(ConfigurationsExample.class.getResourceAsStream("/" + jdbcPropToLoad));
+ } catch (IOException e) {
+ System.err.println("Properties loading failed. Reason: " + e.getMessage());
+ }
+ try {
+ String clazz = p.getProperty("driver.class");
+ Class.forName(clazz);
+ System.out.println(" Jdbc driver loaded :" + clazz);
+ } catch (ClassNotFoundException e) {
+ System.err.println("Jdbc Driver class loading failed. Reason: " + e.getMessage());
+ e.printStackTrace();
+ }
+
+ }
+
+ private ConfigurationsExample() {
+ }
+}
diff --git a/src/example/configurations/jdbc-example/src/prod.properties b/src/example/configurations/jdbc-example/src/prod.properties
new file mode 100644
index 0000000..48c25ee
--- /dev/null
+++ b/src/example/configurations/jdbc-example/src/prod.properties
@@ -0,0 +1,19 @@
+# ***************************************************************
+# * Licensed to the Apache Software Foundation (ASF) under one
+# * or more contributor license agreements. See the NOTICE file
+# * distributed with this work for additional information
+# * regarding copyright ownership. The ASF licenses this file
+# * to you under the Apache License, Version 2.0 (the
+# * "License"); you may not use this file except in compliance
+# * with the License. You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing,
+# * software distributed under the License is distributed on an
+# * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# * KIND, either express or implied. See the License for the
+# * specific language governing permissions and limitations
+# * under the License.
+# ***************************************************************
+driver.class=org.gjt.mm.mysql.Driver
diff --git a/src/example/configurations/multi-projects/filter-framework/build.xml b/src/example/configurations/multi-projects/filter-framework/build.xml
new file mode 100644
index 0000000..566f011
--- /dev/null
+++ b/src/example/configurations/multi-projects/filter-framework/build.xml
@@ -0,0 +1,140 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project name="filter-framework" default="publish" xmlns:ivy="antlib:org.apache.ivy.ant">
+ <!-- some variables used -->
+ <property name="lib.dir" value="lib" />
+ <property name="build.dir" value="build" />
+ <property name="distrib.dir" location="distrib" />
+ <property name="src.dir" value="src" />
+ <property name="test.dir" value="test" />
+ <property name="build.test.dir" value="${build.dir}/test-classes" />
+ <property name="report.test.dir" value="${build.dir}/test-report" />
+ <property name="revision" value="1.3" />
+
+ <property name="ivy.local.default.root" location="${user.home}/.ivy2/local"/>
+
+ <!-- paths used for compilation and run -->
+ <path id="compile.path.id">
+ <fileset dir="${lib.dir}/cc-impl" />
+ </path>
+
+ <path id="test.path.id">
+ <path location="${build.dir}" />
+ <path location="${build.test.dir}" />
+ <fileset dir="${lib.dir}/test" />
+ </path>
+
+ <!-- =================================
+ target: resolve
+ ================================= -->
+ <target name="resolve" description="--> retreive dependencies with ivy">
+ <!-- conf="*" will copie artifacts defined for each conf in a dir matching conf name -->
+ <ivy:retrieve pattern="${ivy.lib.dir}/[conf]/[artifact]-[revision].[ext]"/>
+ </target>
+
+ <!-- =================================
+ target: build
+ ================================= -->
+ <target name="build" depends="clean, resolve" description="--> compile and jar project">
+ <mkdir dir="${build.dir}" />
+ <mkdir dir="${distrib.dir}"/>
+ <javac srcdir="${src.dir}" destdir="${build.dir}" classpathref="compile.path.id" includeAntRuntime="false"/>
+ <jar destfile="${distrib.dir}/filter-api.jar" >
+ <fileset dir="${build.dir}">
+ <include name="filter/*.class"/>
+ </fileset>
+ </jar>
+ <jar destfile="${distrib.dir}/filter-hmimpl.jar" >
+ <fileset dir="${build.dir}">
+ <include name="filter/hmimpl/*.class"/>
+ </fileset>
+ </jar>
+ <jar destfile="${distrib.dir}/filter-ccimpl.jar" >
+ <fileset dir="${build.dir}">
+ <include name="filter/ccimpl/*.class"/>
+ </fileset>
+ </jar>
+ </target>
+
+ <!-- =================================
+ target: test
+ ================================= -->
+ <target name="test" depends="build" description="--> compile and test the project">
+ <mkdir dir="${report.test.dir}"/>
+ <mkdir dir="${build.test.dir}"/>
+
+ <javac srcdir="${test.dir}" destdir="${build.test.dir}" classpathref="test.path.id"/>
+ <junit printsummary="yes" fork="yes" haltonfailure="yes" >
+ <classpath refid="test.path.id"/>
+ <formatter type="plain"/>
+ <batchtest todir="${report.test.dir}" >
+ <fileset dir="${build.test.dir}">
+ <include name="**/**Test.*"/>
+ </fileset>
+ </batchtest>
+ </junit>
+ </target>
+ <!-- =================================
+ target: publish
+ ================================= -->
+ <target name="publish" depends="test" description="--> compile test and publish this project in the local ivy repository">
+ <property name="revision" value="${revision}"/>
+ <ivy:publish artifactspattern="${distrib.dir}/[artifact].[ext]"
+ resolver="local"
+ pubrevision="${revision}"
+ status="release"/>
+ <echo message="project ${ant.project.name} released with version ${revision}" />
+ </target>
+ <!-- =================================
+ target: clean
+ ================================= -->
+ <target name="clean" description="--> clean the project">
+ <delete includeemptydirs="true">
+ <fileset dir="${basedir}">
+ <exclude name="src/**" />
+ <exclude name="test/**" />
+ <exclude name="build.xml" />
+ <exclude name="ivy.xml" />
+ <exclude name=".*" />
+ </fileset>
+ </delete>
+ </target>
+
+ <!-- =================================
+ target: clean-cache
+ ================================= -->
+ <target name="clean-cache" description="--> clean the ivy cache">
+ <ivy:cleancache />
+ </target>
+
+ <!-- =================================
+ target: clean-local
+ ================================= -->
+ <target name="clean-local" description="--> clean the local user repository">
+ <delete dir="${ivy.local.default.root}"/>
+ </target>
+
+
+ <!-- =================================
+ target: report
+ ================================= -->
+ <target name="report" depends="resolve" description="--> generates a report of dependencies">
+ <ivy:report todir="${build.dir}"/>
+ </target>
+</project>
diff --git a/src/example/configurations/multi-projects/filter-framework/ivy.xml b/src/example/configurations/multi-projects/filter-framework/ivy.xml
new file mode 100644
index 0000000..952f577
--- /dev/null
+++ b/src/example/configurations/multi-projects/filter-framework/ivy.xml
@@ -0,0 +1,36 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivy-module version="1.0">
+ <info organisation="org.apache" module="filter-framework"/>
+ <configurations>
+ <conf name="api" description="only provide filter framework API"/>
+ <conf name="homemade-impl" extends="api" description="provide a home made implementation of our api"/>
+ <conf name="cc-impl" extends="api" description="provide an implementation that use apache common collection framework"/>
+ <conf name="test" extends="cc-impl" visibility="private" description="for testing our framework"/>
+ </configurations>
+ <publications>
+ <artifact name="filter-api" type="jar" conf="api" ext="jar"/>
+ <artifact name="filter-hmimpl" type="jar" conf="homemade-impl" ext="jar"/>
+ <artifact name="filter-ccimpl" type="jar" conf="cc-impl" ext="jar"/>
+ </publications>
+ <dependencies>
+ <dependency org="commons-collections" name="commons-collections" rev="3.1" conf="cc-impl->default"/>
+ <dependency org="junit" name="junit" rev="3.8" conf="test->default"/>
+ </dependencies>
+</ivy-module>
diff --git a/src/example/configurations/multi-projects/filter-framework/src/filter/FilterProvider.java b/src/example/configurations/multi-projects/filter-framework/src/filter/FilterProvider.java
new file mode 100644
index 0000000..2ee151a
--- /dev/null
+++ b/src/example/configurations/multi-projects/filter-framework/src/filter/FilterProvider.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package filter;
+
+
+public final class FilterProvider {
+
+ public static IFilter getFilter() {
+ try {
+ Class clazz = Class.forName("filter.ccimpl.CCFilter");
+ return (IFilter) clazz.newInstance();
+ } catch (Exception e) {
+ try {
+ Class clazz = Class.forName("filter.hmimpl.HMFilter");
+ return (IFilter) clazz.newInstance();
+ } catch (Exception e1) {
+ System.err.println("No filter implementation found in classpath !");
+ }
+ return null;
+ }
+ }
+
+ private FilterProvider() {
+ }
+}
diff --git a/src/example/configurations/multi-projects/filter-framework/src/filter/IFilter.java b/src/example/configurations/multi-projects/filter-framework/src/filter/IFilter.java
new file mode 100644
index 0000000..7c5590e
--- /dev/null
+++ b/src/example/configurations/multi-projects/filter-framework/src/filter/IFilter.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package filter;
+
+public interface IFilter {
+ String[] filter(String[] values, String prefix);
+}
diff --git a/src/example/configurations/multi-projects/filter-framework/src/filter/ccimpl/CCFilter.java b/src/example/configurations/multi-projects/filter-framework/src/filter/ccimpl/CCFilter.java
new file mode 100644
index 0000000..3808d20
--- /dev/null
+++ b/src/example/configurations/multi-projects/filter-framework/src/filter/ccimpl/CCFilter.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package filter.ccimpl;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.collections.Predicate;
+import filter.IFilter;
+
+public class CCFilter implements IFilter {
+
+ public String[] filter(String[] values, final String prefix) {
+ if (values == null) {
+ return null;
+ }
+ if (prefix == null) {
+ return values;
+ }
+
+ List result = new ArrayList(Arrays.asList(values));
+ CollectionUtils.filter(result, new Predicate() {
+ public boolean evaluate(Object o) {
+ return o != null && o.toString().startsWith(prefix);
+ }
+ });
+ return (String[]) result.toArray(new String[result.size()]);
+ }
+}
diff --git a/src/example/configurations/multi-projects/filter-framework/src/filter/hmimpl/HMFilter.java b/src/example/configurations/multi-projects/filter-framework/src/filter/hmimpl/HMFilter.java
new file mode 100644
index 0000000..d500809
--- /dev/null
+++ b/src/example/configurations/multi-projects/filter-framework/src/filter/hmimpl/HMFilter.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package filter.hmimpl;
+
+import java.util.ArrayList;
+import java.util.List;
+import filter.IFilter;
+
+public class HMFilter implements IFilter {
+
+ public String[] filter(String[] values, String prefix) {
+ if (values == null) {
+ return null;
+ }
+ if (prefix == null) {
+ return values;
+ }
+ List result = new ArrayList();
+ for (int i = 0; i < values.length; i++) {
+ String string = values[i];
+ if (string != null && string.startsWith(prefix)) {
+ result.add(string);
+ }
+ }
+ return (String[]) result.toArray(new String[result.size()]);
+ }
+}
diff --git a/src/example/configurations/multi-projects/myapp/build.xml b/src/example/configurations/multi-projects/myapp/build.xml
new file mode 100644
index 0000000..460cd43
--- /dev/null
+++ b/src/example/configurations/multi-projects/myapp/build.xml
@@ -0,0 +1,93 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project name="myapp" default="run-cc" xmlns:ivy="antlib:org.apache.ivy.ant">
+ <!-- some variables used -->
+ <property name="lib.dir" value="lib" />
+ <property name="build.dir" value="build" />
+ <property name="src.dir" value="src" />
+
+ <!-- paths used for compilation and run -->
+ <path id="lib.path.id">
+ <fileset dir="${lib.dir}/build" />
+ </path>
+ <path id="run.hm.path.id">
+ <path location="${build.dir}" />
+ <fileset dir="${lib.dir}/noexternaljar" />
+ </path>
+ <path id="run.cc.path.id">
+ <path location="${build.dir}" />
+ <fileset dir="${lib.dir}/withexternaljar" />
+ </path>
+ <!-- =================================
+ target: resolve
+ ================================= -->
+ <target name="resolve" description="--> retreive dependencies with ivy">
+ <ivy:retrieve pattern="${ivy.lib.dir}/[conf]/[artifact].[ext]"/>
+ </target>
+
+ <!-- =================================
+ target: report
+ ================================= -->
+ <target name="report" depends="resolve" description="--> generates a report of dependencies">
+ <ivy:report todir="${build.dir}"/>
+ </target>
+
+ <!-- =================================
+ target: build
+ ================================= -->
+ <target name="build" depends="resolve" description="--> compile the project">
+ <mkdir dir="${build.dir}" />
+ <javac srcdir="${src.dir}" destdir="${build.dir}" classpathref="lib.path.id" includeAntRuntime="false"/>
+ </target>
+
+ <!-- =================================
+ target: run with home made implementation
+ ================================= -->
+ <target name="run-hm" depends="build" description="--> run the project with ome made implementation">
+ <java classpathref="run.hm.path.id" classname="myapp.Main" fork="true"/>
+ </target>
+
+ <!-- =================================
+ target: run with ext lib implementation
+ ================================= -->
+ <target name="run-cc" depends="build" description="--> run the project with ext lib implementation">
+ <java classpathref="run.cc.path.id" classname="myapp.Main" fork="true"/>
+ </target>
+
+ <!-- =================================
+ target: clean
+ ================================= -->
+ <target name="clean" description="--> clean the project">
+ <delete includeemptydirs="true">
+ <fileset dir="${basedir}">
+ <exclude name="src/**" />
+ <exclude name="build.xml" />
+ <exclude name="ivy.xml" />
+ <exclude name=".*" />
+ </fileset>
+ </delete>
+ </target>
+
+ <!-- =================================
+ target: clean-cache
+ ================================= -->
+ <target name="clean-cache" description="--> clean the ivy cache">
+ <ivy:cleancache />
+ </target>
+</project>
diff --git a/src/example/configurations/multi-projects/myapp/ivy.xml b/src/example/configurations/multi-projects/myapp/ivy.xml
new file mode 100644
index 0000000..5890d33
--- /dev/null
+++ b/src/example/configurations/multi-projects/myapp/ivy.xml
@@ -0,0 +1,31 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivy-module version="1.0">
+ <info organisation="org.apache" module="myapp"/>
+
+ <configurations>
+ <conf name="build" visibility="private" description="compilation only need api jar" />
+ <conf name="noexternaljar" description="use only company jar" />
+ <conf name="withexternaljar" description="use company jar and third party jars" />
+ </configurations>
+
+ <dependencies>
+ <dependency org="org.apache" name="filter-framework" rev="latest.integration" conf="build->api; noexternaljar->homemade-impl; withexternaljar->cc-impl"/>
+ </dependencies>
+</ivy-module>
diff --git a/src/example/configurations/multi-projects/myapp/src/myapp/Main.java b/src/example/configurations/multi-projects/myapp/src/myapp/Main.java
new file mode 100644
index 0000000..e5e2729
--- /dev/null
+++ b/src/example/configurations/multi-projects/myapp/src/myapp/Main.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package myapp;
+
+import java.util.Arrays;
+import filter.FilterProvider;
+import filter.IFilter;
+
+public final class Main {
+
+ public static void main(String[] args) {
+ String[] toFilter = new String[] {"one", "two", "tree", "four"};
+ IFilter filter = FilterProvider.getFilter();
+ System.out.println("Filtering with:" + filter.getClass());
+ String[] filtered = filter.filter(toFilter, "t");
+ System.out.println("Result :" + Arrays.asList(filtered));
+ }
+
+ private Main() {
+ }
+}
diff --git a/src/example/dependence/build.xml b/src/example/dependence/build.xml
new file mode 100644
index 0000000..5c94a53
--- /dev/null
+++ b/src/example/dependence/build.xml
@@ -0,0 +1,42 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project default="clean">
+
+ <!-- =================================
+ target: clean
+ ================================= -->
+ <target name="clean" description="--> clean directories">
+ <delete includeemptydirs="true">
+ <fileset dir="settings" excludes="ivysettings.*" />
+ </delete>
+ <ant dir="dependee" antfile="build.xml" target="clean" inheritall="false" inheritrefs="false" />
+ <ant dir="depender" antfile="build.xml" target="clean" inheritall="false" inheritrefs="false" />
+ </target>
+
+ <!-- =================================
+ target: all
+ ================================= -->
+ <target name="all" depends="clean" description="--> make the whole example of dependency">
+ <ant dir="dependee" antfile="build.xml" target="publish" inheritall="false" inheritrefs="false" />
+ <ant dir="depender" antfile="build.xml" inheritall="false" inheritrefs="false" />
+ <ant dir="dependee" antfile="build.xml" target="publish" inheritall="false" inheritrefs="false" />
+ <ant dir="depender" antfile="build.xml" inheritall="false" inheritrefs="false" />
+ </target>
+
+</project>
diff --git a/src/example/dependence/dependee/build.xml b/src/example/dependence/dependee/build.xml
new file mode 100644
index 0000000..d45c22d
--- /dev/null
+++ b/src/example/dependence/dependee/build.xml
@@ -0,0 +1,110 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project name="dependee" default="run" xmlns:ivy="antlib:org.apache.ivy.ant">
+ <!-- some variables used -->
+ <property name="lib.dir" value="${basedir}/lib" />
+ <property name="build.dir" value="${basedir}/build" />
+ <property name="classes.dir" value="${build.dir}/classes" />
+ <property name="src.dir" value="${basedir}/src" />
+
+ <!-- ivy properties used -->
+ <property name="ivy.settings.dir" value="../settings" />
+ <property file="${ivy.settings.dir}/ivysettings.properties" />
+
+ <!-- paths used for compilation and run -->
+ <path id="lib.path.id">
+ <fileset dir="${lib.dir}" />
+ </path>
+ <path id="run.path.id">
+ <path refid="lib.path.id" />
+ <path location="${classes.dir}" />
+ </path>
+
+ <ivy:settings file="${ivy.settings.dir}/ivysettings.xml" />
+
+ <!-- =================================
+ target: resolve
+ ================================= -->
+ <target name="resolve" description="--> resolve and retrieve dependencies with ivy">
+ <ivy:retrieve />
+ </target>
+
+ <!-- =================================
+ target: report
+ ================================= -->
+ <target name="report" depends="resolve" description="--> generates a report of dependencies">
+ <ivy:report todir="${build.dir}"/>
+ </target>
+
+ <!-- =================================
+ target: compile
+ ================================= -->
+ <target name="compile" depends="resolve" description="--> description">
+ <mkdir dir="${classes.dir}" />
+ <javac srcdir="${src.dir}" destdir="${classes.dir}" classpathref="lib.path.id" includeAntRuntime="false"/>
+ </target>
+
+ <!-- =================================
+ target: run
+ ================================= -->
+ <target name="run" depends="compile" description="--> compile and run the project">
+ <java classpathref="run.path.id" classname="standalone.Main"/>
+ </target>
+
+ <!-- =================================
+ target: jar
+ ================================= -->
+ <target name="jar" depends="compile" description="--> make a jar file for this project">
+ <propertyfile file="${classes.dir}/version.properties">
+ <entry key="version" type="int" operation="+" default="0" />
+ </propertyfile>
+ <property file="${classes.dir}/version.properties" />
+ <jar destfile="${build.dir}/${ant.project.name}.jar">
+ <fileset dir="${classes.dir}" />
+ </jar>
+ </target>
+
+ <!-- =================================
+ target: publish
+ ================================= -->
+ <target name="publish" depends="jar" description="--> publish this project in the ivy repository">
+ <property name="revision" value="${version}"/>
+ <delete file="${build.dir}/ivy.xml"/>
+ <ivy:publish artifactspattern="${build.dir}/[artifact].[ext]"
+ resolver="projects"
+ pubrevision="${revision}"
+ status="release"
+ />
+ <echo message="project ${ant.project.name} released with version ${revision}" />
+ </target>
+
+
+ <!-- =================================
+ target: clean
+ ================================= -->
+ <target name="clean" description="--> clean the project">
+ <delete includeemptydirs="true">
+ <fileset dir="${basedir}">
+ <exclude name="src/**" />
+ <exclude name="build.xml" />
+ <exclude name="ivy.xml" />
+ </fileset>
+ </delete>
+ </target>
+</project>
diff --git a/src/example/dependence/dependee/ivy.xml b/src/example/dependence/dependee/ivy.xml
new file mode 100644
index 0000000..c4196ff
--- /dev/null
+++ b/src/example/dependence/dependee/ivy.xml
@@ -0,0 +1,24 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivy-module version="1.0">
+ <info organisation="org.apache" module="dependee"/>
+ <dependencies>
+ <dependency org="commons-lang" name="commons-lang" rev="2.0"/>
+ </dependencies>
+</ivy-module>
diff --git a/src/example/dependence/dependee/src/standalone/Main.java b/src/example/dependence/dependee/src/standalone/Main.java
new file mode 100644
index 0000000..ce28cb8
--- /dev/null
+++ b/src/example/dependence/dependee/src/standalone/Main.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package standalone;
+
+import java.util.Properties;
+
+import org.apache.commons.lang.WordUtils;
+
+/**
+ * TODO write javadoc
+ */
+public final class Main {
+ /**
+ * Returns the version of the project
+ * @return a string representation of the version, null if the version could not be retreived
+ */
+ public static String getVersion() {
+ Properties p = new Properties();
+ try {
+ p.load(Main.class.getResourceAsStream("/version.properties"));
+ String version = p.getProperty("version");
+ if (version != null) {
+ return String.valueOf(Integer.parseInt(version));
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ /**
+ * Return the same string with all words capitalized.
+ * @param str the string conatining the words to capitalize
+ * @return null if the string was null, the string with all words capitalized otherwise
+ */
+ public static String capitalizeWords(String str) {
+ System.out.println(" [" + Main.class.getName() + "] capitalizing string \""
+ + str + "\" using " + WordUtils.class.getName());
+ return WordUtils.capitalizeFully(str);
+ }
+ public static void main(String[] args) {
+ String message = "sentence to capitalize";
+ System.out.println("standard message : " + message);
+ System.out.println("capitalized message : " + capitalizeWords(message));
+ }
+
+ private Main() {
+ }
+}
diff --git a/src/example/dependence/depender/build.xml b/src/example/dependence/depender/build.xml
new file mode 100644
index 0000000..2cf9249
--- /dev/null
+++ b/src/example/dependence/depender/build.xml
@@ -0,0 +1,93 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project name="depender" default="run" xmlns:ivy="antlib:org.apache.ivy.ant">
+ <!-- some variables used -->
+ <property name="lib.dir" value="${basedir}/lib" />
+ <property name="build.dir" value="${basedir}/build" />
+ <property name="classes.dir" value="${build.dir}/classes" />
+ <property name="src.dir" value="${basedir}/src" />
+
+ <!-- ivy properties used -->
+ <property name="ivy.settings.dir" value="../settings" />
+ <property file="${ivy.settings.dir}/ivysettings.properties" />
+
+ <!-- paths used for compilation and run -->
+ <path id="lib.path.id">
+ <fileset dir="${lib.dir}" />
+ </path>
+ <path id="run.path.id">
+ <path refid="lib.path.id" />
+ <path location="${classes.dir}" />
+ </path>
+
+ <ivy:settings file="${ivy.settings.dir}/ivysettings.xml" />
+
+ <!-- =================================
+ target: resolve
+ ================================= -->
+ <target name="resolve" description="--> resolve and retrieve dependencies with ivy">
+ <ivy:retrieve />
+ </target>
+
+ <!-- =================================
+ target: report
+ ================================= -->
+ <target name="report" depends="resolve" description="--> generates a report of dependencies">
+ <ivy:report todir="${build.dir}" dot="true"/>
+ </target>
+
+ <!-- =================================
+ target: gen-graph
+ ================================= -->
+ <target name="gen-graph" depends="report" description="--> generates a graph of dependencies (requires dot in your path - see http://www.graphviz.org/)">
+ <property name="dot.file" value="${build.dir}/apache-depending-default.dot" />
+ <property name="ivygraph.output.file" value="${build.dir}/graph.png" />
+ <exec executable="dot">
+ <arg line="-T png -o ${ivygraph.output.file} ${dot.file}" />
+ </exec>
+ </target>
+
+ <!-- =================================
+ target: compile
+ ================================= -->
+ <target name="compile" depends="resolve" description="--> description">
+ <mkdir dir="${classes.dir}" />
+ <javac srcdir="${src.dir}" destdir="${classes.dir}" classpathref="lib.path.id" includeAntRuntime="false"/>
+ </target>
+
+ <!-- =================================
+ target: run
+ ================================= -->
+ <target name="run" depends="clean, compile" description="--> compile and run the project">
+ <java classpathref="run.path.id" classname="depending.Main"/>
+ </target>
+
+ <!-- =================================
+ target: clean
+ ================================= -->
+ <target name="clean" description="--> clean the project">
+ <delete includeemptydirs="true">
+ <fileset dir="${basedir}">
+ <exclude name="src/**" />
+ <exclude name="build.xml" />
+ <exclude name="ivy.xml" />
+ </fileset>
+ </delete>
+ </target>
+</project>
diff --git a/src/example/dependence/depender/ivy.xml b/src/example/dependence/depender/ivy.xml
new file mode 100644
index 0000000..1f77ce6
--- /dev/null
+++ b/src/example/dependence/depender/ivy.xml
@@ -0,0 +1,24 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivy-module version="1.0">
+ <info organisation="org.apache" module="depender"/>
+ <dependencies>
+ <dependency name="dependee" rev="latest.integration" />
+ </dependencies>
+</ivy-module>
diff --git a/src/example/dependence/depender/src/depending/Main.java b/src/example/dependence/depender/src/depending/Main.java
new file mode 100644
index 0000000..2fa27da
--- /dev/null
+++ b/src/example/dependence/depender/src/depending/Main.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package depending;
+
+/**
+ * TODO write javadoc
+ */
+public final class Main {
+ public static void main(String[] args) {
+ String standaloneVersion = standalone.Main.getVersion();
+ if (standaloneVersion != null) {
+ System.out.println("you are using version " + standaloneVersion
+ + " of class " + standalone.Main.class.getName());
+ } else {
+ System.err.println("failed to get version of " + standalone.Main.class.getName());
+ }
+ String message = "i am " + Main.class.getName()
+ + " and " + standalone.Main.class.getName() + " will do the job for me";
+ System.out.println("standard message : " + message);
+ System.out.println("capitalized message : " + standalone.Main.capitalizeWords(message));
+ }
+
+ private Main() {
+ }
+}
diff --git a/src/example/dependence/settings/ivysettings.properties b/src/example/dependence/settings/ivysettings.properties
new file mode 100644
index 0000000..f68f415
--- /dev/null
+++ b/src/example/dependence/settings/ivysettings.properties
@@ -0,0 +1,19 @@
+# ***************************************************************
+# * Licensed to the Apache Software Foundation (ASF) under one
+# * or more contributor license agreements. See the NOTICE file
+# * distributed with this work for additional information
+# * regarding copyright ownership. The ASF licenses this file
+# * to you under the Apache License, Version 2.0 (the
+# * "License"); you may not use this file except in compliance
+# * with the License. You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing,
+# * software distributed under the License is distributed on an
+# * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# * KIND, either express or implied. See the License for the
+# * specific language governing permissions and limitations
+# * under the License.
+# ***************************************************************
+repository.dir=${ivy.settings.dir}/repository
diff --git a/src/example/dependence/settings/ivysettings.xml b/src/example/dependence/settings/ivysettings.xml
new file mode 100644
index 0000000..e06d437
--- /dev/null
+++ b/src/example/dependence/settings/ivysettings.xml
@@ -0,0 +1,33 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivysettings>
+ <properties file="${ivy.settings.dir}/ivysettings.properties"/>
+ <settings defaultResolver="libraries" />
+ <caches defaultCacheDir="${ivy.settings.dir}/ivy-cache" />
+ <resolvers>
+ <filesystem name="projects">
+ <artifact pattern="${repository.dir}/[artifact]-[revision].[ext]" />
+ <ivy pattern="${repository.dir}/[module]-[revision].xml" />
+ </filesystem>
+ <ibiblio name="libraries" m2compatible="true" usepoms="false" />
+ </resolvers>
+ <modules>
+ <module organisation="org.apache" name="dependee" resolver="projects"/>
+ </modules>
+</ivysettings>
diff --git a/src/example/dual/build.xml b/src/example/dual/build.xml
new file mode 100644
index 0000000..56f281f
--- /dev/null
+++ b/src/example/dual/build.xml
@@ -0,0 +1,38 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project default="clean-all" xmlns:ivy="antlib:org.apache.ivy.ant">
+
+ <!-- =================================
+ target: clean
+ ================================= -->
+ <target name="clean" description="--> clean directories">
+ <ant dir="project" antfile="build.xml" target="clean" inheritall="false" inheritrefs="false" />
+ </target>
+
+ <!-- =================================
+ target: clean-cache
+ ================================= -->
+ <target name="clean-cache" description="--> clean the ivy cache">
+ <property name="ivy.settings.dir" value="settings" />
+ <ivy:settings file="${ivy.settings.dir}/ivysettings.xml" />
+ <ivy:cleancache />
+ </target>
+
+ <target name="clean-all" depends="clean, clean-cache" description="--> clean directories and ivy cache"/>
+</project>
diff --git a/src/example/dual/project/build.xml b/src/example/dual/project/build.xml
new file mode 100644
index 0000000..bf550f3
--- /dev/null
+++ b/src/example/dual/project/build.xml
@@ -0,0 +1,81 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project name="dual-ivy" default="run" xmlns:ivy="antlib:org.apache.ivy.ant">
+ <!-- some variables used -->
+ <property name="lib.dir" value="${basedir}/lib" />
+ <property name="build.dir" value="${basedir}/build" />
+ <property name="src.dir" value="${basedir}/src" />
+
+ <!-- ivy properties used -->
+ <property name="ivy.settings.dir" value="../settings" />
+
+ <!-- paths used for compilation and run -->
+ <path id="lib.path.id">
+ <fileset dir="${lib.dir}" />
+ </path>
+ <path id="run.path.id">
+ <path refid="lib.path.id" />
+ <path location="${build.dir}" />
+ </path>
+
+ <ivy:settings file="${ivy.settings.dir}/ivysettings.xml" />
+
+ <!-- =================================
+ target: resolve
+ ================================= -->
+ <target name="resolve" description="--> resolve and retrieve dependencies with ivy">
+ <ivy:retrieve />
+ </target>
+
+ <!-- =================================
+ target: report
+ ================================= -->
+ <target name="report" depends="resolve" description="--> generates a report of dependencies">
+ <ivy:report todir="${build.dir}"/>
+ </target>
+
+ <!-- =================================
+ target: run
+ ================================= -->
+ <target name="run" depends="resolve" description="--> compile and run the project">
+ <mkdir dir="${build.dir}" />
+ <javac srcdir="${src.dir}" destdir="${build.dir}" classpathref="lib.path.id" includeAntRuntime="false"/>
+ <java classpathref="run.path.id" classname="example.Hello"/>
+ </target>
+
+ <!-- =================================
+ target: clean
+ ================================= -->
+ <target name="clean" description="--> clean the project">
+ <delete includeemptydirs="true">
+ <fileset dir="${basedir}">
+ <exclude name="src/**" />
+ <exclude name="build.xml" />
+ <exclude name="ivy.xml" />
+ </fileset>
+ </delete>
+ </target>
+
+ <!-- =================================
+ target: clean-cache
+ ================================= -->
+ <target name="clean-cache" description="--> clean the ivy cache">
+ <ivy:cleancache />
+ </target>
+</project>
diff --git a/src/example/dual/project/ivy.xml b/src/example/dual/project/ivy.xml
new file mode 100644
index 0000000..02d8bab
--- /dev/null
+++ b/src/example/dual/project/ivy.xml
@@ -0,0 +1,25 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivy-module version="1.0">
+ <info organisation="org.apache" module="hello-ivy"/>
+ <dependencies>
+ <dependency org="commons-httpclient" name="commons-httpclient" rev="2.0.2"/>
+ <dependency org="commons-lang" name="commons-lang" rev="2.0"/>
+ </dependencies>
+</ivy-module>
diff --git a/src/example/dual/project/src/example/Hello.java b/src/example/dual/project/src/example/Hello.java
new file mode 100644
index 0000000..30e6bf9
--- /dev/null
+++ b/src/example/dual/project/src/example/Hello.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package example;
+
+import org.apache.commons.httpclient.HttpClient;
+import org.apache.commons.httpclient.methods.HeadMethod;
+import org.apache.commons.lang.WordUtils;
+
+/**
+ * Simple hello world example to show how easy it is to retrieve libs with ivy,
+ * including transitive dependencies
+ */
+public final class Hello {
+ public static void main(String[] args) throws Exception {
+ String message = "hello ivy !";
+ System.out.println("standard message : " + message);
+ System.out.println("capitalized by " + WordUtils.class.getName()
+ + " : " + WordUtils.capitalizeFully(message));
+
+ HttpClient client = new HttpClient();
+ HeadMethod head = new HeadMethod("http://www.ibiblio.org/");
+ client.executeMethod(head);
+
+ int status = head.getStatusCode();
+ System.out.println("head status code with httpclient: " + status);
+ head.releaseConnection();
+
+ System.out.println(
+ "now check if httpclient dependency on commons-logging has been realized");
+ Class clss = Class.forName("org.apache.commons.logging.Log");
+ System.out.println("found logging class in classpath: " + clss);
+ }
+
+ private Hello() {
+ }
+}
diff --git a/src/example/dual/repository/commons-httpclient-ivy-2.0.2.xml b/src/example/dual/repository/commons-httpclient-ivy-2.0.2.xml
new file mode 100644
index 0000000..0b2f32c
--- /dev/null
+++ b/src/example/dual/repository/commons-httpclient-ivy-2.0.2.xml
@@ -0,0 +1,29 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivy-module version="1.0">
+ <info
+ organisation="commons-httpclient"
+ module="commons-httpclient"
+ revision="2.0.2"
+ status="release"
+ publication="20041010174300"/>
+ <dependencies>
+ <dependency org="commons-logging" name="commons-logging" rev="1.0.4" conf="default"/>
+ </dependencies>
+</ivy-module>
diff --git a/src/example/dual/settings/ivysettings.xml b/src/example/dual/settings/ivysettings.xml
new file mode 100644
index 0000000..9d48a8a
--- /dev/null
+++ b/src/example/dual/settings/ivysettings.xml
@@ -0,0 +1,29 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivysettings>
+ <settings defaultResolver="dual-example"/>
+ <resolvers>
+ <dual name="dual-example">
+ <filesystem name="ivys">
+ <ivy pattern="${ivy.settings.dir}/../repository/[module]-ivy-[revision].xml" />
+ </filesystem>
+ <ibiblio name="ibiblio" m2compatible="true" usepoms="false" />
+ </dual>
+ </resolvers>
+</ivysettings>
diff --git a/src/example/go-ivy/build.xml b/src/example/go-ivy/build.xml
new file mode 100644
index 0000000..d92b11f
--- /dev/null
+++ b/src/example/go-ivy/build.xml
@@ -0,0 +1,151 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project name="go-ivy" default="go" xmlns:ivy="antlib:org.apache.ivy.ant">
+ <!--
+ this build file is a self contained project: it doesn't require anything else
+ that ant 1.6.2 or greater and java 1.4 or greater properly installed.
+
+ It is used to showcase how easy and straightforward it can be to use Ivy.
+
+ This is not an example of the best pratice to use in a project, especially
+ for the java source code "generation" :-) (see generate-src target)
+
+ To run copy this file in an empty directory, open a shell or a command window
+ in this directory and run "ant". It will download ivy and then use it to resolve
+ the dependency of the class which is itself "contained" in this build script.
+
+ After a successful build run "ant" again and you will see the build will be
+ much faster.
+
+ More information can be found at http://ant.apache.org/ivy/
+ -->
+
+ <!-- here is the version of ivy we will use. change this property to try a newer
+ version if you want -->
+ <property name="ivy.install.version" value="2.0.0-beta1" />
+ <property name="ivy.jar.dir" value="${basedir}/ivy" />
+ <property name="ivy.jar.file" value="${ivy.jar.dir}/ivy.jar" />
+
+ <property name="build.dir" value="build" />
+ <property name="src.dir" value="src" />
+
+
+ <target name="download-ivy" unless="skip.download">
+ <mkdir dir="${ivy.jar.dir}"/>
+ <!-- download Ivy from web site so that it can be used even without any special installation -->
+ <echo message="installing ivy..."/>
+ <get src="https://repo1.maven.org/maven2/org/apache/ivy/ivy/${ivy.install.version}/ivy-${ivy.install.version}.jar"
+ dest="${ivy.jar.file}" usetimestamp="true"/>
+ </target>
+
+ <!-- =================================
+ target: install-ivy
+ this target is not necessary if you put ivy.jar in your ant lib directory
+ if you already have ivy in your ant lib, you can simply remove this
+ target and the dependency the 'go' target has on it
+ ================================= -->
+ <target name="install-ivy" depends="download-ivy" description="--> install ivy">
+ <!-- try to load ivy here from local ivy dir, in case the user has not already dropped
+ it into ant's lib dir (note that the latter copy will always take precedence).
+ We will not fail as long as the ivy jar is in at least one of ant's lib dir or
+ the local lib dir. -->
+ <path id="ivy.lib.path">
+ <pathelement location="${ivy.jar.file}"/>
+ </path>
+ <taskdef resource="org/apache/ivy/ant/antlib.xml"
+ uri="antlib:org.apache.ivy.ant" classpathref="ivy.lib.path"/>
+ </target>
+
+ <!-- =================================
+ target: go
+ Go ivy, go!
+ ================================= -->
+ <target name="go" depends="install-ivy, generate-src"
+ description="--> resolve dependencies, compile and run the project">
+ <echo message="using ivy to resolve commons-lang 2.1..."/>
+ <!-- here comes the magic line: asks ivy to resolve a dependency on
+ commons-lang 2.1 and to build an ant path with it from its cache -->
+ <ivy:cachepath organisation="commons-lang" module="commons-lang" revision="2.1"
+ pathid="lib.path.id" inline="true"/>
+
+ <echo message="compiling..."/>
+ <mkdir dir="${build.dir}" />
+ <javac srcdir="${src.dir}" destdir="${build.dir}" classpathref="lib.path.id" includeAntRuntime="false"/>
+
+ <echo>
+We are now ready to execute our simple program with its dependency on commons-lang.
+Let's go!
+ </echo>
+ <java classname="example.Hello">
+ <classpath>
+ <path refid="lib.path.id" />
+ <path location="${build.dir}" />
+ </classpath>
+ </java>
+ </target>
+
+ <!-- =================================
+ target: generate-src
+ 'Generates' the class source. It actually just echo a simple java
+ source code to a file. In real life this file would already be
+ present on your file system, and this target wouldn't be necessary.
+ ================================= -->
+ <target name="generate-src">
+ <mkdir dir="${src.dir}/example" />
+ <echo file="${src.dir}/example/Hello.java">
+package example;
+
+import org.apache.commons.lang.WordUtils;
+
+public class Hello {
+ public static void main(String[] args) {
+ String message = "hello ivy !";
+ System.out.println("standard message : " + message);
+ System.out.println("capitalized by " + WordUtils.class.getName()
+ + " : " + WordUtils.capitalizeFully(message));
+ }
+}
+ </echo>
+ </target>
+
+ <!-- =================================
+ target: clean
+ ================================= -->
+ <target name="clean" description="--> clean the project">
+ <delete includeemptydirs="true" quiet="true">
+ <fileset dir="${src.dir}" />
+ <fileset dir="${build.dir}" />
+ </delete>
+ </target>
+
+ <!-- =================================
+ target: clean-ivy
+ ================================= -->
+ <target name="clean-ivy" description="--> clean the ivy installation">
+ <delete dir="${ivy.jar.dir}"/>
+ </target>
+
+ <!-- =================================
+ target: clean-cache
+ ================================= -->
+ <target name="clean-cache" depends="install-ivy"
+ description="--> clean the ivy cache">
+ <ivy:cleancache />
+ </target>
+</project>
diff --git a/src/example/hello-ivy/build.xml b/src/example/hello-ivy/build.xml
new file mode 100644
index 0000000..1e94ea4
--- /dev/null
+++ b/src/example/hello-ivy/build.xml
@@ -0,0 +1,80 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project name="hello-ivy" default="run" xmlns:ivy="antlib:org.apache.ivy.ant">
+ <!-- some variables used -->
+ <property name="lib.dir" value="lib" />
+ <property name="build.dir" value="build" />
+ <property name="src.dir" value="src" />
+
+ <!-- paths used for compilation and run -->
+ <path id="lib.path.id">
+ <fileset dir="${lib.dir}" />
+ </path>
+ <path id="run.path.id">
+ <path refid="lib.path.id" />
+ <path location="${build.dir}" />
+ </path>
+
+ <!-- =================================
+ target: resolve
+ ================================= -->
+ <target name="resolve" description="--> retreive dependencies with ivy">
+ <ivy:retrieve/>
+ </target>
+
+ <!-- =================================
+ target: report
+ ================================= -->
+ <target name="report" depends="resolve" description="--> generates a report of dependencies">
+ <ivy:report todir="${build.dir}"/>
+ </target>
+
+ <!-- =================================
+ target: run
+ ================================= -->
+ <target name="run" depends="resolve" description="--> compile and run the project">
+ <mkdir dir="${build.dir}" />
+ <javac srcdir="${src.dir}" destdir="${build.dir}" classpathref="lib.path.id" includeAntRuntime="false"/>
+ <property name="msg" value="hello ivy !"/>
+ <java classpathref="run.path.id" classname="example.Hello">
+ <arg value="-message"/>
+ <arg value="${msg}"/>
+ </java>
+ </target>
+
+ <!-- =================================
+ target: clean
+ ================================= -->
+ <target name="clean" description="--> clean the project">
+ <delete includeemptydirs="true">
+ <fileset dir="${basedir}">
+ <exclude name="src/**" />
+ <exclude name="build.xml" />
+ <exclude name="ivy.xml" />
+ </fileset>
+ </delete>
+ </target>
+
+ <!-- =================================
+ target: clean-cache
+ ================================= -->
+ <target name="clean-cache" description="--> clean the ivy cache">
+ <ivy:cleancache />
+ </target>
+</project>
diff --git a/src/example/hello-ivy/ivy.xml b/src/example/hello-ivy/ivy.xml
new file mode 100644
index 0000000..da3870e
--- /dev/null
+++ b/src/example/hello-ivy/ivy.xml
@@ -0,0 +1,25 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivy-module version="2.0">
+ <info organisation="org.apache" module="hello-ivy"/>
+ <dependencies>
+ <dependency org="commons-lang" name="commons-lang" rev="2.0"/>
+ <dependency org="commons-cli" name="commons-cli" rev="1.0"/>
+ </dependencies>
+</ivy-module>
diff --git a/src/example/hello-ivy/src/example/Hello.java b/src/example/hello-ivy/src/example/Hello.java
new file mode 100644
index 0000000..55bff13
--- /dev/null
+++ b/src/example/hello-ivy/src/example/Hello.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package example;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.GnuParser;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.OptionBuilder;
+import org.apache.commons.cli.Options;
+import org.apache.commons.lang.WordUtils;
+
+/**
+ * Simple example to show how easy it is to retrieve transitive libs with ivy !!!
+ */
+public final class Hello {
+ public static void main(String[] args) throws Exception {
+ Option msg = OptionBuilder.withArgName("msg")
+ .hasArg()
+ .withDescription("the message to capitalize")
+ .create("message");
+ Options options = new Options();
+ options.addOption(msg);
+
+ CommandLineParser parser = new GnuParser();
+ CommandLine line = parser.parse(options, args);
+
+ String message = line.getOptionValue("message", "hello ivy !");
+ System.out.println("standard message : " + message);
+ System.out.println("capitalized by " + WordUtils.class.getName()
+ + " : " + WordUtils.capitalizeFully(message));
+ }
+
+ private Hello() {
+ }
+}
diff --git a/src/example/multi-project/build.xml b/src/example/multi-project/build.xml
new file mode 100644
index 0000000..200a439
--- /dev/null
+++ b/src/example/multi-project/build.xml
@@ -0,0 +1,69 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project name="all"
+ xmlns:ivy="antlib:org.apache.ivy.ant">
+
+ <property name="ivy.jar.dir" value="${user.home}/.ivy2/jars" />
+ <property name="ivy.jar.file" value="${ivy.jar.dir}/ivy.jar" />
+
+ <property name="build.dir" value="build" />
+ <property name="src.dir" value="src" />
+
+
+ <!-- =================================
+ target: load-ivy
+ this target is not necessary if you put ivy.jar in your ant lib directory
+ if you already have ivy 1.4 in your ant lib, you can simply remove this
+ target
+ ================================= -->
+ <target name="load-ivy">
+ <!-- try to load ivy here from home ivy dir, in case the user has not already dropped
+ it into ant's lib dir (note that the latter copy will always take precedence).
+ We will not fail as long as ivy home lib dir exists (it may be empty) and
+ ivy is in at least one of ant's lib dir or the ivy home lib dir. -->
+ <path id="ivy.lib.path">
+ <pathelement location="${ivy.jar.file}"/>
+ </path>
+ <taskdef resource="org/apache/ivy/ant/antlib.xml"
+ uri="antlib:org.apache.ivy.ant" classpathref="ivy.lib.path"/>
+ </target>
+
+ <target name="buildlist" depends="load-ivy">
+ <ivy:buildlist reference="build-path">
+ <fileset dir="projects" includes="**/build.xml"/>
+ </ivy:buildlist>
+ </target>
+
+ <target name="publish-all" depends="buildlist"
+ description="compile, jar and publish all projects in the right order">
+ <subant target="publish" buildpathref="build-path" />
+ </target>
+
+ <target name="clean-all" depends="buildlist" description="clean all projects">
+ <subant target="clean" buildpathref="build-path" />
+ </target>
+
+ <target name="clean" depends="clean-all, load-ivy"
+ description="clean tutorial: delete repository, ivy cache, and all projects">
+ <delete dir="repository"/>
+ <ivy:cleancache />
+ </target>
+
+
+</project>
diff --git a/src/example/multi-project/common/build.properties b/src/example/multi-project/common/build.properties
new file mode 100644
index 0000000..9552b4a
--- /dev/null
+++ b/src/example/multi-project/common/build.properties
@@ -0,0 +1,30 @@
+# ***************************************************************
+# * Licensed to the Apache Software Foundation (ASF) under one
+# * or more contributor license agreements. See the NOTICE file
+# * distributed with this work for additional information
+# * regarding copyright ownership. The ASF licenses this file
+# * to you under the Apache License, Version 2.0 (the
+# * "License"); you may not use this file except in compliance
+# * with the License. You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing,
+# * software distributed under the License is distributed on an
+# * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# * KIND, either express or implied. See the License for the
+# * specific language governing permissions and limitations
+# * under the License.
+# ***************************************************************
+lib.dir = ${basedir}/lib
+build.dir = ${basedir}/build
+classes.dir = ${build.dir}/classes
+src.dir = ${basedir}/src
+repository.dir=${common.dir}/../repository
+
+ivy.file = ${basedir}/ivy.xml
+
+jar.file = ${build.dir}/${ant.project.name}.jar
+main.class.name = ${ant.project.name}.Main
+
+module.version.target = 1.0
diff --git a/src/example/multi-project/common/common.xml b/src/example/multi-project/common/common.xml
new file mode 100644
index 0000000..89cd508
--- /dev/null
+++ b/src/example/multi-project/common/common.xml
@@ -0,0 +1,205 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project name="common"
+ xmlns:ivy="antlib:org.apache.ivy.ant">
+ <!-- a sample common ant build file, used for ivy multi-project tutorial
+ feel free to copy and adapt it to your own needs
+ Note that the only targets specific to ivy are:
+ load-ivy
+ resolve
+ report
+ ivy-new-version
+ publish
+ publish-local
+
+ All other targets are usual ant based targets, which could have been written
+ in a build not depending at all on ivy:
+ resolve constructs a lib directory based upon ivy dependencies, and then the lib dir
+ is used as in any classical ant build
+ -->
+
+ <property file="${common.dir}/build.properties"/>
+
+ <property name="ivy.jar.dir" value="${user.home}/.ivy2/jars" />
+ <property name="ivy.jar.file" value="${ivy.jar.dir}/ivy.jar" />
+
+ <!-- =================================
+ target: load-ivy
+ this target is not necessary if you put ivy.jar in your ant lib directory
+ if you already have ivy 2.0 in your ant lib, you can simply remove this
+ target
+ ================================= -->
+ <target name="load-ivy">
+ <!-- try to load ivy here from home ivy dir, in case the user has not already dropped
+ it into ant's lib dir (note that the latter copy will always take precedence).
+ We will not fail as long as ivy home lib dir exists (it may be empty) and
+ ivy is in at least one of ant's lib dir or the ivy home lib dir. -->
+ <mkdir dir="${ivy.jar.dir}" />
+ <path id="ivy.lib.path">
+ <fileset dir="${ivy.jar.dir}" includes="*.jar"/>
+ </path>
+ <taskdef resource="org/apache/ivy/ant/antlib.xml"
+ uri="antlib:org.apache.ivy.ant" classpathref="ivy.lib.path"/>
+ </target>
+
+ <path id="lib.path.id">
+ <fileset dir="${lib.dir}" />
+ </path>
+ <path id="run.path.id">
+ <path refid="lib.path.id" />
+ <path location="${classes.dir}" />
+ </path>
+
+
+ <!-- setup ivy default configuration with some custom info -->
+ <property name="ivy.local.default.root" value="${repository.dir}/local"/>
+ <property name="ivy.shared.default.root" value="${repository.dir}/shared"/>
+
+ <!-- here is how we would have configured ivy if we had our own ivysettings file
+ <ivy:settings file="${common.dir}/ivysettings.xml" />
+ -->
+
+
+ <!-- =================================
+ target: resolve
+ ================================= -->
+ <target name="resolve" depends="clean-lib, load-ivy" description="--> resolve and retrieve dependencies with ivy">
+ <mkdir dir="${lib.dir}"/> <!-- not usually necessary, ivy creates the directory IF there are dependencies -->
+
+ <!-- the call to resolve is not mandatory, retrieve makes an implicit call if we don't -->
+ <ivy:resolve file="${ivy.file}"/>
+ <ivy:retrieve pattern="${lib.dir}/[artifact].[ext]" />
+ </target>
+
+ <!-- =================================
+ target: report
+ ================================= -->
+ <target name="report" depends="resolve" description="--> generates a report of dependencies">
+ <ivy:report todir="${build.dir}"/>
+ </target>
+
+ <!-- =================================
+ target: compile
+ ================================= -->
+ <target name="compile" depends="resolve" description="--> compile the project">
+ <mkdir dir="${classes.dir}" />
+ <javac srcdir="${src.dir}" destdir="${classes.dir}" classpathref="lib.path.id" debug="true" includeAntRuntime="false"/>
+ </target>
+
+ <!-- =================================
+ target: run
+ ================================= -->
+ <target name="run" depends="version, compile" description="--> compile and run the project">
+ <java classpathref="run.path.id" classname="${main.class.name}"/>
+ </target>
+
+ <target name="ivy-new-version" depends="load-ivy" unless="ivy.new.revision">
+ <!-- default module version prefix value -->
+ <property name="module.version.prefix" value="${module.version.target}-dev-b" />
+
+ <!-- asks to ivy an available version number -->
+ <ivy:info file="${ivy.file}" />
+ <ivy:buildnumber
+ organisation="${ivy.organisation}" module="${ivy.module}"
+ revision="${module.version.prefix}" defaultBuildNumber="1" revSep=""/>
+ </target>
+
+ <target name="local-version">
+ <tstamp>
+ <format property="now" pattern="yyyyMMddHHmmss"/>
+ </tstamp>
+ <property name="ivy.new.revision" value="${module.version.target}-local-${now}"/>
+ </target>
+
+ <target name="version" depends="ivy-new-version">
+ <!-- create version file in classpath for later inclusion in jar -->
+ <mkdir dir="${classes.dir}"/>
+ <echo message="version=${ivy.new.revision}" file="${classes.dir}/${ant.project.name}.properties" append="false" />
+
+ <!-- load generated version properties file -->
+ <property file="${classes.dir}/${ant.project.name}.properties" />
+ </target>
+
+ <!-- =================================
+ target: jar
+ ================================= -->
+ <target name="jar" depends="version, compile" description="--> make a jar file for this project">
+ <jar destfile="${jar.file}">
+ <fileset dir="${classes.dir}" />
+ <manifest>
+ <attribute name="Built-By" value="${user.name}"/>
+ <attribute name="Build-Version" value="${version}" />
+ </manifest>
+ </jar>
+ </target>
+
+ <!-- =================================
+ target: publish
+ ================================= -->
+ <target name="publish" depends="clean-build, jar" description="--> publish this project in the ivy repository">
+ <ivy:publish artifactspattern="${build.dir}/[artifact].[ext]"
+ resolver="shared"
+ pubrevision="${version}"
+ status="release"
+ />
+ <echo message="project ${ant.project.name} released with version ${version}" />
+ </target>
+
+ <!-- =================================
+ target: publish-local
+ ================================= -->
+ <target name="publish-local" depends="local-version, jar" description="--> publish this project in the local ivy repository">
+ <ivy:publish artifactspattern="${build.dir}/[artifact].[ext]"
+ resolver="local"
+ pubrevision="${version}"
+ pubdate="${now}"
+ status="integration"
+ forcedeliver="true"
+ />
+ <echo message="project ${ant.project.name} published locally with version ${version}" />
+ </target>
+
+ <!-- =================================
+ target: clean-local
+ ================================= -->
+ <target name="clean-local" depends="load-ivy"
+ description="--> cleans the local repository for the current module">
+ <ivy:info file="${ivy.file}" />
+ <delete dir="${ivy.local.default.root}/${ivy.organisation}/${ivy.module}"/>
+ </target>
+
+ <!-- =================================
+ target: clean-lib
+ ================================= -->
+ <target name="clean-lib" description="--> clean the project libraries directory (dependencies)">
+ <delete includeemptydirs="true" dir="${lib.dir}"/>
+ </target>
+
+ <!-- =================================
+ target: clean-build
+ ================================= -->
+ <target name="clean-build" description="--> clean the project built files">
+ <delete includeemptydirs="true" dir="${build.dir}"/>
+ </target>
+
+ <!-- =================================
+ target: clean
+ ================================= -->
+ <target name="clean" depends="clean-build, clean-lib" description="--> clean the project" />
+</project>
diff --git a/src/example/multi-project/projects/console/build.properties b/src/example/multi-project/projects/console/build.properties
new file mode 100644
index 0000000..1b54925
--- /dev/null
+++ b/src/example/multi-project/projects/console/build.properties
@@ -0,0 +1,21 @@
+# ***************************************************************
+# * Licensed to the Apache Software Foundation (ASF) under one
+# * or more contributor license agreements. See the NOTICE file
+# * distributed with this work for additional information
+# * regarding copyright ownership. The ASF licenses this file
+# * to you under the Apache License, Version 2.0 (the
+# * "License"); you may not use this file except in compliance
+# * with the License. You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing,
+# * software distributed under the License is distributed on an
+# * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# * KIND, either express or implied. See the License for the
+# * specific language governing permissions and limitations
+# * under the License.
+# ***************************************************************
+projects.dir = ${basedir}/..
+wkspace.dir = ${projects.dir}/..
+common.dir = ${wkspace.dir}/common
diff --git a/src/example/multi-project/projects/console/build.xml b/src/example/multi-project/projects/console/build.xml
new file mode 100644
index 0000000..fa7d93c
--- /dev/null
+++ b/src/example/multi-project/projects/console/build.xml
@@ -0,0 +1,23 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project name="console" default="compile">
+ <property file="build.properties"/>
+
+ <import file="${common.dir}/common.xml"/>
+</project>
diff --git a/src/example/multi-project/projects/console/ivy.xml b/src/example/multi-project/projects/console/ivy.xml
new file mode 100644
index 0000000..959a58b
--- /dev/null
+++ b/src/example/multi-project/projects/console/ivy.xml
@@ -0,0 +1,30 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivy-module version="1.0">
+ <info
+ organisation="org.apache.ivy.example"
+ module="console"
+ status="integration"/>
+ <dependencies>
+ <dependency name="version" rev="latest.integration" conf="default" />
+ <dependency name="list" rev="latest.integration" conf="default->standalone" />
+ <dependency name="find" rev="latest.integration" conf="default->standalone" />
+ <dependency name="sizewhere" rev="latest.integration" conf="default->standalone" />
+ </dependencies>
+</ivy-module>
diff --git a/src/example/multi-project/projects/console/src/console/Main.java b/src/example/multi-project/projects/console/src/console/Main.java
new file mode 100644
index 0000000..1a328b1
--- /dev/null
+++ b/src/example/multi-project/projects/console/src/console/Main.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package console;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.util.Collection;
+import java.util.Arrays;
+import java.lang.reflect.Method;
+
+
+public final class Main {
+ private static final Collection QUIT_COMMANDS =
+ Arrays.asList(new String[] {"quit", "q", "exit"});
+ private static final Collection HELP_COMMANDS =
+ Arrays.asList(new String[] {"help", "h", "?"});
+
+ public static void main(String[] a) throws Exception {
+ BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
+ while (true) {
+ System.out.print("> ");
+ String command = in.readLine().trim();
+ if (QUIT_COMMANDS.contains(command)) {
+ return;
+ }
+ if (HELP_COMMANDS.contains(command)) {
+ help();
+ continue;
+ }
+ String[] split = command.split(" ");
+ if (split.length == 0) {
+ error(command);
+ continue;
+ }
+
+ try {
+ String[] args = new String[split.length - 1];
+ System.arraycopy(split, 1, args, 0, args.length);
+ Class cl = Class.forName(split[0] + ".Main");
+ Method m = cl.getMethod("main", new Class[] {String[].class});
+ m.invoke(null, new Object[] {args});
+ } catch (Exception ex) {
+ error(command);
+ continue;
+ }
+ }
+ }
+
+ private static void help() {
+ System.out.println("available commands:");
+ System.out.println("\tquit: quit the console");
+ System.out.println("\thelp: displays this message");
+ System.out.println("\tlist -dir <dir>: list files in given directory");
+ System.out.println("\tfind -dir <dir> -name <name>: list files with given name in given dir");
+ System.out.println("\tsizewhere -dir <dir> -name <name>: "
+ + "compute total size of files with given name in given dir");
+ System.out.println("\thelp: displays this message");
+ }
+
+ private static void error(String command) {
+ System.out.println("unknown command " + command);
+ System.out.println("type ? for help");
+ }
+
+ private Main() {
+ }
+}
diff --git a/src/example/multi-project/projects/find/build.properties b/src/example/multi-project/projects/find/build.properties
new file mode 100644
index 0000000..1b54925
--- /dev/null
+++ b/src/example/multi-project/projects/find/build.properties
@@ -0,0 +1,21 @@
+# ***************************************************************
+# * Licensed to the Apache Software Foundation (ASF) under one
+# * or more contributor license agreements. See the NOTICE file
+# * distributed with this work for additional information
+# * regarding copyright ownership. The ASF licenses this file
+# * to you under the Apache License, Version 2.0 (the
+# * "License"); you may not use this file except in compliance
+# * with the License. You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing,
+# * software distributed under the License is distributed on an
+# * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# * KIND, either express or implied. See the License for the
+# * specific language governing permissions and limitations
+# * under the License.
+# ***************************************************************
+projects.dir = ${basedir}/..
+wkspace.dir = ${projects.dir}/..
+common.dir = ${wkspace.dir}/common
diff --git a/src/example/multi-project/projects/find/build.xml b/src/example/multi-project/projects/find/build.xml
new file mode 100644
index 0000000..270e2cf
--- /dev/null
+++ b/src/example/multi-project/projects/find/build.xml
@@ -0,0 +1,23 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project name="find" default="compile">
+ <property file="build.properties"/>
+
+ <import file="${common.dir}/common.xml"/>
+</project>
diff --git a/src/example/multi-project/projects/find/ivy.xml b/src/example/multi-project/projects/find/ivy.xml
new file mode 100644
index 0000000..ce19513
--- /dev/null
+++ b/src/example/multi-project/projects/find/ivy.xml
@@ -0,0 +1,37 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivy-module version="1.0">
+ <info
+ organisation="org.apache.ivy.example"
+ module="find"
+ status="integration"/>
+ <configurations>
+ <conf name="core"/>
+ <conf name="standalone" extends="core"/>
+ </configurations>
+ <publications>
+ <artifact name="find" type="jar" conf="core" />
+ </publications>
+ <dependencies>
+ <dependency name="version" rev="latest.integration" conf="core->default" />
+ <dependency name="list" rev="latest.integration" conf="core" />
+ <dependency org="commons-collections" name="commons-collections" rev="3.1" conf="core->default" />
+ <dependency org="commons-cli" name="commons-cli" rev="1.0" conf="standalone->default" />
+ </dependencies>
+</ivy-module>
diff --git a/src/example/multi-project/projects/find/src/find/FindFile.java b/src/example/multi-project/projects/find/src/find/FindFile.java
new file mode 100644
index 0000000..30e22a9
--- /dev/null
+++ b/src/example/multi-project/projects/find/src/find/FindFile.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package find;
+
+import version.Version;
+import list.ListFile;
+
+import java.util.Collection;
+import java.io.File;
+
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.collections.Predicate;
+
+public final class FindFile {
+ static {
+ Version.register("find");
+ }
+
+ public static Collection find(File dir, String name) {
+ return find(ListFile.list(dir), name);
+ }
+
+ private static Collection find(Collection files, final String name) {
+ return CollectionUtils.select(files, new Predicate() {
+ public boolean evaluate(Object o) {
+ return ((File) o).getName().indexOf(name) != -1;
+ }
+ });
+ }
+
+ private FindFile() {
+ }
+}
diff --git a/src/example/multi-project/projects/find/src/find/Main.java b/src/example/multi-project/projects/find/src/find/Main.java
new file mode 100644
index 0000000..63c9db0
--- /dev/null
+++ b/src/example/multi-project/projects/find/src/find/Main.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package find;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.GnuParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.OptionBuilder;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+
+public final class Main {
+ private static Options getOptions() {
+ Option dir = OptionBuilder.withArgName("dir")
+ .hasArg()
+ .withDescription("list files in given dir")
+ .create("dir");
+ Option name = OptionBuilder.withArgName("name")
+ .hasArg()
+ .withDescription("list files with given name")
+ .create("name");
+ Options options = new Options();
+
+ options.addOption(dir);
+ options.addOption(name);
+
+ return options;
+ }
+
+ public static void main(String[] args) throws Exception {
+ Options options = getOptions();
+ try {
+
+ CommandLineParser parser = new GnuParser();
+
+ CommandLine line = parser.parse(options, args);
+ File dir = new File(line.getOptionValue("dir", "."));
+ String name = line.getOptionValue("name", "jar");
+ Collection files = FindFile.find(dir, name);
+ System.out.println("listing files in " + dir + " containing " + name);
+ for (Iterator it = files.iterator(); it.hasNext();) {
+ System.out.println("\t" + it.next() + "\n");
+ }
+ } catch (ParseException exp) {
+ // oops, something went wrong
+ System.err.println("Parsing failed. Reason: " + exp.getMessage());
+
+ HelpFormatter formatter = new HelpFormatter();
+ formatter.printHelp("find", options);
+ }
+ }
+
+ private Main() {
+ }
+}
diff --git a/src/example/multi-project/projects/list/build.properties b/src/example/multi-project/projects/list/build.properties
new file mode 100644
index 0000000..1b54925
--- /dev/null
+++ b/src/example/multi-project/projects/list/build.properties
@@ -0,0 +1,21 @@
+# ***************************************************************
+# * Licensed to the Apache Software Foundation (ASF) under one
+# * or more contributor license agreements. See the NOTICE file
+# * distributed with this work for additional information
+# * regarding copyright ownership. The ASF licenses this file
+# * to you under the Apache License, Version 2.0 (the
+# * "License"); you may not use this file except in compliance
+# * with the License. You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing,
+# * software distributed under the License is distributed on an
+# * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# * KIND, either express or implied. See the License for the
+# * specific language governing permissions and limitations
+# * under the License.
+# ***************************************************************
+projects.dir = ${basedir}/..
+wkspace.dir = ${projects.dir}/..
+common.dir = ${wkspace.dir}/common
diff --git a/src/example/multi-project/projects/list/build.xml b/src/example/multi-project/projects/list/build.xml
new file mode 100644
index 0000000..e86db1a
--- /dev/null
+++ b/src/example/multi-project/projects/list/build.xml
@@ -0,0 +1,23 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project name="list" default="compile">
+ <property file="build.properties"/>
+
+ <import file="${common.dir}/common.xml"/>
+</project>
diff --git a/src/example/multi-project/projects/list/ivy.xml b/src/example/multi-project/projects/list/ivy.xml
new file mode 100644
index 0000000..8251fdc
--- /dev/null
+++ b/src/example/multi-project/projects/list/ivy.xml
@@ -0,0 +1,35 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivy-module version="1.0">
+ <info
+ organisation="org.apache.ivy.example"
+ module="list"
+ status="integration"/>
+ <configurations>
+ <conf name="core"/>
+ <conf name="standalone" extends="core"/>
+ </configurations>
+ <publications>
+ <artifact name="list" type="jar" conf="core" />
+ </publications>
+ <dependencies>
+ <dependency name="version" rev="latest.integration" conf="core->default" />
+ <dependency org="commons-cli" name="commons-cli" rev="1.0" conf="standalone->default" />
+ </dependencies>
+</ivy-module>
diff --git a/src/example/multi-project/projects/list/src/list/ListFile.java b/src/example/multi-project/projects/list/src/list/ListFile.java
new file mode 100644
index 0000000..99b921a
--- /dev/null
+++ b/src/example/multi-project/projects/list/src/list/ListFile.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package list;
+
+import version.Version;
+import java.util.Collection;
+import java.util.ArrayList;
+import java.io.File;
+
+public final class ListFile {
+ static {
+ Version.register("list");
+ }
+
+ public static Collection list(File dir) {
+ Collection files = new ArrayList();
+
+ return list(dir, files);
+ }
+
+ private static Collection list(File file, Collection files) {
+ if (file.isDirectory()) {
+ File[] f = file.listFiles();
+ for (int i = 0; i < f.length; i++) {
+ list(f[i], files);
+ }
+ } else {
+ files.add(file);
+ }
+ return files;
+ }
+
+ private ListFile() {
+ }
+}
diff --git a/src/example/multi-project/projects/list/src/list/Main.java b/src/example/multi-project/projects/list/src/list/Main.java
new file mode 100644
index 0000000..8874994
--- /dev/null
+++ b/src/example/multi-project/projects/list/src/list/Main.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package list;
+
+import java.io.File;
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.GnuParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.OptionBuilder;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+
+public final class Main {
+ private static Options getOptions() {
+ Option dir = OptionBuilder.withArgName("dir")
+ .hasArg()
+ .withDescription("list files in given dir")
+ .create("dir");
+ Options options = new Options();
+
+ options.addOption(dir);
+
+ return options;
+ }
+
+ public static void main(String[] args) throws Exception {
+ Options options = getOptions();
+ try {
+
+ CommandLineParser parser = new GnuParser();
+
+ CommandLine line = parser.parse(options, args);
+ File dir = new File(line.getOptionValue("dir", "."));
+ Collection files = ListFile.list(dir);
+ System.out.println("listing files in " + dir);
+ for (Iterator it = files.iterator(); it.hasNext();) {
+ System.out.println("\t" + it.next() + "\n");
+ }
+ } catch (ParseException exp) {
+ // oops, something went wrong
+ System.err.println("Parsing failed. Reason: " + exp.getMessage());
+
+ HelpFormatter formatter = new HelpFormatter();
+ formatter.printHelp("list", options);
+ }
+ }
+
+ private Main() {
+ }
+
+}
diff --git a/src/example/multi-project/projects/size/build.properties b/src/example/multi-project/projects/size/build.properties
new file mode 100644
index 0000000..1b54925
--- /dev/null
+++ b/src/example/multi-project/projects/size/build.properties
@@ -0,0 +1,21 @@
+# ***************************************************************
+# * Licensed to the Apache Software Foundation (ASF) under one
+# * or more contributor license agreements. See the NOTICE file
+# * distributed with this work for additional information
+# * regarding copyright ownership. The ASF licenses this file
+# * to you under the Apache License, Version 2.0 (the
+# * "License"); you may not use this file except in compliance
+# * with the License. You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing,
+# * software distributed under the License is distributed on an
+# * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# * KIND, either express or implied. See the License for the
+# * specific language governing permissions and limitations
+# * under the License.
+# ***************************************************************
+projects.dir = ${basedir}/..
+wkspace.dir = ${projects.dir}/..
+common.dir = ${wkspace.dir}/common
diff --git a/src/example/multi-project/projects/size/build.xml b/src/example/multi-project/projects/size/build.xml
new file mode 100644
index 0000000..efaacbf
--- /dev/null
+++ b/src/example/multi-project/projects/size/build.xml
@@ -0,0 +1,23 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project name="size" default="compile">
+ <property file="build.properties"/>
+
+ <import file="${common.dir}/common.xml"/>
+</project>
diff --git a/src/example/multi-project/projects/size/ivy.xml b/src/example/multi-project/projects/size/ivy.xml
new file mode 100644
index 0000000..ff20d0e
--- /dev/null
+++ b/src/example/multi-project/projects/size/ivy.xml
@@ -0,0 +1,28 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivy-module version="1.0">
+ <info
+ organisation="org.apache.ivy.example"
+ module="size"
+ status="integration"/>
+ <dependencies>
+ <dependency name="version" rev="latest.integration" conf="default" />
+ <dependency name="list" rev="latest.integration" conf="default->core" />
+ </dependencies>
+</ivy-module>
diff --git a/src/example/multi-project/projects/size/src/size/FileSize.java b/src/example/multi-project/projects/size/src/size/FileSize.java
new file mode 100644
index 0000000..7a3e842
--- /dev/null
+++ b/src/example/multi-project/projects/size/src/size/FileSize.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package size;
+
+import version.Version;
+import java.util.Collection;
+import java.util.Iterator;
+import java.io.File;
+
+public final class FileSize {
+ static {
+ Version.register("size");
+ }
+
+ public static long totalSize(File dir) {
+ return totalSize(list.ListFile.list(dir));
+ }
+
+ public static long totalSize(Collection files) {
+ long total = 0;
+ for (Iterator it = files.iterator(); it.hasNext();) {
+ File f = (File) it.next();
+ total += f.length();
+ }
+ return total;
+ }
+
+ private FileSize() {
+ }
+}
diff --git a/src/example/multi-project/projects/sizewhere/build.properties b/src/example/multi-project/projects/sizewhere/build.properties
new file mode 100644
index 0000000..1b54925
--- /dev/null
+++ b/src/example/multi-project/projects/sizewhere/build.properties
@@ -0,0 +1,21 @@
+# ***************************************************************
+# * Licensed to the Apache Software Foundation (ASF) under one
+# * or more contributor license agreements. See the NOTICE file
+# * distributed with this work for additional information
+# * regarding copyright ownership. The ASF licenses this file
+# * to you under the Apache License, Version 2.0 (the
+# * "License"); you may not use this file except in compliance
+# * with the License. You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing,
+# * software distributed under the License is distributed on an
+# * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# * KIND, either express or implied. See the License for the
+# * specific language governing permissions and limitations
+# * under the License.
+# ***************************************************************
+projects.dir = ${basedir}/..
+wkspace.dir = ${projects.dir}/..
+common.dir = ${wkspace.dir}/common
diff --git a/src/example/multi-project/projects/sizewhere/build.xml b/src/example/multi-project/projects/sizewhere/build.xml
new file mode 100644
index 0000000..107d1ca
--- /dev/null
+++ b/src/example/multi-project/projects/sizewhere/build.xml
@@ -0,0 +1,23 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project name="sizewhere" default="compile">
+ <property file="build.properties"/>
+
+ <import file="${common.dir}/common.xml"/>
+</project>
diff --git a/src/example/multi-project/projects/sizewhere/ivy.xml b/src/example/multi-project/projects/sizewhere/ivy.xml
new file mode 100644
index 0000000..344cb3f
--- /dev/null
+++ b/src/example/multi-project/projects/sizewhere/ivy.xml
@@ -0,0 +1,37 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivy-module version="1.0">
+ <info
+ organisation="org.apache.ivy.example"
+ module="sizewhere"
+ status="integration"/>
+ <configurations>
+ <conf name="core"/>
+ <conf name="standalone" extends="core"/>
+ </configurations>
+ <publications>
+ <artifact name="sizewhere" type="jar" conf="core" />
+ </publications>
+ <dependencies>
+ <dependency name="version" rev="latest.integration" conf="core->default" />
+ <dependency name="size" rev="latest.integration" conf="core->default" />
+ <dependency name="find" rev="latest.integration" conf="core" />
+ <dependency org="commons-cli" name="commons-cli" rev="1.0" conf="standalone->default" />
+ </dependencies>
+</ivy-module>
diff --git a/src/example/multi-project/projects/sizewhere/src/sizewhere/Main.java b/src/example/multi-project/projects/sizewhere/src/sizewhere/Main.java
new file mode 100644
index 0000000..2a795ff
--- /dev/null
+++ b/src/example/multi-project/projects/sizewhere/src/sizewhere/Main.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package sizewhere;
+
+import java.io.File;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.GnuParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.OptionBuilder;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+
+public final class Main {
+ private static Options getOptions() {
+ Option dir = OptionBuilder.withArgName("dir")
+ .hasArg()
+ .withDescription("give total size of files in given dir")
+ .create("dir");
+ Option name = OptionBuilder.withArgName("name")
+ .hasArg()
+ .withDescription("give total size of files with given name")
+ .create("name");
+ Options options = new Options();
+
+ options.addOption(dir);
+ options.addOption(name);
+
+ return options;
+ }
+
+ public static void main(String[] args) throws Exception {
+ Options options = getOptions();
+ try {
+
+ CommandLineParser parser = new GnuParser();
+
+ CommandLine line = parser.parse(options, args);
+ File dir = new File(line.getOptionValue("dir", "."));
+ String name = line.getOptionValue("name", "jar");
+ System.out.println("total size of files in " + dir
+ + " containing " + name + ": " + SizeWhere.totalSize(dir, name));
+ } catch (ParseException exp) {
+ // oops, something went wrong
+ System.err.println("Parsing failed. Reason: " + exp.getMessage());
+
+ HelpFormatter formatter = new HelpFormatter();
+ formatter.printHelp("sizewhere", options);
+ }
+ }
+
+ private Main() {
+ }
+}
diff --git a/src/example/multi-project/projects/sizewhere/src/sizewhere/SizeWhere.java b/src/example/multi-project/projects/sizewhere/src/sizewhere/SizeWhere.java
new file mode 100644
index 0000000..fc25c2a
--- /dev/null
+++ b/src/example/multi-project/projects/sizewhere/src/sizewhere/SizeWhere.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package sizewhere;
+
+import version.Version;
+import size.FileSize;
+import find.FindFile;
+
+import java.io.File;
+
+public final class SizeWhere {
+ static {
+ Version.register("sizewhere");
+ }
+
+ public static long totalSize(File dir, String name) {
+ return FileSize.totalSize(FindFile.find(dir, name));
+ }
+
+ private SizeWhere() {
+ }
+}
diff --git a/src/example/multi-project/projects/version/build.properties b/src/example/multi-project/projects/version/build.properties
new file mode 100644
index 0000000..1b54925
--- /dev/null
+++ b/src/example/multi-project/projects/version/build.properties
@@ -0,0 +1,21 @@
+# ***************************************************************
+# * Licensed to the Apache Software Foundation (ASF) under one
+# * or more contributor license agreements. See the NOTICE file
+# * distributed with this work for additional information
+# * regarding copyright ownership. The ASF licenses this file
+# * to you under the Apache License, Version 2.0 (the
+# * "License"); you may not use this file except in compliance
+# * with the License. You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing,
+# * software distributed under the License is distributed on an
+# * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# * KIND, either express or implied. See the License for the
+# * specific language governing permissions and limitations
+# * under the License.
+# ***************************************************************
+projects.dir = ${basedir}/..
+wkspace.dir = ${projects.dir}/..
+common.dir = ${wkspace.dir}/common
diff --git a/src/example/multi-project/projects/version/build.xml b/src/example/multi-project/projects/version/build.xml
new file mode 100644
index 0000000..5200004
--- /dev/null
+++ b/src/example/multi-project/projects/version/build.xml
@@ -0,0 +1,23 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project name="version" default="compile">
+ <property file="build.properties"/>
+
+ <import file="${common.dir}/common.xml"/>
+</project>
diff --git a/src/example/multi-project/projects/version/ivy.xml b/src/example/multi-project/projects/version/ivy.xml
new file mode 100644
index 0000000..7a29b46
--- /dev/null
+++ b/src/example/multi-project/projects/version/ivy.xml
@@ -0,0 +1,24 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivy-module version="1.0">
+ <info
+ organisation="org.apache.ivy.example"
+ module="version"
+ status="integration"/>
+</ivy-module>
diff --git a/src/example/multi-project/projects/version/src/version/Version.java b/src/example/multi-project/projects/version/src/version/Version.java
new file mode 100644
index 0000000..22952a8
--- /dev/null
+++ b/src/example/multi-project/projects/version/src/version/Version.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package version;
+
+import java.io.InputStream;
+import java.util.Properties;
+import java.util.Map;
+import java.util.HashMap;
+
+public final class Version {
+ static {
+ versions = new HashMap();
+ register("version");
+ }
+
+ private static Map versions;
+
+ public static void register(String module) {
+ try {
+ InputStream moduleVersion = Version.class.getResourceAsStream("/" + module
+ + ".properties");
+ Properties props = new Properties();
+ props.load(moduleVersion);
+ String version = (String) props.get("version");
+ versions.put(module, version);
+ System.out.println("--- using " + module + " v" + version);
+ } catch (Exception ex) {
+ System.err.println("an error occurred while registering " + module + ": "
+ + ex.getMessage());
+ ex.printStackTrace();
+ }
+ }
+
+ private Version() {
+ }
+}
diff --git a/src/example/packager-resolver/ivysettings.xml b/src/example/packager-resolver/ivysettings.xml
new file mode 100644
index 0000000..fbbaafd
--- /dev/null
+++ b/src/example/packager-resolver/ivysettings.xml
@@ -0,0 +1,35 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivysettings>
+ <resolvers>
+ <packager name="ivyroundup" buildRoot="${user.home}/.ivy2/packager/build" resourceCache="${user.home}/.ivy2/packager/cache">
+
+ <!-- This defines the Ivy file location as is normally done -->
+ <ivy pattern="http://ivyroundup.googlecode.com/svn/trunk/repo/modules/[organisation]/[module]/[revision]/ivy.xml"/>
+
+ <!-- This defines the location of the build instructions -->
+ <artifact pattern="http://ivyroundup.googlecode.com/svn/trunk/repo/modules/[organisation]/[module]/[revision]/packager.xml"/>
+ </packager>
+ </resolvers>
+ <modules>
+ <module organisation=".*" name=".*" resolver="ivyroundup"/>
+ </modules>
+ <caches useOrigin="true"/>
+</ivysettings>
+
diff --git a/src/java/org/apache/ivy/Ivy.java b/src/java/org/apache/ivy/Ivy.java
new file mode 100644
index 0000000..c3799e9
--- /dev/null
+++ b/src/java/org/apache/ivy/Ivy.java
@@ -0,0 +1,975 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.ivy.core.IvyContext;
+import org.apache.ivy.core.LogOptions;
+import org.apache.ivy.core.cache.ResolutionCacheManager;
+import org.apache.ivy.core.check.CheckEngine;
+import org.apache.ivy.core.deliver.DeliverEngine;
+import org.apache.ivy.core.deliver.DeliverOptions;
+import org.apache.ivy.core.event.EventManager;
+import org.apache.ivy.core.install.InstallEngine;
+import org.apache.ivy.core.install.InstallOptions;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleId;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.publish.PublishEngine;
+import org.apache.ivy.core.publish.PublishOptions;
+import org.apache.ivy.core.report.ResolveReport;
+import org.apache.ivy.core.repository.RepositoryManagementEngine;
+import org.apache.ivy.core.resolve.ResolveData;
+import org.apache.ivy.core.resolve.ResolveEngine;
+import org.apache.ivy.core.resolve.ResolveOptions;
+import org.apache.ivy.core.resolve.ResolvedModuleRevision;
+import org.apache.ivy.core.retrieve.RetrieveEngine;
+import org.apache.ivy.core.retrieve.RetrieveOptions;
+import org.apache.ivy.core.retrieve.RetrieveReport;
+import org.apache.ivy.core.search.ModuleEntry;
+import org.apache.ivy.core.search.OrganisationEntry;
+import org.apache.ivy.core.search.RevisionEntry;
+import org.apache.ivy.core.search.SearchEngine;
+import org.apache.ivy.core.settings.IvySettings;
+import org.apache.ivy.core.sort.SortEngine;
+import org.apache.ivy.core.sort.SortOptions;
+import org.apache.ivy.plugins.circular.CircularDependencyException;
+import org.apache.ivy.plugins.matcher.PatternMatcher;
+import org.apache.ivy.plugins.repository.TransferEvent;
+import org.apache.ivy.plugins.repository.TransferListener;
+import org.apache.ivy.plugins.resolver.BasicResolver;
+import org.apache.ivy.plugins.resolver.DependencyResolver;
+import org.apache.ivy.plugins.trigger.Trigger;
+import org.apache.ivy.util.DateUtil;
+import org.apache.ivy.util.HostUtil;
+import org.apache.ivy.util.Message;
+import org.apache.ivy.util.MessageLoggerEngine;
+
+/**
+ * <a href="http://ant.apache.org/ivy/">Ivy</a> is a free java based dependency manager.
+ * <p>
+ * This class is the main class of Ivy, which acts as a Facade to all services offered by Ivy:
+ * <ul>
+ * <li>resolve dependencies</li>
+ * <li>retrieve artifacts to a local location</li>
+ * <li>deliver and publish modules</li>
+ * <li>repository search and listing</li>
+ * </ul>
+ * Here is one typical usage:
+ *
+ * <pre>
+ * Ivy ivy = Ivy.newInstance();
+ * ivy.configure(new URL("ivysettings.xml"));
+ * ivy.resolve(new URL("ivy.xml"));
+ * </pre>
+ *
+ * </p>
+ * <h2>Using Ivy engines directly</h2>
+ * <p>
+ * If the methods offered by the {@link Ivy} class are not flexible enough and you want to use Ivy
+ * engines directly, you need to call the methods within a single {@link IvyContext} associated to
+ * the {@link Ivy} instance you use.<br/>
+ * To do so, it is recommended to use the {@link #execute(org.apache.ivy.Ivy.IvyCallback)} method
+ * like this:
+ *
+ * <pre>
+ * Ivy ivy = Ivy.newInstance();
+ * ivy.execute(new IvyCallback() {
+ * public Object doInIvyContext(Ivy ivy, IvyContext context) {
+ * // obviously we can use regular Ivy methods in the callback
+ * ivy.configure(new URL("ivysettings.xml"));
+ * // and we can safely use Ivy engines too
+ * ivy.getResolveEngine().resolve(new URL("ivy.xml"));
+ * return null;
+ * }
+ * });
+ * </pre>
+ *
+ * </p>
+ */
+public class Ivy {
+ /**
+ * Callback used to execute a set of Ivy related methods within an {@link IvyContext}.
+ *
+ * @see Ivy#execute(org.apache.ivy.Ivy.IvyCallback)
+ */
+ public static interface IvyCallback {
+ /**
+ * Executes Ivy related job within an {@link IvyContext}
+ *
+ * @param ivy
+ * the {@link Ivy} instance to which this callback is related
+ * @param context
+ * the {@link IvyContext} in which this callback is executed
+ * @return the result of this job, <code>null</code> if there is no result
+ */
+ public Object doInIvyContext(Ivy ivy, IvyContext context);
+ }
+
+ private static final int KILO = 1024;
+
+ /**
+ * @deprecated Use the {@link DateUtil} utility class instead.
+ */
+ @Deprecated
+ public static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat(
+ DateUtil.DATE_FORMAT_PATTERN);
+
+ /**
+ * the current version of Ivy, as displayed on the console when Ivy is initialized
+ */
+ private static final String IVY_VERSION;
+
+ /**
+ * the date at which this version of Ivy has been built. May be empty if unknown.
+ */
+ private static final String IVY_DATE;
+
+ static {
+ // initialize IVY_VERSION and IVY_DATE
+ Properties props = new Properties();
+ URL moduleURL = Message.class.getResource("/module.properties");
+ if (moduleURL != null) {
+ try {
+ InputStream module = moduleURL.openStream();
+ props.load(module);
+ module.close();
+ } catch (IOException e) {
+ // ignore this exception, we will initialize with default values
+ }
+ }
+ IVY_VERSION = props.getProperty("version", "non official version");
+ IVY_DATE = props.getProperty("date", "");
+ }
+
+ /**
+ * Returns the current version of Ivy, as displayed on the console when Ivy is initialized.
+ *
+ * @return the current version of Ivy
+ */
+ public static String getIvyVersion() {
+ return IVY_VERSION;
+ }
+
+ /**
+ * Returns the date at which this version of Ivy has been built.
+ * <p>
+ * May be empty if unknown.
+ *
+ * @return the date at which this version of Ivy has been built
+ */
+ public static String getIvyDate() {
+ return IVY_DATE;
+ }
+
+ /**
+ * Returns the URL at which Ivy web site can be found.
+ *
+ * @return the URL at which Ivy web site can be found
+ */
+ public static String getIvyHomeURL() {
+ return "http://ant.apache.org/ivy/";
+ }
+
+ public static Ivy newInstance() {
+ Ivy ivy = new Ivy();
+ ivy.bind();
+ return ivy;
+ }
+
+ public static Ivy newInstance(IvySettings settings) {
+ Ivy ivy = new Ivy();
+ ivy.setSettings(settings);
+ ivy.bind();
+ return ivy;
+ }
+
+ /**
+ * True if the current processing has been requested to be interrupted, false otherwise
+ */
+ private boolean interrupted;
+
+ /**
+ * True if this instance of Ivy has already been bound to its dependencies, false otherwise.
+ *
+ * @see bind()
+ */
+ private boolean bound;
+
+ /*
+ * Following are dependencies of the Ivy instance on instances of engines and manager which
+ * actually do the work The attributes can be set either manually using the corresponding
+ * setters, or all at once with the default implementations using the bind method
+ */
+ private IvySettings settings;
+
+ private EventManager eventManager;
+
+ private SortEngine sortEngine;
+
+ private SearchEngine searchEngine;
+
+ private CheckEngine checkEngine;
+
+ private ResolveEngine resolveEngine;
+
+ private RetrieveEngine retrieveEngine;
+
+ private DeliverEngine deliverEngine;
+
+ private PublishEngine publishEngine;
+
+ private InstallEngine installEngine;
+
+ private RepositoryManagementEngine repositoryEngine;
+
+ /**
+ * The logger engine to use to log messages when using this Ivy instance.
+ */
+ private MessageLoggerEngine loggerEngine = new MessageLoggerEngine();
+
+ /**
+ * The default constructor of Ivy allows to create an instance of Ivy with none of its
+ * dependencies (engines, settings, ...) created. If you use this constructor, it's your
+ * responsibility to set the dependencies of Ivy using the appropriate setters
+ * (setResolveEngine, ...). You can also call the bind method to set all the dependencies except
+ * those that you have provided using the setters. If you want to get an instance ready to use,
+ * prefer the use of Ivy.newInstance().
+ */
+ public Ivy() {
+ }
+
+ /**
+ * This method is used to bind this Ivy instance to required dependencies, i.e. instance of
+ * settings, engines, and so on.
+ * <p>
+ * After this call Ivy is still not configured, which means that the settings object is still
+ * empty.
+ * </p>
+ */
+ public void bind() {
+ pushContext();
+ try {
+ if (settings == null) {
+ settings = new IvySettings();
+ }
+ if (eventManager == null) {
+ eventManager = new EventManager();
+ }
+ if (sortEngine == null) {
+ sortEngine = new SortEngine(settings);
+ }
+ if (searchEngine == null) {
+ searchEngine = new SearchEngine(settings);
+ }
+ if (resolveEngine == null) {
+ resolveEngine = new ResolveEngine(settings, eventManager, sortEngine);
+ }
+ if (retrieveEngine == null) {
+ retrieveEngine = new RetrieveEngine(settings, eventManager);
+ }
+ if (deliverEngine == null) {
+ deliverEngine = new DeliverEngine(settings);
+ }
+ if (publishEngine == null) {
+ publishEngine = new PublishEngine(settings, eventManager);
+ }
+ if (installEngine == null) {
+ installEngine = new InstallEngine(settings, searchEngine, resolveEngine);
+ }
+ if (repositoryEngine == null) {
+ repositoryEngine = new RepositoryManagementEngine(settings, searchEngine,
+ resolveEngine);
+ }
+
+ eventManager.addTransferListener(new TransferListener() {
+ public void transferProgress(TransferEvent evt) {
+ ResolveData resolve;
+ switch (evt.getEventType()) {
+ case TransferEvent.TRANSFER_PROGRESS:
+ resolve = IvyContext.getContext().getResolveData();
+ if (resolve == null
+ || !LogOptions.LOG_QUIET.equals(resolve.getOptions().getLog())) {
+ Message.progress();
+ }
+ break;
+ case TransferEvent.TRANSFER_COMPLETED:
+ resolve = IvyContext.getContext().getResolveData();
+ if (resolve == null
+ || !LogOptions.LOG_QUIET.equals(resolve.getOptions().getLog())) {
+ Message.endProgress(" (" + (evt.getTotalLength() / KILO) + "kB)");
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ });
+
+ bound = true;
+ } finally {
+ popContext();
+ }
+ }
+
+ /**
+ * Executes the given callback in the context of this Ivy instance.
+ * <p>
+ * Alternatively you can use the {@link #pushContext()} and {@link #popContext()} methods, but
+ * this is not recommended:
+ *
+ * <pre>
+ * Object result = null;
+ * pushContext();
+ * try {
+ * result = callback.doInIvyContext(this, IvyContext.getContext());
+ * } finally {
+ * popContext();
+ * }
+ * doSomethingWithResult(result);
+ * </pre>
+ *
+ * </p>
+ *
+ * @param callback
+ * @return
+ */
+ public Object execute(IvyCallback callback) {
+ pushContext();
+ try {
+ return callback.doInIvyContext(this, IvyContext.getContext());
+ } finally {
+ popContext();
+ }
+ }
+
+ /**
+ * Pushes a new IvyContext bound to this Ivy instance if the current context is not already
+ * bound to this Ivy instance. If the current context is already bound to this Ivy instance, it
+ * pushes the current context on the context stack, so that you can (and must) always call
+ * {@link #popContext()} when you're done.
+ * <p>
+ * Alternatively, you can use the {@link #execute(org.apache.ivy.Ivy.IvyCallback)} method which
+ * takes care of everything for you.
+ * </p>
+ */
+ public void pushContext() {
+ if (IvyContext.getContext().peekIvy() != this) {
+ // the current Ivy context is associated with another Ivy instance, we push a new
+ // instance
+ IvyContext.pushNewContext();
+ IvyContext.getContext().setIvy(this);
+ } else {
+ // the current Ivy context is already associated with this Ivy instance, we only push it
+ // for popping consistency
+ IvyContext.pushContext(IvyContext.getContext());
+ }
+ }
+
+ /**
+ * Pops the current Ivy context.
+ * <p>
+ * You must call this method once and only once for each call to {@link #pushContext()}, when
+ * you're done with the your Ivy related work.
+ * </p>
+ * <p>
+ * Alternatively, you can use the {@link #execute(org.apache.ivy.Ivy.IvyCallback)} method which
+ * takes care of everything for you.
+ * </p>
+ */
+ public void popContext() {
+ IvyContext.popContext();
+ }
+
+ // ///////////////////////////////////////////////////////////////////////
+ // LOAD SETTINGS
+ // ///////////////////////////////////////////////////////////////////////
+ public void configure(File settingsFile) throws ParseException, IOException {
+ pushContext();
+ try {
+ assertBound();
+ settings.load(settingsFile);
+ postConfigure();
+ } finally {
+ popContext();
+ }
+ }
+
+ public void configure(URL settingsURL) throws ParseException, IOException {
+ pushContext();
+ try {
+ assertBound();
+ settings.load(settingsURL);
+ postConfigure();
+ } finally {
+ popContext();
+ }
+ }
+
+ public void configureDefault() throws ParseException, IOException {
+ pushContext();
+ try {
+ assertBound();
+ settings.loadDefault();
+ postConfigure();
+ } finally {
+ popContext();
+ }
+ }
+
+ /**
+ * Configures Ivy with 1.4 compatible default settings
+ */
+ public void configureDefault14() throws ParseException, IOException {
+ pushContext();
+ try {
+ assertBound();
+ settings.loadDefault14();
+ postConfigure();
+ } finally {
+ popContext();
+ }
+ }
+
+ // ///////////////////////////////////////////////////////////////////////
+ // CHECK
+ // ///////////////////////////////////////////////////////////////////////
+ public boolean check(URL ivyFile, String resolvername) {
+ pushContext();
+ try {
+ return checkEngine.check(ivyFile, resolvername);
+ } finally {
+ popContext();
+ }
+ }
+
+ // ///////////////////////////////////////////////////////////////////////
+ // RESOLVE
+ // ///////////////////////////////////////////////////////////////////////
+
+ public ResolveReport resolve(File ivySource) throws ParseException, IOException {
+ pushContext();
+ try {
+ return resolveEngine.resolve(ivySource);
+ } finally {
+ popContext();
+ }
+ }
+
+ public ResolveReport resolve(URL ivySource) throws ParseException, IOException {
+ pushContext();
+ try {
+ return resolveEngine.resolve(ivySource);
+ } finally {
+ popContext();
+ }
+ }
+
+ public ResolveReport resolve(ModuleRevisionId mrid, ResolveOptions options, boolean changing)
+ throws ParseException, IOException {
+ pushContext();
+ try {
+ return resolveEngine.resolve(mrid, options, changing);
+ } finally {
+ popContext();
+ }
+ }
+
+ public ResolveReport resolve(URL ivySource, ResolveOptions options) throws ParseException,
+ IOException {
+ pushContext();
+ try {
+ return resolveEngine.resolve(ivySource, options);
+ } finally {
+ popContext();
+ }
+ }
+
+ public ResolveReport resolve(File ivySource, ResolveOptions options) throws ParseException,
+ IOException {
+ return resolve(ivySource.toURI().toURL(), options);
+ }
+
+ public ResolveReport resolve(ModuleDescriptor md, ResolveOptions options)
+ throws ParseException, IOException {
+ pushContext();
+ try {
+ return resolveEngine.resolve(md, options);
+ } finally {
+ popContext();
+ }
+ }
+
+ // ///////////////////////////////////////////////////////////////////////
+ // INSTALL
+ // ///////////////////////////////////////////////////////////////////////
+
+ public ResolveReport install(ModuleRevisionId mrid, String from, String to,
+ InstallOptions options) throws IOException {
+ pushContext();
+ try {
+ return installEngine.install(mrid, from, to, options);
+ } finally {
+ popContext();
+ }
+ }
+
+ // ///////////////////////////////////////////////////////////////////////
+ // RETRIEVE
+ // ///////////////////////////////////////////////////////////////////////
+
+ public int retrieve(ModuleRevisionId mrid, String destFilePattern, RetrieveOptions options)
+ throws IOException {
+ pushContext();
+ try {
+ return retrieveEngine.retrieve(mrid, destFilePattern, options);
+ } finally {
+ popContext();
+ }
+ }
+
+ public RetrieveReport retrieve(ModuleRevisionId mrid, RetrieveOptions options)
+ throws IOException {
+ pushContext();
+ try {
+ return retrieveEngine.retrieve(mrid, options);
+ } finally {
+ popContext();
+ }
+ }
+
+ // ///////////////////////////////////////////////////////////////////////
+ // DELIVER
+ // ///////////////////////////////////////////////////////////////////////
+
+ public void deliver(ModuleRevisionId mrid, String revision, String destIvyPattern)
+ throws IOException, ParseException {
+ pushContext();
+ try {
+ deliverEngine.deliver(mrid, revision, destIvyPattern,
+ DeliverOptions.newInstance(settings));
+ } finally {
+ popContext();
+ }
+ }
+
+ public void deliver(String revision, String destIvyPattern, DeliverOptions options)
+ throws IOException, ParseException {
+ pushContext();
+ try {
+ deliverEngine.deliver(revision, destIvyPattern, options);
+ } finally {
+ popContext();
+ }
+ }
+
+ /**
+ * Example of use: deliver(mrid, "1.5", "target/ivy/ivy-[revision].xml",
+ * DeliverOptions.newInstance(settings).setStatus("release").setValidate(false));
+ *
+ * @param mrid
+ * @param revision
+ * @param destIvyPattern
+ * @param options
+ * @throws IOException
+ * @throws ParseException
+ */
+ public void deliver(ModuleRevisionId mrid, String revision, String destIvyPattern,
+ DeliverOptions options) throws IOException, ParseException {
+ pushContext();
+ try {
+ deliverEngine.deliver(mrid, revision, destIvyPattern, options);
+ } finally {
+ popContext();
+ }
+ }
+
+ // ///////////////////////////////////////////////////////////////////////
+ // PUBLISH
+ // ///////////////////////////////////////////////////////////////////////
+
+ public Collection publish(ModuleRevisionId mrid, Collection srcArtifactPattern,
+ String resolverName, PublishOptions options) throws IOException {
+ pushContext();
+ try {
+ return publishEngine.publish(mrid, srcArtifactPattern, resolverName, options);
+ } finally {
+ popContext();
+ }
+ }
+
+ // ///////////////////////////////////////////////////////////////////////
+ // SORT
+ // ///////////////////////////////////////////////////////////////////////
+
+ /**
+ * Sorts the collection of IvyNode from the less dependent to the more dependent
+ */
+ public List sortNodes(Collection nodes, SortOptions options) {
+ pushContext();
+ try {
+ return getSortEngine().sortNodes(nodes, options);
+ } finally {
+ popContext();
+ }
+ }
+
+ /**
+ * Sorts the given ModuleDescriptors from the less dependent to the more dependent. This sort
+ * ensures that a ModuleDescriptor is always found in the list before all ModuleDescriptors
+ * depending directly on it.
+ *
+ * @param moduleDescriptors
+ * a Collection of ModuleDescriptor to sort
+ * @param options
+ * Options to use to sort the descriptors.
+ * @return a List of sorted ModuleDescriptors
+ * @throws CircularDependencyException
+ * if a circular dependency exists and circular dependency strategy decide to throw
+ * an exception
+ */
+ public List sortModuleDescriptors(Collection moduleDescriptors, SortOptions options) {
+ pushContext();
+ try {
+ return getSortEngine().sortModuleDescriptors(moduleDescriptors, options);
+ } finally {
+ popContext();
+ }
+ }
+
+ // ///////////////////////////////////////////////////////////////////////
+ // SEARCH
+ // ///////////////////////////////////////////////////////////////////////
+
+ public ResolvedModuleRevision findModule(ModuleRevisionId mrid) {
+ pushContext();
+ try {
+ ResolveOptions options = new ResolveOptions();
+ options.setValidate(false);
+ return resolveEngine.findModule(mrid, options);
+ } finally {
+ popContext();
+ }
+ }
+
+ public ModuleEntry[] listModuleEntries(OrganisationEntry org) {
+ pushContext();
+ try {
+ return searchEngine.listModuleEntries(org);
+ } finally {
+ popContext();
+ }
+ }
+
+ public ModuleId[] listModules(ModuleId criteria, PatternMatcher matcher) {
+ pushContext();
+ try {
+ return searchEngine.listModules(criteria, matcher);
+ } finally {
+ popContext();
+ }
+ }
+
+ public ModuleRevisionId[] listModules(ModuleRevisionId criteria, PatternMatcher matcher) {
+ pushContext();
+ try {
+ return searchEngine.listModules(criteria, matcher);
+ } finally {
+ popContext();
+ }
+ }
+
+ public String[] listModules(String org) {
+ pushContext();
+ try {
+ return searchEngine.listModules(org);
+ } finally {
+ popContext();
+ }
+ }
+
+ public OrganisationEntry[] listOrganisationEntries() {
+ pushContext();
+ try {
+ return searchEngine.listOrganisationEntries();
+ } finally {
+ popContext();
+ }
+ }
+
+ public String[] listOrganisations() {
+ pushContext();
+ try {
+ return searchEngine.listOrganisations();
+ } finally {
+ popContext();
+ }
+ }
+
+ public RevisionEntry[] listRevisionEntries(ModuleEntry module) {
+ pushContext();
+ try {
+ return searchEngine.listRevisionEntries(module);
+ } finally {
+ popContext();
+ }
+ }
+
+ public String[] listRevisions(String org, String module) {
+ pushContext();
+ try {
+ return searchEngine.listRevisions(org, module);
+ } finally {
+ popContext();
+ }
+ }
+
+ public String[] listTokenValues(String token, Map otherTokenValues) {
+ pushContext();
+ try {
+ return searchEngine.listTokenValues(token, otherTokenValues);
+ } finally {
+ popContext();
+ }
+ }
+
+ // ///////////////////////////////////////////////////////////////////////
+ // INTERRUPTIONS
+ // ///////////////////////////////////////////////////////////////////////
+
+ /**
+ * Interrupts the current running operation, no later than interruptTimeout milliseconds after
+ * the call
+ */
+ public void interrupt() {
+ Thread operatingThread = IvyContext.getContext().getOperatingThread();
+ interrupt(operatingThread);
+ }
+
+ /**
+ * Interrupts the current running operation in the given operating thread, no later than
+ * interruptTimeout milliseconds after the call
+ */
+ public void interrupt(Thread operatingThread) {
+ if (operatingThread != null && operatingThread.isAlive()) {
+ if (operatingThread == Thread.currentThread()) {
+ throw new IllegalStateException("cannot call interrupt from ivy operating thread");
+ }
+ Message.verbose("interrupting operating thread...");
+ operatingThread.interrupt();
+ synchronized (this) {
+ interrupted = true;
+ }
+ try {
+ Message.verbose("waiting clean interruption of operating thread");
+ operatingThread.join(settings.getInterruptTimeout());
+ } catch (InterruptedException e) {
+ // reset thread interrupt status
+ Thread.currentThread().interrupt();
+ }
+ if (operatingThread.isAlive()) {
+ Message.warn("waited clean interruption for too long: stopping operating thread");
+ operatingThread.stop();
+ }
+ synchronized (this) {
+ interrupted = false;
+ }
+ }
+ }
+
+ public synchronized boolean isInterrupted() {
+ return interrupted;
+ }
+
+ /**
+ * Check if the current operation has been interrupted, and if it is the case, throw a runtime
+ * exception
+ */
+ public void checkInterrupted() {
+ if (isInterrupted()) {
+ Message.info("operation interrupted");
+ throw new RuntimeException("operation interrupted");
+ }
+ }
+
+ public static String getWorkingRevision() {
+ return "working@" + HostUtil.getLocalHostName();
+ }
+
+ public ResolutionCacheManager getResolutionCacheManager() {
+ return settings.getResolutionCacheManager();
+ }
+
+ private void assertBound() {
+ if (!bound) {
+ bind();
+ }
+ }
+
+ private void postConfigure() {
+ Collection triggers = settings.getTriggers();
+ for (Iterator iter = triggers.iterator(); iter.hasNext();) {
+ Trigger trigger = (Trigger) iter.next();
+ eventManager.addIvyListener(trigger, trigger.getEventFilter());
+ }
+
+ for (Iterator iter = settings.getResolvers().iterator(); iter.hasNext();) {
+ DependencyResolver resolver = (DependencyResolver) iter.next();
+ if (resolver instanceof BasicResolver) {
+ ((BasicResolver) resolver).setEventManager(eventManager);
+ }
+ }
+ }
+
+ public String getVariable(String name) {
+ pushContext();
+ try {
+ assertBound();
+ return settings.getVariable(name);
+ } finally {
+ popContext();
+ }
+ }
+
+ public String substitute(String str) {
+ pushContext();
+ try {
+ assertBound();
+ return settings.substitute(str);
+ } finally {
+ popContext();
+ }
+ }
+
+ public void setVariable(String varName, String value) {
+ pushContext();
+ try {
+ assertBound();
+ settings.setVariable(varName, value);
+ } finally {
+ popContext();
+ }
+ }
+
+ // ///////////////////////////////////////////////////////////////////
+ // GETTERS / SETTERS
+ // ///////////////////////////////////////////////////////////////////
+
+ public IvySettings getSettings() {
+ return settings;
+ }
+
+ public EventManager getEventManager() {
+ return eventManager;
+ }
+
+ public CheckEngine getCheckEngine() {
+ return checkEngine;
+ }
+
+ public void setCheckEngine(CheckEngine checkEngine) {
+ this.checkEngine = checkEngine;
+ }
+
+ public DeliverEngine getDeliverEngine() {
+ return deliverEngine;
+ }
+
+ public void setDeliverEngine(DeliverEngine deliverEngine) {
+ this.deliverEngine = deliverEngine;
+ }
+
+ public InstallEngine getInstallEngine() {
+ return installEngine;
+ }
+
+ public void setInstallEngine(InstallEngine installEngine) {
+ this.installEngine = installEngine;
+ }
+
+ public PublishEngine getPublishEngine() {
+ return publishEngine;
+ }
+
+ public void setPublishEngine(PublishEngine publishEngine) {
+ this.publishEngine = publishEngine;
+ }
+
+ public ResolveEngine getResolveEngine() {
+ return resolveEngine;
+ }
+
+ public void setResolveEngine(ResolveEngine resolveEngine) {
+ this.resolveEngine = resolveEngine;
+ }
+
+ public RetrieveEngine getRetrieveEngine() {
+ return retrieveEngine;
+ }
+
+ public void setRetrieveEngine(RetrieveEngine retrieveEngine) {
+ this.retrieveEngine = retrieveEngine;
+ }
+
+ public SearchEngine getSearchEngine() {
+ return searchEngine;
+ }
+
+ public void setSearchEngine(SearchEngine searchEngine) {
+ this.searchEngine = searchEngine;
+ }
+
+ public SortEngine getSortEngine() {
+ return sortEngine;
+ }
+
+ public void setSortEngine(SortEngine sortEngine) {
+ this.sortEngine = sortEngine;
+ }
+
+ public RepositoryManagementEngine getRepositoryEngine() {
+ return repositoryEngine;
+ }
+
+ public void setRepositoryEngine(RepositoryManagementEngine repositoryEngine) {
+ this.repositoryEngine = repositoryEngine;
+ }
+
+ public void setEventManager(EventManager eventManager) {
+ this.eventManager = eventManager;
+ }
+
+ public void setSettings(IvySettings settings) {
+ this.settings = settings;
+ }
+
+ public MessageLoggerEngine getLoggerEngine() {
+ return loggerEngine;
+ }
+}
diff --git a/src/java/org/apache/ivy/Ivy14.java b/src/java/org/apache/ivy/Ivy14.java
new file mode 100644
index 0000000..0ae8b7a
--- /dev/null
+++ b/src/java/org/apache/ivy/Ivy14.java
@@ -0,0 +1,502 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.text.ParseException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.ivy.core.deliver.DeliverOptions;
+import org.apache.ivy.core.deliver.PublishingDependencyRevisionResolver;
+import org.apache.ivy.core.install.InstallOptions;
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleId;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.publish.PublishOptions;
+import org.apache.ivy.core.report.ArtifactDownloadReport;
+import org.apache.ivy.core.report.ResolveReport;
+import org.apache.ivy.core.resolve.DownloadOptions;
+import org.apache.ivy.core.resolve.IvyNode;
+import org.apache.ivy.core.resolve.ResolveOptions;
+import org.apache.ivy.core.resolve.ResolvedModuleRevision;
+import org.apache.ivy.core.retrieve.RetrieveOptions;
+import org.apache.ivy.core.search.ModuleEntry;
+import org.apache.ivy.core.search.OrganisationEntry;
+import org.apache.ivy.core.search.RevisionEntry;
+import org.apache.ivy.core.sort.SilentNonMatchingVersionReporter;
+import org.apache.ivy.core.sort.SortOptions;
+import org.apache.ivy.plugins.matcher.PatternMatcher;
+import org.apache.ivy.plugins.resolver.DependencyResolver;
+import org.apache.ivy.util.Message;
+import org.apache.ivy.util.filter.Filter;
+import org.apache.ivy.util.filter.FilterHelper;
+
+/**
+ * This class can be used for easy migration from Ivy 1.4 API.
+ * <p>
+ * Indeed, Ivy 2.0 API has changed substantially, so it can take time to migrate existing code using
+ * Ivy 1.4 API to the new API. Using this class it's really easy: replace your instance of Ivy by an
+ * instance of this class.
+ * <p>
+ * For instance, where you were doing:
+ *
+ * <pre>
+ * Ivy ivy = new Ivy();
+ * </pre>
+ *
+ * do instead:
+ *
+ * <pre>
+ * Ivy14 ivy = new Ivy14();
+ * </pre>
+ *
+ * And that should be enough in most cases!
+ */
+public class Ivy14 {
+ // CheckStyle:ParameterNumberCheck OFF
+ // don't check parameter numbers, since this class is here for backward compatibility
+
+ private Ivy ivy;
+
+ public Ivy14() {
+ this(Ivy.newInstance());
+ }
+
+ public Ivy14(Ivy ivy) {
+ this.ivy = ivy;
+ }
+
+ public boolean check(URL ivyFile, String resolvername) {
+ return ivy.check(ivyFile, resolvername);
+ }
+
+ public void configure(File settingsFile) throws ParseException, IOException {
+ ivy.configure(settingsFile);
+ }
+
+ public void configure(URL settingsURL) throws ParseException, IOException {
+ ivy.configure(settingsURL);
+ }
+
+ public void configureDefault() throws ParseException, IOException {
+ ivy.configureDefault();
+ }
+
+ public void deliver(ModuleRevisionId mrid, String revision, File cache, String destIvyPattern,
+ String status, Date pubdate, PublishingDependencyRevisionResolver pdrResolver,
+ boolean validate, boolean resolveDynamicRevisions) throws IOException, ParseException {
+ ivy.deliver(mrid, revision, destIvyPattern, new DeliverOptions(status, pubdate,
+ pdrResolver, validate, resolveDynamicRevisions, null));
+ }
+
+ public void deliver(ModuleRevisionId mrid, String revision, File cache, String destIvyPattern,
+ String status, Date pubdate, PublishingDependencyRevisionResolver pdrResolver,
+ boolean validate) throws IOException, ParseException {
+ deliver(mrid, revision, cache, destIvyPattern, status, pubdate, pdrResolver, validate, true);
+ }
+
+ public Map determineArtifactsToCopy(ModuleId moduleId, String[] confs, File cache,
+ String destFilePattern, String destIvyPattern, Filter artifactFilter)
+ throws ParseException, IOException {
+ return ivy.getRetrieveEngine().determineArtifactsToCopy(
+ new ModuleRevisionId(moduleId, Ivy.getWorkingRevision()),
+ destFilePattern,
+ new RetrieveOptions().setConfs(confs).setDestIvyPattern(destIvyPattern)
+ .setArtifactFilter(artifactFilter));
+ }
+
+ public Map determineArtifactsToCopy(ModuleId moduleId, String[] confs, File cache,
+ String destFilePattern, String destIvyPattern) throws ParseException, IOException {
+ return ivy.getRetrieveEngine().determineArtifactsToCopy(
+ new ModuleRevisionId(moduleId, Ivy.getWorkingRevision()), destFilePattern,
+ new RetrieveOptions().setConfs(confs).setDestIvyPattern(destIvyPattern));
+ }
+
+ public ArtifactDownloadReport download(Artifact artifact, File cache, boolean useOrigin) {
+ Message.deprecated("using cache and useOrigin when calling download is not supported anymore");
+ return ivy.getResolveEngine().download(artifact, new DownloadOptions());
+ }
+
+ public ResolvedModuleRevision findModule(ModuleRevisionId id) {
+ ResolveOptions options = new ResolveOptions();
+ options.setValidate(false);
+ return ivy.getResolveEngine().findModule(id, options);
+ }
+
+ public IvyNode[] getDependencies(ModuleDescriptor md, String[] confs, File cache, Date date,
+ ResolveReport report, boolean validate, boolean transitive) {
+ return ivy.getResolveEngine().getDependencies(
+ md,
+ newResolveOptions(confs, null, cache, date, validate, false, transitive, false, true,
+ true, FilterHelper.NO_FILTER), report);
+ }
+
+ public IvyNode[] getDependencies(ModuleDescriptor md, String[] confs, File cache, Date date,
+ ResolveReport report, boolean validate) {
+ return ivy.getResolveEngine().getDependencies(
+ md,
+ newResolveOptions(confs, null, cache, date, validate, false, true, false, true, true,
+ FilterHelper.NO_FILTER), report);
+ }
+
+ public IvyNode[] getDependencies(URL ivySource, String[] confs, File cache, Date date,
+ boolean validate) throws ParseException, IOException {
+ return ivy.getResolveEngine().getDependencies(
+ ivySource,
+ newResolveOptions(confs, null, cache, date, validate, false, true, false, true, true,
+ FilterHelper.NO_FILTER));
+ }
+
+ public String getVariable(String name) {
+ return ivy.getVariable(name);
+ }
+
+ public ResolveReport install(ModuleRevisionId mrid, String from, String to, boolean transitive,
+ boolean validate, boolean overwrite, Filter artifactFilter, File cache,
+ String matcherName) throws IOException {
+ return ivy.install(mrid, from, to, new InstallOptions().setTransitive(transitive)
+ .setValidate(validate).setOverwrite(overwrite).setArtifactFilter(artifactFilter)
+ .setMatcherName(matcherName));
+ }
+
+ public void interrupt() {
+ ivy.interrupt();
+ }
+
+ public void interrupt(Thread operatingThread) {
+ ivy.interrupt(operatingThread);
+ }
+
+ public boolean isInterrupted() {
+ return ivy.isInterrupted();
+ }
+
+ public ModuleEntry[] listModuleEntries(OrganisationEntry org) {
+ return ivy.listModuleEntries(org);
+ }
+
+ public ModuleId[] listModules(ModuleId criteria, PatternMatcher matcher) {
+ return ivy.listModules(criteria, matcher);
+ }
+
+ public ModuleRevisionId[] listModules(ModuleRevisionId criteria, PatternMatcher matcher) {
+ return ivy.listModules(criteria, matcher);
+ }
+
+ public String[] listModules(String org) {
+ return ivy.listModules(org);
+ }
+
+ public OrganisationEntry[] listOrganisationEntries() {
+ return ivy.listOrganisationEntries();
+ }
+
+ public String[] listOrganisations() {
+ return ivy.listOrganisations();
+ }
+
+ public RevisionEntry[] listRevisionEntries(ModuleEntry module) {
+ return ivy.listRevisionEntries(module);
+ }
+
+ public String[] listRevisions(String org, String module) {
+ return ivy.listRevisions(org, module);
+ }
+
+ public String[] listTokenValues(String token, Map otherTokenValues) {
+ return ivy.listTokenValues(token, otherTokenValues);
+ }
+
+ public Collection publish(ModuleDescriptor md, DependencyResolver resolver,
+ Collection srcArtifactPattern, String srcIvyPattern, Artifact[] extraArtifacts,
+ boolean overwrite, String conf) throws IOException {
+ return ivy.getPublishEngine().publish(
+ md,
+ srcArtifactPattern,
+ resolver,
+ new PublishOptions().setSrcIvyPattern(srcIvyPattern).setExtraArtifacts(extraArtifacts)
+ .setOverwrite(overwrite).setConfs(splitConfs(conf)));
+ }
+
+ public Collection publish(ModuleRevisionId mrid, String pubrevision, File cache,
+ Collection srcArtifactPattern, String resolverName, String srcIvyPattern,
+ String status, Date pubdate, Artifact[] extraArtifacts, boolean validate,
+ boolean overwrite, boolean update, String conf) throws IOException {
+ return ivy.publish(
+ mrid,
+ srcArtifactPattern,
+ resolverName,
+ new PublishOptions().setStatus(status).setPubdate(pubdate).setPubrevision(pubrevision)
+ .setSrcIvyPattern(srcIvyPattern).setExtraArtifacts(extraArtifacts)
+ .setUpdate(update).setValidate(validate).setOverwrite(overwrite)
+ .setConfs(splitConfs(conf)));
+ }
+
+ public Collection publish(ModuleRevisionId mrid, String pubrevision, File cache,
+ String srcArtifactPattern, String resolverName, String srcIvyPattern, boolean validate,
+ boolean overwrite) throws IOException {
+ return ivy.publish(mrid, Collections.singleton(srcArtifactPattern), resolverName,
+ new PublishOptions().setPubrevision(pubrevision).setSrcIvyPattern(srcIvyPattern)
+ .setValidate(validate).setOverwrite(overwrite));
+ }
+
+ public Collection publish(ModuleRevisionId mrid, String pubrevision, File cache,
+ String srcArtifactPattern, String resolverName, String srcIvyPattern, boolean validate)
+ throws IOException {
+ return ivy.publish(mrid, Collections.singleton(srcArtifactPattern), resolverName,
+ new PublishOptions().setPubrevision(pubrevision).setSrcIvyPattern(srcIvyPattern)
+ .setValidate(validate));
+ }
+
+ public Collection publish(ModuleRevisionId mrid, String pubrevision, File cache,
+ String srcArtifactPattern, String resolverName, String srcIvyPattern, String status,
+ Date pubdate, Artifact[] extraArtifacts, boolean validate, boolean overwrite,
+ boolean update, String conf) throws IOException {
+ return ivy.publish(
+ mrid,
+ Collections.singleton(srcArtifactPattern),
+ resolverName,
+ new PublishOptions().setStatus(status).setPubdate(pubdate).setPubrevision(pubrevision)
+ .setSrcIvyPattern(srcIvyPattern).setExtraArtifacts(extraArtifacts)
+ .setUpdate(update).setValidate(validate).setOverwrite(overwrite)
+ .setConfs(splitConfs(conf)));
+ }
+
+ public ResolveReport resolve(File ivySource) throws ParseException, IOException {
+ return ivy.resolve(ivySource);
+ }
+
+ public ResolveReport resolve(ModuleDescriptor md, String[] confs, File cache, Date date,
+ boolean validate, boolean useCacheOnly, boolean transitive, boolean useOrigin,
+ boolean download, boolean outputReport, Filter artifactFilter) throws ParseException,
+ IOException {
+ return ivy.resolve(
+ md,
+ newResolveOptions(confs, null, cache, date, validate, useCacheOnly, transitive,
+ useOrigin, download, outputReport, artifactFilter));
+ }
+
+ private ResolveOptions newResolveOptions(String[] confs, String revision, File cache,
+ Date date, boolean validate, boolean useCacheOnly, boolean transitive,
+ boolean useOrigin, boolean download, boolean outputReport, Filter artifactFilter) {
+ if (useOrigin) {
+ ivy.getSettings().useDeprecatedUseOrigin();
+ }
+ return new ResolveOptions().setConfs(confs).setRevision(revision).setValidate(validate)
+ .setUseCacheOnly(useCacheOnly).setTransitive(transitive).setDownload(download)
+ .setOutputReport(outputReport).setArtifactFilter(artifactFilter);
+ }
+
+ public ResolveReport resolve(ModuleDescriptor md, String[] confs, File cache, Date date,
+ boolean validate, boolean useCacheOnly, boolean transitive, boolean download,
+ boolean outputReport, Filter artifactFilter) throws ParseException, IOException {
+ return ivy.resolve(
+ md,
+ newResolveOptions(confs, null, cache, date, validate, useCacheOnly, transitive, false,
+ download, outputReport, artifactFilter));
+ }
+
+ public ResolveReport resolve(ModuleDescriptor md, String[] confs, File cache, Date date,
+ boolean validate, boolean useCacheOnly, boolean transitive, Filter artifactFilter)
+ throws ParseException, IOException {
+ return ivy.resolve(
+ md,
+ newResolveOptions(confs, null, cache, date, validate, useCacheOnly, transitive, false,
+ true, true, artifactFilter));
+ }
+
+ public ResolveReport resolve(ModuleDescriptor md, String[] confs, File cache, Date date,
+ boolean validate, boolean useCacheOnly, Filter artifactFilter) throws ParseException,
+ IOException {
+ return ivy.resolve(
+ md,
+ newResolveOptions(confs, null, cache, date, validate, useCacheOnly, true, false, true,
+ true, artifactFilter));
+ }
+
+ public ResolveReport resolve(ModuleRevisionId mrid, String[] confs, boolean transitive,
+ boolean changing, File cache, Date date, boolean validate, boolean useCacheOnly,
+ boolean useOrigin, Filter artifactFilter) throws ParseException, IOException {
+ return ivy.resolve(
+ mrid,
+ newResolveOptions(confs, null, cache, date, validate, useCacheOnly, transitive,
+ useOrigin, true, true, artifactFilter), changing);
+ }
+
+ public ResolveReport resolve(ModuleRevisionId mrid, String[] confs, boolean transitive,
+ boolean changing, File cache, Date date, boolean validate, boolean useCacheOnly,
+ Filter artifactFilter) throws ParseException, IOException {
+ return ivy.resolve(
+ mrid,
+ newResolveOptions(confs, null, cache, date, validate, useCacheOnly, transitive, false,
+ true, true, artifactFilter), changing);
+ }
+
+ public ResolveReport resolve(ModuleRevisionId mrid, String[] confs) throws ParseException,
+ IOException {
+ return ivy.resolve(
+ mrid,
+ newResolveOptions(confs, null, ivy.getSettings().getDefaultCache(), null, true, false,
+ true, false, true, true, FilterHelper.NO_FILTER), false);
+ }
+
+ public ResolveReport resolve(URL ivySource, String revision, String[] confs, File cache,
+ Date date, boolean validate, boolean useCacheOnly, boolean transitive,
+ boolean useOrigin, Filter artifactFilter) throws ParseException, IOException {
+ return ivy.resolve(
+ ivySource,
+ newResolveOptions(confs, revision, cache, date, validate, useCacheOnly, transitive,
+ useOrigin, true, true, artifactFilter));
+ }
+
+ public ResolveReport resolve(URL ivySource, String revision, String[] confs, File cache,
+ Date date, boolean validate, boolean useCacheOnly, boolean transitive,
+ Filter artifactFilter) throws ParseException, IOException {
+ return ivy.resolve(
+ ivySource,
+ newResolveOptions(confs, revision, cache, date, validate, useCacheOnly, transitive,
+ false, true, true, artifactFilter));
+ }
+
+ public ResolveReport resolve(URL ivySource, String revision, String[] confs, File cache,
+ Date date, boolean validate, boolean useCacheOnly, Filter artifactFilter)
+ throws ParseException, IOException {
+ return ivy.resolve(
+ ivySource,
+ newResolveOptions(confs, revision, cache, date, validate, useCacheOnly, true, false,
+ true, true, artifactFilter));
+ }
+
+ public ResolveReport resolve(URL ivySource, String revision, String[] confs, File cache,
+ Date date, boolean validate, boolean useCacheOnly) throws ParseException, IOException {
+ return ivy.resolve(
+ ivySource,
+ newResolveOptions(confs, revision, cache, date, validate, useCacheOnly, true, false,
+ true, true, FilterHelper.NO_FILTER));
+ }
+
+ public ResolveReport resolve(URL ivySource, String revision, String[] confs, File cache,
+ Date date, boolean validate) throws ParseException, IOException {
+ return ivy.resolve(
+ ivySource,
+ newResolveOptions(confs, revision, cache, date, validate, false, true, false, true,
+ true, FilterHelper.NO_FILTER));
+ }
+
+ public ResolveReport resolve(URL ivySource) throws ParseException, IOException {
+ return ivy.resolve(ivySource);
+ }
+
+ public int retrieve(ModuleId moduleId, String[] confs, File cache, String destFilePattern,
+ String destIvyPattern, Filter artifactFilter, boolean sync, boolean useOrigin,
+ boolean makeSymlinks) {
+ try {
+ return ivy.retrieve(new ModuleRevisionId(moduleId, Ivy.getWorkingRevision()),
+ destFilePattern,
+ new RetrieveOptions().setConfs(confs).setDestIvyPattern(destIvyPattern)
+ .setArtifactFilter(artifactFilter).setSync(sync).setUseOrigin(useOrigin)
+ .setMakeSymlinks(makeSymlinks));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public int retrieve(ModuleId moduleId, String[] confs, File cache, String destFilePattern,
+ String destIvyPattern, Filter artifactFilter, boolean sync, boolean useOrigin) {
+ try {
+ return ivy.retrieve(new ModuleRevisionId(moduleId, Ivy.getWorkingRevision()),
+ destFilePattern,
+ new RetrieveOptions().setConfs(confs).setDestIvyPattern(destIvyPattern)
+ .setArtifactFilter(artifactFilter).setSync(sync).setUseOrigin(useOrigin));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public int retrieve(ModuleId moduleId, String[] confs, File cache, String destFilePattern,
+ String destIvyPattern, Filter artifactFilter) {
+ try {
+ return ivy.retrieve(new ModuleRevisionId(moduleId, Ivy.getWorkingRevision()),
+ destFilePattern,
+ new RetrieveOptions().setConfs(confs).setDestIvyPattern(destIvyPattern)
+ .setArtifactFilter(artifactFilter));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public int retrieve(ModuleId moduleId, String[] confs, File cache, String destFilePattern,
+ String destIvyPattern) {
+ try {
+ return ivy.retrieve(new ModuleRevisionId(moduleId, Ivy.getWorkingRevision()),
+ destFilePattern,
+ new RetrieveOptions().setConfs(confs).setDestIvyPattern(destIvyPattern));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public int retrieve(ModuleId moduleId, String[] confs, File cache, String destFilePattern) {
+ try {
+ return ivy.retrieve(new ModuleRevisionId(moduleId, Ivy.getWorkingRevision()),
+ destFilePattern, new RetrieveOptions().setConfs(confs));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public void setVariable(String varName, String value) {
+ ivy.setVariable(varName, value);
+ }
+
+ public List sortModuleDescriptors(Collection moduleDescriptors) {
+ return ivy
+ .sortModuleDescriptors(moduleDescriptors, new SortOptions()
+ .setNonMatchingVersionReporter(new SilentNonMatchingVersionReporter()));
+ }
+
+ public List sortNodes(Collection nodes) {
+ return ivy
+ .sortNodes(nodes, new SortOptions()
+ .setNonMatchingVersionReporter(new SilentNonMatchingVersionReporter()));
+ }
+
+ public String substitute(String str) {
+ return ivy.substitute(str);
+ }
+
+ private String[] splitConfs(String conf) {
+ if (conf == null || "".equals(conf)) {
+ return null;
+ }
+ String[] confs = conf.split(",");
+ for (int i = 0; i < confs.length; i++) {
+ confs[i] = confs[i].trim();
+ }
+ return confs;
+ }
+
+ // CheckStyle:ParameterNumberCheck ON
+}
diff --git a/src/java/org/apache/ivy/Main.java b/src/java/org/apache/ivy/Main.java
new file mode 100644
index 0000000..a274e64
--- /dev/null
+++ b/src/java/org/apache/ivy/Main.java
@@ -0,0 +1,591 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import org.apache.ivy.core.cache.ResolutionCacheManager;
+import org.apache.ivy.core.deliver.DeliverOptions;
+import org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.publish.PublishOptions;
+import org.apache.ivy.core.report.ArtifactDownloadReport;
+import org.apache.ivy.core.report.ResolveReport;
+import org.apache.ivy.core.resolve.ResolveOptions;
+import org.apache.ivy.core.retrieve.RetrieveOptions;
+import org.apache.ivy.core.settings.IvySettings;
+import org.apache.ivy.plugins.parser.xml.XmlModuleDescriptorWriter;
+import org.apache.ivy.plugins.report.XmlReportParser;
+import org.apache.ivy.util.DefaultMessageLogger;
+import org.apache.ivy.util.Message;
+import org.apache.ivy.util.cli.CommandLine;
+import org.apache.ivy.util.cli.CommandLineParser;
+import org.apache.ivy.util.cli.OptionBuilder;
+import org.apache.ivy.util.cli.ParseException;
+import org.apache.ivy.util.filter.FilterHelper;
+import org.apache.ivy.util.url.CredentialsStore;
+import org.apache.ivy.util.url.URLHandler;
+import org.apache.ivy.util.url.URLHandlerDispatcher;
+import org.apache.ivy.util.url.URLHandlerRegistry;
+
+/**
+ * Class used to launch ivy as a standalone tool.
+ * <p>
+ * Valid arguments can be obtained with the -? argument.
+ */
+public final class Main {
+ private static final int HELP_WIDTH = 80;
+
+ static CommandLineParser getParser() {
+ return new CommandLineParser()
+ .addCategory("settings options")
+ .addOption(
+ new OptionBuilder("settings").arg("settingsfile")
+ .description("use given file for settings").create())
+ .addOption(
+ new OptionBuilder("cache").arg("cachedir")
+ .description("use given directory for cache").create())
+ .addOption(
+ new OptionBuilder("novalidate").description(
+ "do not validate ivy files against xsd").create())
+ .addOption(
+ new OptionBuilder("m2compatible").description("use maven2 compatibility")
+ .create())
+ .addOption(
+ new OptionBuilder("conf").arg("settingsfile").deprecated()
+ .description("use given file for settings").create())
+ .addOption(
+ new OptionBuilder("useOrigin")
+ .deprecated()
+ .description(
+ "use original artifact location "
+ + "with local resolvers instead of copying to the cache")
+ .create())
+
+ .addCategory("resolve options")
+ .addOption(
+ new OptionBuilder("ivy").arg("ivyfile")
+ .description("use given file as ivy file").create())
+ .addOption(
+ new OptionBuilder("refresh").description("refresh dynamic resolved revisions")
+ .create())
+ .addOption(
+ new OptionBuilder("dependency")
+ .arg("organisation")
+ .arg("module")
+ .arg("revision")
+ .description(
+ "use this instead of ivy file to do the rest "
+ + "of the work with this as a dependency.").create())
+ .addOption(
+ new OptionBuilder("confs").arg("configurations").countArgs(false)
+ .description("resolve given configurations").create())
+ .addOption(
+ new OptionBuilder("types").arg("types").countArgs(false)
+ .description("comma separated list of accepted artifact types")
+ .create())
+ .addOption(
+ new OptionBuilder("mode").arg("resolvemode")
+ .description("the resolve mode to use").create())
+ .addOption(
+ new OptionBuilder("notransitive").description(
+ "do not resolve dependencies transitively").create())
+
+ .addCategory("retrieve options")
+ .addOption(
+ new OptionBuilder("retrieve").arg("retrievepattern")
+ .description("use given pattern as retrieve pattern").create())
+ .addOption(
+ new OptionBuilder("ivypattern").arg("pattern")
+ .description("use given pattern to copy the ivy files").create())
+ .addOption(
+ new OptionBuilder("sync").description("use sync mode for retrieve").create())
+ .addOption(
+ new OptionBuilder("symlink").description("create symbolic links").create())
+
+ .addCategory("cache path options")
+ .addOption(
+ new OptionBuilder("cachepath")
+ .arg("cachepathfile")
+ .description(
+ "outputs a classpath consisting of all dependencies in cache "
+ + "(including transitive ones) "
+ + "of the given ivy file to the given cachepathfile")
+ .create())
+
+ .addCategory("deliver options")
+ .addOption(
+ new OptionBuilder("deliverto").arg("ivypattern")
+ .description("use given pattern as resolved ivy file pattern").create())
+
+ .addCategory("publish options")
+ .addOption(
+ new OptionBuilder("publish").arg("resolvername")
+ .description("use given resolver to publish to").create())
+ .addOption(
+ new OptionBuilder("publishpattern").arg("artpattern")
+ .description("use given pattern to find artifacts to publish").create())
+ .addOption(
+ new OptionBuilder("revision").arg("revision")
+ .description("use given revision to publish the module").create())
+ .addOption(
+ new OptionBuilder("status").arg("status")
+ .description("use given status to publish the module").create())
+ .addOption(
+ new OptionBuilder("overwrite").description(
+ "overwrite files in the repository if they exist").create())
+
+ .addCategory("http auth options")
+ .addOption(
+ new OptionBuilder("realm").arg("realm")
+ .description("use given realm for HTTP AUTH").create())
+ .addOption(
+ new OptionBuilder("host").arg("host")
+ .description("use given host for HTTP AUTH").create())
+ .addOption(
+ new OptionBuilder("username").arg("username")
+ .description("use given username for HTTP AUTH").create())
+ .addOption(
+ new OptionBuilder("passwd").arg("passwd")
+ .description("use given password for HTTP AUTH").create())
+
+ .addCategory("launcher options")
+ .addOption(
+ new OptionBuilder("main").arg("main")
+ .description("the FQCN of the main class to launch").create())
+ .addOption(
+ new OptionBuilder("args").arg("args").countArgs(false)
+ .description("the arguments to give to the launched process").create())
+ .addOption(
+ new OptionBuilder("cp").arg("cp")
+ .description("extra classpath to use when launching process").create())
+
+ .addCategory("message options")
+ .addOption(
+ new OptionBuilder("debug").description("set message level to debug").create())
+ .addOption(
+ new OptionBuilder("verbose").description("set message level to verbose")
+ .create())
+ .addOption(
+ new OptionBuilder("warn").description("set message level to warn").create())
+ .addOption(
+ new OptionBuilder("error").description("set message level to error").create())
+
+ .addCategory("help options")
+ .addOption(new OptionBuilder("?").description("display this help").create())
+ .addOption(
+ new OptionBuilder("deprecated").description("show deprecated options").create())
+ .addOption(
+ new OptionBuilder("version").description("displays version information")
+ .create());
+ }
+
+ public static void main(String[] args) throws Exception {
+ CommandLineParser parser = getParser();
+ try {
+ run(parser, args);
+ System.exit(0);
+ } catch (ParseException ex) {
+ System.err.println(ex.getMessage());
+ usage(parser, false);
+ System.exit(1);
+ }
+ }
+
+ static void run(CommandLineParser parser, String[] args) throws Exception {
+ // parse the command line arguments
+ CommandLine line = parser.parse(args);
+
+ if (line.hasOption("?")) {
+ usage(parser, line.hasOption("deprecated"));
+ return;
+ }
+
+ if (line.hasOption("version")) {
+ System.out.println("Apache Ivy " + Ivy.getIvyVersion() + " - " + Ivy.getIvyDate()
+ + " :: " + Ivy.getIvyHomeURL());
+ return;
+ }
+
+ boolean validate = line.hasOption("novalidate") ? false : true;
+
+ Ivy ivy = Ivy.newInstance();
+ initMessage(line, ivy);
+ IvySettings settings = initSettings(line, ivy);
+ ivy.pushContext();
+
+ File cache = new File(settings.substitute(line.getOptionValue("cache", settings
+ .getDefaultCache().getAbsolutePath())));
+
+ if (line.hasOption("cache")) {
+ // override default cache path with user supplied cache path
+ settings.setDefaultCache(cache);
+ }
+
+ if (!cache.exists()) {
+ cache.mkdirs();
+ } else if (!cache.isDirectory()) {
+ error(cache + " is not a directory");
+ }
+
+ String[] confs;
+ if (line.hasOption("confs")) {
+ confs = line.getOptionValues("confs");
+ } else {
+ confs = new String[] {"*"};
+ }
+
+ File ivyfile;
+ if (line.hasOption("dependency")) {
+ String[] dep = line.getOptionValues("dependency");
+ ivyfile = File.createTempFile("ivy", ".xml");
+ ivyfile.deleteOnExit();
+ DefaultModuleDescriptor md = DefaultModuleDescriptor
+ .newDefaultInstance(ModuleRevisionId.newInstance(dep[0], dep[1] + "-caller",
+ "working"));
+ DefaultDependencyDescriptor dd = new DefaultDependencyDescriptor(md,
+ ModuleRevisionId.newInstance(dep[0], dep[1], dep[2]), false, false, true);
+ for (int i = 0; i < confs.length; i++) {
+ dd.addDependencyConfiguration("default", confs[i]);
+ }
+ md.addDependency(dd);
+ XmlModuleDescriptorWriter.write(md, ivyfile);
+ confs = new String[] {"default"};
+ } else {
+ ivyfile = new File(settings.substitute(line.getOptionValue("ivy", "ivy.xml")));
+ if (!ivyfile.exists()) {
+ error("ivy file not found: " + ivyfile);
+ } else if (ivyfile.isDirectory()) {
+ error("ivy file is not a file: " + ivyfile);
+ }
+ }
+
+ if (line.hasOption("useOrigin")) {
+ ivy.getSettings().useDeprecatedUseOrigin();
+ }
+ ResolveOptions resolveOptions = new ResolveOptions()
+ .setConfs(confs)
+ .setValidate(validate)
+ .setResolveMode(line.getOptionValue("mode"))
+ .setArtifactFilter(
+ FilterHelper.getArtifactTypeFilter(line.getOptionValues("types")));
+ if (line.hasOption("notransitive")) {
+ resolveOptions.setTransitive(false);
+ }
+ if (line.hasOption("refresh")) {
+ resolveOptions.setRefresh(true);
+ }
+ ResolveReport report = ivy.resolve(ivyfile.toURI().toURL(), resolveOptions);
+ if (report.hasError()) {
+ System.exit(1);
+ }
+ ModuleDescriptor md = report.getModuleDescriptor();
+
+ if (confs.length == 1 && "*".equals(confs[0])) {
+ confs = md.getConfigurationsNames();
+ }
+ if (line.hasOption("retrieve")) {
+ String retrievePattern = settings.substitute(line.getOptionValue("retrieve"));
+ if (retrievePattern.indexOf("[") == -1) {
+ retrievePattern = retrievePattern + "/lib/[conf]/[artifact].[ext]";
+ }
+ String ivyPattern = settings.substitute(line.getOptionValue("ivypattern"));
+ ivy.retrieve(
+ md.getModuleRevisionId(),
+ retrievePattern,
+ new RetrieveOptions()
+ .setConfs(confs)
+ .setSync(line.hasOption("sync"))
+ .setUseOrigin(line.hasOption("useOrigin"))
+ .setDestIvyPattern(ivyPattern)
+ .setArtifactFilter(
+ FilterHelper.getArtifactTypeFilter(line.getOptionValues("types")))
+ .setMakeSymlinks(line.hasOption("symlink"))
+ .setMakeSymlinksInMass(line.hasOption("symlinkmass")));
+ }
+ if (line.hasOption("cachepath")) {
+ outputCachePath(ivy, cache, md, confs,
+ line.getOptionValue("cachepath", "ivycachepath.txt"));
+ }
+
+ if (line.hasOption("revision")) {
+ ivy.deliver(
+ md.getResolvedModuleRevisionId(),
+ settings.substitute(line.getOptionValue("revision")),
+ settings.substitute(line.getOptionValue("deliverto", "ivy-[revision].xml")),
+ DeliverOptions.newInstance(settings)
+ .setStatus(settings.substitute(line.getOptionValue("status", "release")))
+ .setValidate(validate));
+ if (line.hasOption("publish")) {
+ ivy.publish(
+ md.getResolvedModuleRevisionId(),
+ Collections.singleton(settings.substitute(line.getOptionValue("publishpattern",
+ "distrib/[type]s/[artifact]-[revision].[ext]"))),
+ line.getOptionValue("publish"),
+ new PublishOptions()
+ .setPubrevision(settings.substitute(line.getOptionValue("revision")))
+ .setValidate(validate)
+ .setSrcIvyPattern(
+ settings.substitute(line.getOptionValue("deliverto",
+ "ivy-[revision].xml")))
+ .setOverwrite(line.hasOption("overwrite")));
+ }
+ }
+ if (line.hasOption("main")) {
+ // check if the option cp has been set
+ List fileList = getExtraClasspathFileList(line);
+
+ // merge -args and left over args
+ String[] fargs = line.getOptionValues("args");
+ if (fargs == null) {
+ fargs = new String[0];
+ }
+ String[] extra = line.getLeftOverArgs();
+ if (extra == null) {
+ extra = new String[0];
+ }
+ String[] params = new String[fargs.length + extra.length];
+ System.arraycopy(fargs, 0, params, 0, fargs.length);
+ System.arraycopy(extra, 0, params, fargs.length, extra.length);
+ // invoke with given main class and merged params
+ invoke(ivy, cache, md, confs, fileList, line.getOptionValue("main"), params);
+ }
+ ivy.getLoggerEngine().popLogger();
+ ivy.popContext();
+ }
+
+ /**
+ * Parses the <code>cp</code> option from the command line, and returns a list of {@link File}.
+ * <p>
+ * All the files contained in the returned List exist, non existing files are simply skipped
+ * with a warning.
+ * </p>
+ *
+ * @param line
+ * the command line in which the cp option shold be parsed
+ * @return a List of files to include as extra classpath entries, or <code>null</code> if no cp
+ * option was provided.
+ */
+ private static List/* <File> */getExtraClasspathFileList(CommandLine line) {
+ List fileList = null;
+ if (line.hasOption("cp")) {
+ fileList = new ArrayList/* <File> */();
+ String[] cpArray = line.getOptionValues("cp");
+ for (int index = 0; index < cpArray.length; index++) {
+ StringTokenizer tokenizer = new StringTokenizer(cpArray[index],
+ System.getProperty("path.separator"));
+ while (tokenizer.hasMoreTokens()) {
+ String token = tokenizer.nextToken();
+ File file = new File(token);
+ if (file.exists()) {
+ fileList.add(file);
+ } else {
+ Message.warn("Skipping extra classpath '" + file
+ + "' as it does not exist.");
+ }
+ }
+ }
+ }
+ return fileList;
+ }
+
+ private static IvySettings initSettings(CommandLine line, Ivy ivy)
+ throws java.text.ParseException, IOException, ParseException {
+ IvySettings settings = ivy.getSettings();
+ settings.addAllVariables(System.getProperties());
+ if (line.hasOption("m2compatible")) {
+ settings.setVariable("ivy.default.configuration.m2compatible", "true");
+ }
+
+ configureURLHandler(line.getOptionValue("realm", null), line.getOptionValue("host", null),
+ line.getOptionValue("username", null), line.getOptionValue("passwd", null));
+
+ String settingsPath = line.getOptionValue("settings", "");
+ if ("".equals(settingsPath)) {
+ settingsPath = line.getOptionValue("conf", "");
+ if (!"".equals(settingsPath)) {
+ Message.deprecated("-conf is deprecated, use -settings instead");
+ }
+ }
+ if ("".equals(settingsPath)) {
+ ivy.configureDefault();
+ } else {
+ File conffile = new File(settingsPath);
+ if (!conffile.exists()) {
+ error("ivy configuration file not found: " + conffile);
+ } else if (conffile.isDirectory()) {
+ error("ivy configuration file is not a file: " + conffile);
+ }
+ ivy.configure(conffile);
+ }
+ return settings;
+ }
+
+ private static void initMessage(CommandLine line, Ivy ivy) {
+ if (line.hasOption("debug")) {
+ ivy.getLoggerEngine().pushLogger(new DefaultMessageLogger(Message.MSG_DEBUG));
+ } else if (line.hasOption("verbose")) {
+ ivy.getLoggerEngine().pushLogger(new DefaultMessageLogger(Message.MSG_VERBOSE));
+ } else if (line.hasOption("warn")) {
+ ivy.getLoggerEngine().pushLogger(new DefaultMessageLogger(Message.MSG_WARN));
+ } else if (line.hasOption("error")) {
+ ivy.getLoggerEngine().pushLogger(new DefaultMessageLogger(Message.MSG_ERR));
+ } else {
+ ivy.getLoggerEngine().pushLogger(new DefaultMessageLogger(Message.MSG_INFO));
+ }
+ }
+
+ private static void outputCachePath(Ivy ivy, File cache, ModuleDescriptor md, String[] confs,
+ String outFile) {
+ try {
+ String pathSeparator = System.getProperty("path.separator");
+ StringBuffer buf = new StringBuffer();
+ Collection all = new LinkedHashSet();
+ ResolutionCacheManager cacheMgr = ivy.getResolutionCacheManager();
+ XmlReportParser parser = new XmlReportParser();
+ for (int i = 0; i < confs.length; i++) {
+ String resolveId = ResolveOptions.getDefaultResolveId(md);
+ File report = cacheMgr.getConfigurationResolveReportInCache(resolveId, confs[i]);
+ parser.parse(report);
+
+ all.addAll(Arrays.asList(parser.getArtifactReports()));
+ }
+ for (Iterator iter = all.iterator(); iter.hasNext();) {
+ ArtifactDownloadReport artifact = (ArtifactDownloadReport) iter.next();
+ if (artifact.getLocalFile() != null) {
+ buf.append(artifact.getLocalFile().getCanonicalPath());
+ buf.append(pathSeparator);
+ }
+ }
+
+ PrintWriter writer = new PrintWriter(new FileOutputStream(outFile));
+ if (buf.length() > 0) {
+ writer.println(buf.substring(0, buf.length() - pathSeparator.length()));
+ }
+ writer.close();
+ System.out.println("cachepath output to " + outFile);
+
+ } catch (Exception ex) {
+ throw new RuntimeException("impossible to build ivy cache path: " + ex.getMessage(), ex);
+ }
+ }
+
+ private static void invoke(Ivy ivy, File cache, ModuleDescriptor md, String[] confs,
+ List fileList, String mainclass, String[] args) {
+ List urls = new ArrayList();
+
+ // Add option cp (extra classpath) urls
+ if (fileList != null && fileList.size() > 0) {
+ for (Iterator iter = fileList.iterator(); iter.hasNext();) {
+ File file = (File) iter.next();
+ try {
+ urls.add(file.toURI().toURL());
+ } catch (MalformedURLException e) {
+ // Should not happen, just ignore.
+ }
+ }
+ }
+
+ try {
+ Collection all = new LinkedHashSet();
+ ResolutionCacheManager cacheMgr = ivy.getResolutionCacheManager();
+ XmlReportParser parser = new XmlReportParser();
+ for (int i = 0; i < confs.length; i++) {
+ String resolveId = ResolveOptions.getDefaultResolveId(md);
+ File report = cacheMgr.getConfigurationResolveReportInCache(resolveId, confs[i]);
+ parser.parse(report);
+
+ all.addAll(Arrays.asList(parser.getArtifactReports()));
+ }
+ for (Iterator iter = all.iterator(); iter.hasNext();) {
+ ArtifactDownloadReport artifact = (ArtifactDownloadReport) iter.next();
+
+ if (artifact.getLocalFile() != null) {
+ urls.add(artifact.getLocalFile().toURI().toURL());
+ }
+ }
+ } catch (Exception ex) {
+ throw new RuntimeException("impossible to build ivy cache path: " + ex.getMessage(), ex);
+ }
+
+ URLClassLoader classLoader = new URLClassLoader((URL[]) urls.toArray(new URL[urls.size()]),
+ Main.class.getClassLoader());
+
+ try {
+ Class c = classLoader.loadClass(mainclass);
+
+ Method mainMethod = c.getMethod("main", new Class[] {String[].class});
+
+ Thread.currentThread().setContextClassLoader(classLoader);
+ mainMethod.invoke(null, new Object[] {(args == null ? new String[0] : args)});
+ } catch (ClassNotFoundException cnfe) {
+ throw new RuntimeException("Could not find class: " + mainclass, cnfe);
+ } catch (SecurityException e) {
+ throw new RuntimeException("Could not find main method: " + mainclass, e);
+ } catch (NoSuchMethodException e) {
+ throw new RuntimeException("Could not find main method: " + mainclass, e);
+ } catch (IllegalAccessException e) {
+ throw new RuntimeException("No permissions to invoke main method: " + mainclass, e);
+ } catch (InvocationTargetException e) {
+ throw new RuntimeException("Unexpected exception invoking main method: " + mainclass, e);
+ }
+ }
+
+ private static void configureURLHandler(String realm, String host, String username,
+ String passwd) {
+ CredentialsStore.INSTANCE.addCredentials(realm, host, username, passwd);
+
+ URLHandlerDispatcher dispatcher = new URLHandlerDispatcher();
+ URLHandler httpHandler = URLHandlerRegistry.getHttp();
+ dispatcher.setDownloader("http", httpHandler);
+ dispatcher.setDownloader("https", httpHandler);
+ URLHandlerRegistry.setDefault(dispatcher);
+ }
+
+ private static void error(String msg) throws ParseException {
+ throw new ParseException(msg);
+ }
+
+ private static void usage(CommandLineParser parser, boolean showDeprecated) {
+ // automatically generate the help statement
+ PrintWriter pw = new PrintWriter(System.out);
+ parser.printHelp(pw, HELP_WIDTH, "ivy", showDeprecated);
+ pw.flush();
+ }
+
+ private Main() {
+ }
+}
diff --git a/src/java/org/apache/ivy/ant/AddPathTask.java b/src/java/org/apache/ivy/ant/AddPathTask.java
new file mode 100644
index 0000000..2397657
--- /dev/null
+++ b/src/java/org/apache/ivy/ant/AddPathTask.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.ant;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.DirSet;
+import org.apache.tools.ant.types.FileList;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Path.PathElement;
+
+/**
+ * This task is not directly related to ivy, but is useful in some modular build systems. The idea
+ * is to be able to contribute new sub path elements to an existing path.
+ */
+public class AddPathTask extends Task {
+ private String toPath;
+
+ private boolean first = false;
+
+ private Path toAdd;
+
+ public String getTopath() {
+ return toPath;
+ }
+
+ public void setTopath(String toPath) {
+ this.toPath = toPath;
+ }
+
+ public void setProject(Project project) {
+ super.setProject(project);
+ toAdd = new Path(project);
+ }
+
+ public void execute() throws BuildException {
+ Object element = getProject().getReference(toPath);
+ if (element == null) {
+ throw new BuildException("destination path not found: " + toPath);
+ }
+ if (!(element instanceof Path)) {
+ throw new BuildException("destination path is not a path: " + element.getClass());
+ }
+ Path dest = (Path) element;
+ if (first) {
+ // now way to add path elements at te beginning of the existing path: we do the opposite
+ // and replace the reference
+ toAdd.append(dest);
+ getProject().addReference(toPath, toAdd);
+ } else {
+ dest.append(toAdd);
+ }
+ }
+
+ public void add(Path path) throws BuildException {
+ toAdd.add(path);
+ }
+
+ public void addDirset(DirSet dset) throws BuildException {
+ toAdd.addDirset(dset);
+ }
+
+ public void addFilelist(FileList fl) throws BuildException {
+ toAdd.addFilelist(fl);
+ }
+
+ public void addFileset(FileSet fs) throws BuildException {
+ toAdd.addFileset(fs);
+ }
+
+ public Path createPath() throws BuildException {
+ return toAdd.createPath();
+ }
+
+ public PathElement createPathElement() throws BuildException {
+ return toAdd.createPathElement();
+ }
+
+ public boolean isFirst() {
+ return first;
+ }
+
+ public void setFirst(boolean first) {
+ this.first = first;
+ }
+}
diff --git a/src/java/org/apache/ivy/ant/AntBuildTrigger.java b/src/java/org/apache/ivy/ant/AntBuildTrigger.java
new file mode 100644
index 0000000..6b0dd3f
--- /dev/null
+++ b/src/java/org/apache/ivy/ant/AntBuildTrigger.java
@@ -0,0 +1,172 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.ant;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.ivy.core.IvyContext;
+import org.apache.ivy.core.IvyPatternHelper;
+import org.apache.ivy.core.event.IvyEvent;
+import org.apache.ivy.plugins.trigger.AbstractTrigger;
+import org.apache.ivy.plugins.trigger.Trigger;
+import org.apache.ivy.util.Message;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Ant;
+import org.apache.tools.ant.taskdefs.Property;
+
+/**
+ * Triggers an ant build on an event occurence.
+ * <p>
+ * Example of use:
+ *
+ * <pre>
+ * <ant-build-trigger event="pre-resolve-dependency"
+ * filter="revision=latest.integration"
+ * antfile="/path/to/[module]/build.xml"
+ * target="compile"/>
+ * </pre>
+ *
+ * Triggers an ant build for any dependency in asked in latest.integration, just before resolving
+ * the dependency.
+ * </p>
+ * <p>
+ * The onlyonce property is used to tell if the ant build should be triggered only once, or several
+ * times in the same build.
+ * </p>
+ *
+ * @see AntCallTrigger
+ * @since 1.4
+ */
+public class AntBuildTrigger extends AbstractTrigger implements Trigger {
+ private boolean onlyOnce = true;
+
+ private String target = null;
+
+ private Collection builds = new ArrayList();
+
+ private String buildFilePattern;
+
+ private String prefix;
+
+ public void progress(IvyEvent event) {
+ File f = getBuildFile(event);
+ if (f.exists()) {
+ if (onlyOnce && isBuilt(f)) {
+ Message.verbose("target build file already built, skipping: " + f);
+ } else {
+ Ant ant = new Ant();
+ Project project = (Project) IvyContext
+ .peekInContextStack(IvyTask.ANT_PROJECT_CONTEXT_KEY);
+ if (project == null) {
+ project = new Project();
+ project.init();
+ }
+
+ ant.setProject(project);
+ ant.setTaskName("ant");
+
+ ant.setAntfile(f.getAbsolutePath());
+ ant.setInheritAll(false);
+ String target = getTarget();
+ if (target != null) {
+ ant.setTarget(target);
+ }
+ Map atts = event.getAttributes();
+ for (Iterator iter = atts.keySet().iterator(); iter.hasNext();) {
+ String key = (String) iter.next();
+ String value = (String) atts.get(key);
+ if (value != null) {
+ Property p = ant.createProperty();
+ p.setName(prefix == null ? key : prefix + key);
+ p.setValue(value);
+ }
+ }
+
+ Message.verbose("triggering build: " + f + " target=" + target + " for " + event);
+ try {
+ ant.execute();
+ } catch (BuildException e) {
+ Message.verbose("Exception occurred while executing target " + target);
+ throw e;
+ }
+ markBuilt(f);
+
+ Message.debug("triggered build finished: " + f + " target=" + target + " for "
+ + event);
+ }
+ } else {
+ Message.verbose("no build file found for dependency, skipping: " + f);
+ }
+ }
+
+ private void markBuilt(File f) {
+ builds.add(f.getAbsolutePath());
+ }
+
+ private boolean isBuilt(File f) {
+ return builds.contains(f.getAbsolutePath());
+ }
+
+ private File getBuildFile(IvyEvent event) {
+ return IvyContext
+ .getContext()
+ .getSettings()
+ .resolveFile(
+ IvyPatternHelper.substituteTokens(getBuildFilePattern(), event.getAttributes()));
+ }
+
+ public String getBuildFilePattern() {
+ return buildFilePattern;
+ }
+
+ public void setAntfile(String pattern) {
+ buildFilePattern = pattern;
+ }
+
+ public String getTarget() {
+ return target;
+ }
+
+ public void setTarget(String target) {
+ this.target = target;
+ }
+
+ public boolean isOnlyonce() {
+ return onlyOnce;
+ }
+
+ public void setOnlyonce(boolean onlyonce) {
+ onlyOnce = onlyonce;
+ }
+
+ public String getPrefix() {
+ return prefix;
+ }
+
+ public void setPrefix(String prefix) {
+ this.prefix = prefix;
+ if (!prefix.endsWith(".")) {
+ this.prefix += ".";
+ }
+ }
+}
diff --git a/src/java/org/apache/ivy/ant/AntCallTrigger.java b/src/java/org/apache/ivy/ant/AntCallTrigger.java
new file mode 100644
index 0000000..005eea1
--- /dev/null
+++ b/src/java/org/apache/ivy/ant/AntCallTrigger.java
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.ant;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.ivy.core.IvyContext;
+import org.apache.ivy.core.IvyPatternHelper;
+import org.apache.ivy.core.event.IvyEvent;
+import org.apache.ivy.plugins.trigger.AbstractTrigger;
+import org.apache.ivy.plugins.trigger.Trigger;
+import org.apache.ivy.util.Message;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.CallTarget;
+import org.apache.tools.ant.taskdefs.Property;
+
+/**
+ * Triggers an call to an ant target on an event occurence.
+ * <p>
+ * This trigger only works when ivy is called from an ant build file, otherwise the trigger only log
+ * a failure.
+ * <p>
+ * Example of use in an ivysettings file:
+ *
+ * <pre>
+ * <ant-call-trigger event="post-download-artifact" filter="type=zip"
+ * target="unzip"/>
+ * </pre>
+ *
+ * Triggers a call to the target "unzip" for any downloaded artifact of type zip
+ *
+ * @see AntBuildTrigger
+ * @since 1.4
+ */
+public class AntCallTrigger extends AbstractTrigger implements Trigger {
+ private boolean onlyonce = true;
+
+ private String target = null;
+
+ private Collection calls = new ArrayList();
+
+ private String prefix;
+
+ public void progress(IvyEvent event) {
+ Project project = (Project) IvyContext.peekInContextStack(IvyTask.ANT_PROJECT_CONTEXT_KEY);
+ if (project == null) {
+ Message.info("ant call trigger can only be used from an ant build. Ignoring.");
+ return;
+ }
+ if (onlyonce && isTriggered(event)) {
+ Message.verbose("call already triggered for this event, skipping: " + event);
+ } else {
+ CallTarget call = new CallTarget();
+
+ call.setProject(project);
+ call.setTaskName("antcall");
+
+ Map attributes = event.getAttributes();
+ String target = IvyPatternHelper.substituteTokens(getTarget(), attributes);
+ call.setTarget(target);
+
+ for (Iterator iter = attributes.keySet().iterator(); iter.hasNext();) {
+ String key = (String) iter.next();
+ String value = (String) attributes.get(key);
+ Property p = call.createParam();
+ p.setName(prefix == null ? key : prefix + key);
+ p.setValue(value == null ? "" : value);
+ }
+
+ Message.verbose("triggering ant call: target=" + target + " for " + event);
+ call.execute();
+ markTriggered(event);
+
+ Message.debug("triggered ant call finished: target=" + target + " for " + event);
+ }
+ }
+
+ private void markTriggered(IvyEvent event) {
+ calls.add(event);
+ }
+
+ private boolean isTriggered(IvyEvent event) {
+ return calls.contains(event);
+ }
+
+ public String getTarget() {
+ return target;
+ }
+
+ public void setTarget(String target) {
+ this.target = target;
+ }
+
+ public boolean isOnlyonce() {
+ return onlyonce;
+ }
+
+ public void setOnlyonce(boolean onlyonce) {
+ this.onlyonce = onlyonce;
+ }
+
+ public String getPrefix() {
+ return prefix;
+ }
+
+ public void setPrefix(String prefix) {
+ this.prefix = prefix;
+ if (!prefix.endsWith(".")) {
+ this.prefix += ".";
+ }
+ }
+}
diff --git a/src/java/org/apache/ivy/ant/AntMessageLogger.java b/src/java/org/apache/ivy/ant/AntMessageLogger.java
new file mode 100644
index 0000000..85547f9
--- /dev/null
+++ b/src/java/org/apache/ivy/ant/AntMessageLogger.java
@@ -0,0 +1,150 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.ant;
+
+import org.apache.ivy.Ivy;
+import org.apache.ivy.util.AbstractMessageLogger;
+import org.apache.ivy.util.Checks;
+import org.apache.ivy.util.MessageLogger;
+import org.apache.tools.ant.BuildEvent;
+import org.apache.tools.ant.BuildListener;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.Task;
+
+/**
+ * Implementation of the simple message facility for ant.
+ */
+public class AntMessageLogger extends AbstractMessageLogger {
+ private static final int PROGRESS_LOG_PERIOD = 1500;
+
+ /**
+ * Creates and register an {@link AntMessageLogger} for the given {@link Task}, with the given
+ * {@link Ivy} instance.
+ * <p>
+ * The created instance will automatically be unregistered from the Ivy instance when the task
+ * finishes.
+ * </p>
+ *
+ * @param task
+ * the task the logger should use for logging
+ * @param ivy
+ * the ivy instance on which the logger should be registered
+ */
+ public static void register(ProjectComponent task, final Ivy ivy) {
+ MessageLogger current = ivy.getLoggerEngine().peekLogger();
+ if (current instanceof AntMessageLogger && task instanceof Task
+ && ((AntMessageLogger) current).task instanceof Task) {
+ Task currentTask = (Task) ((AntMessageLogger) current).task;
+
+ if ((currentTask.getTaskName() != null)
+ && currentTask.getTaskName().equals(((Task) task).getTaskName())) {
+ // The current AntMessageLogger already logs with the same
+ // prefix as the given task. So we shouldn't do anything...
+ return;
+ }
+ }
+
+ AntMessageLogger logger = new AntMessageLogger(task);
+ ivy.getLoggerEngine().pushLogger(logger);
+ task.getProject().addBuildListener(new BuildListener() {
+ private int stackDepth = 0;
+
+ public void buildFinished(BuildEvent event) {
+ }
+
+ public void buildStarted(BuildEvent event) {
+ }
+
+ public void targetStarted(BuildEvent event) {
+ }
+
+ public void targetFinished(BuildEvent event) {
+ }
+
+ public void taskStarted(BuildEvent event) {
+ stackDepth++;
+ }
+
+ public void taskFinished(BuildEvent event) {
+ // NB: There is somtimes task created by an other task
+ // in that case, we should not uninit Message. The log should stay associated
+ // with the initial task, except if it was an antcall, ant or subant target
+ // NB2 : Testing the identity of the task is not enought, event.getTask() return
+ // an instance of UnknownElement is wrapping the concrete instance
+ stackDepth--;
+ if (stackDepth == -1) {
+ ivy.getLoggerEngine().popLogger();
+ event.getProject().removeBuildListener(this);
+ }
+ }
+
+ public void messageLogged(BuildEvent event) {
+ }
+ });
+
+ }
+
+ private ProjectComponent task;
+
+ private long lastProgressFlush = 0;
+
+ private StringBuffer buf = new StringBuffer();
+
+ /**
+ * Constructs a new AntMEssageImpl instance.
+ *
+ * @param antProjectComponent
+ * the ant project component this message implementation should use for logging. Must
+ * not be <code>null</code>.
+ */
+ protected AntMessageLogger(ProjectComponent task) {
+ Checks.checkNotNull(task, "task");
+ this.task = task;
+ }
+
+ public void log(String msg, int level) {
+ task.log(msg, level);
+ }
+
+ public void rawlog(String msg, int level) {
+ task.getProject().log(msg, level);
+ }
+
+ public void doProgress() {
+ buf.append(".");
+ if (lastProgressFlush == 0) {
+ lastProgressFlush = System.currentTimeMillis();
+ }
+ // log with ant causes a new line -> we do it only once in a while
+ if (System.currentTimeMillis() - lastProgressFlush > PROGRESS_LOG_PERIOD) {
+ task.log(buf.toString());
+ buf.setLength(0);
+ lastProgressFlush = System.currentTimeMillis();
+ }
+ }
+
+ public void doEndProgress(String msg) {
+ task.log(buf + msg);
+ buf.setLength(0);
+ lastProgressFlush = 0;
+ }
+
+ public String toString() {
+ return "AntMessageLogger:" + task;
+ }
+}
diff --git a/src/java/org/apache/ivy/ant/BuildOBRTask.java b/src/java/org/apache/ivy/ant/BuildOBRTask.java
new file mode 100644
index 0000000..226167a
--- /dev/null
+++ b/src/java/org/apache/ivy/ant/BuildOBRTask.java
@@ -0,0 +1,208 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.ant;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.text.ParseException;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.xml.transform.TransformerConfigurationException;
+
+import org.apache.ivy.Ivy;
+import org.apache.ivy.core.IvyContext;
+import org.apache.ivy.core.cache.DefaultRepositoryCacheManager;
+import org.apache.ivy.core.cache.RepositoryCacheManager;
+import org.apache.ivy.core.settings.IvySettings;
+import org.apache.ivy.osgi.obr.xml.OBRXMLWriter;
+import org.apache.ivy.osgi.repo.ArtifactReportManifestIterable;
+import org.apache.ivy.osgi.repo.FSManifestIterable;
+import org.apache.ivy.osgi.repo.ManifestAndLocation;
+import org.apache.ivy.osgi.repo.ResolverManifestIterable;
+import org.apache.ivy.plugins.resolver.BasicResolver;
+import org.apache.ivy.plugins.resolver.DependencyResolver;
+import org.apache.ivy.util.Message;
+import org.apache.tools.ant.BuildException;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+
+public class BuildOBRTask extends IvyCacheTask {
+
+ private String resolverName = null;
+
+ private File file = null;
+
+ private String cacheName = null;
+
+ private String encoding = "UTF-8";
+
+ private boolean indent = true;
+
+ private File baseDir;
+
+ private boolean quiet;
+
+ private List<String> sourceTypes = Arrays.asList("source", "sources", "src");
+
+ public void setResolver(String resolverName) {
+ this.resolverName = resolverName;
+ }
+
+ public void setCache(String cacheName) {
+ this.cacheName = cacheName;
+ }
+
+ public void setOut(File file) {
+ this.file = file;
+ }
+
+ public void setEncoding(String encoding) {
+ this.encoding = encoding;
+ }
+
+ public void setIndent(boolean indent) {
+ this.indent = indent;
+ }
+
+ public void setBaseDir(File dir) {
+ this.baseDir = dir;
+ }
+
+ public void setQuiet(boolean quiet) {
+ this.quiet = quiet;
+ }
+
+ public void setSourceType(String sourceType) {
+ this.sourceTypes = Arrays.asList(sourceType.split(","));
+ }
+
+ protected void prepareTask() {
+ // if browsing a folder, not need for an Ivy instance
+ if (baseDir == null) {
+ super.prepareTask();
+ }
+ // ensure the configured source type get also resolved
+ if (getType() != null && !getType().equals("*") && sourceTypes != null
+ && !sourceTypes.isEmpty()) {
+ StringBuilder buffer = new StringBuilder(getType());
+ for (String sourceType : sourceTypes) {
+ buffer.append(",");
+ buffer.append(sourceType);
+ }
+ setType(buffer.toString());
+ }
+ }
+
+ public void doExecute() throws BuildException {
+ if (file == null) {
+ throw new BuildException("No output file specified: use the attribute 'out'");
+ }
+
+ Iterable<ManifestAndLocation> it;
+ if (resolverName != null) {
+ if (baseDir != null) {
+ throw new BuildException("specify only one of 'resolver' or 'baseDir'");
+ }
+ if (cacheName != null) {
+ throw new BuildException("specify only one of 'resolver' or 'cache'");
+ }
+ Ivy ivy = getIvyInstance();
+ IvySettings settings = ivy.getSettings();
+ DependencyResolver resolver = settings.getResolver(resolverName);
+ if (resolver == null) {
+ throw new BuildException("the resolver '" + resolverName + "' was not found");
+ }
+ if (!(resolver instanceof BasicResolver)) {
+ throw new BuildException("the type of resolver '" + resolver.getClass().getName()
+ + "' is not supported.");
+ }
+ it = new ResolverManifestIterable((BasicResolver) resolver);
+ } else if (baseDir != null) {
+ if (cacheName != null) {
+ throw new BuildException("specify only one of 'baseDir' or 'cache'");
+ }
+ if (!baseDir.isDirectory()) {
+ throw new BuildException(baseDir + " is not a directory");
+ }
+ it = new FSManifestIterable(baseDir);
+ } else if (cacheName != null) {
+ Ivy ivy = getIvyInstance();
+ RepositoryCacheManager cacheManager = ivy.getSettings().getRepositoryCacheManager(
+ cacheName);
+ if (!(cacheManager instanceof DefaultRepositoryCacheManager)) {
+ throw new BuildException("the type of cache '" + cacheManager.getClass().getName()
+ + "' is not supported.");
+ }
+ File basedir = ((DefaultRepositoryCacheManager) cacheManager).getBasedir();
+ it = new FSManifestIterable(basedir);
+ } else {
+ prepareAndCheck();
+ try {
+ it = new ArtifactReportManifestIterable(getArtifactReports(), sourceTypes);
+ } catch (ParseException e) {
+ throw new BuildException("Impossible to parse the artifact reports: "
+ + e.getMessage(), e);
+ } catch (IOException e) {
+ throw new BuildException("Impossible to read the artifact reports: "
+ + e.getMessage(), e);
+ }
+ }
+
+ OutputStream out;
+ try {
+ out = new FileOutputStream(file);
+ } catch (FileNotFoundException e) {
+ throw new BuildException(file + " was not found", e);
+ }
+
+ ContentHandler hd;
+ try {
+ hd = OBRXMLWriter.newHandler(out, encoding, indent);
+ } catch (TransformerConfigurationException e) {
+ throw new BuildException("Sax configuration error: " + e.getMessage(), e);
+ }
+
+ class AntMessageLogger2 extends AntMessageLogger {
+ AntMessageLogger2() {
+ super(BuildOBRTask.this);
+ }
+ }
+ IvyContext.getContext().getMessageLogger();
+ Message.setDefaultLogger(new AntMessageLogger2());
+
+ try {
+ OBRXMLWriter.writeManifests(it, hd, quiet);
+ } catch (SAXException e) {
+ throw new BuildException("Sax serialisation error: " + e.getMessage(), e);
+ }
+
+ try {
+ out.flush();
+ out.close();
+ } catch (IOException e) {
+ // don't care
+ }
+
+ Message.sumupProblems();
+ }
+
+}
diff --git a/src/java/org/apache/ivy/ant/ConvertManifestTask.java b/src/java/org/apache/ivy/ant/ConvertManifestTask.java
new file mode 100644
index 0000000..ab470d7
--- /dev/null
+++ b/src/java/org/apache/ivy/ant/ConvertManifestTask.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.ant;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.text.ParseException;
+import java.util.jar.Manifest;
+
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.osgi.core.BundleInfo;
+import org.apache.ivy.osgi.core.BundleInfoAdapter;
+import org.apache.ivy.osgi.core.ExecutionEnvironmentProfileProvider;
+import org.apache.ivy.osgi.core.ManifestParser;
+import org.apache.ivy.osgi.core.OSGiManifestParser;
+import org.apache.ivy.plugins.parser.xml.XmlModuleDescriptorWriter;
+import org.apache.tools.ant.BuildException;
+
+public class ConvertManifestTask extends IvyTask {
+
+ private File manifest = null;
+
+ private File ivyFile = null;
+
+ private ExecutionEnvironmentProfileProvider profileProvider;
+
+ public void setProfileProvider(ExecutionEnvironmentProfileProvider profileProvider) {
+ this.profileProvider = profileProvider;
+ }
+
+ public void setManifest(File manifest) {
+ this.manifest = manifest;
+ }
+
+ public void setIvyFile(File ivyFile) {
+ this.ivyFile = ivyFile;
+ }
+
+ public void doExecute() throws BuildException {
+ if (ivyFile == null) {
+ throw new BuildException("destination ivy file is required for convertmanifest task");
+ }
+ if (manifest == null) {
+ throw new BuildException("source manifest file is required for convertmanifest task");
+ }
+ if (profileProvider == null) {
+ try {
+ profileProvider = new ExecutionEnvironmentProfileProvider();
+ } catch (IOException e) {
+ throw new BuildException("Enable to load the default environment profiles", e);
+ }
+ }
+
+ Manifest m;
+ try {
+ m = new Manifest(new FileInputStream(manifest));
+ } catch (FileNotFoundException e) {
+ throw new BuildException("the manifest file '" + manifest + "' was not found", e);
+ } catch (IOException e) {
+ throw new BuildException("the manifest file '" + manifest + "' could not be read", e);
+ }
+
+ BundleInfo bundleInfo;
+ try {
+ bundleInfo = ManifestParser.parseManifest(m);
+ } catch (ParseException e) {
+ throw new BuildException("Incorrect manifest file '" + manifest + "'", e);
+ }
+ ModuleDescriptor md = BundleInfoAdapter.toModuleDescriptor(
+ OSGiManifestParser.getInstance(), null, bundleInfo, m, profileProvider);
+
+ try {
+ XmlModuleDescriptorWriter.write(md, ivyFile);
+ } catch (IOException e) {
+ throw new BuildException("The ivyFile '" + ivyFile + "' could not be written", e);
+ }
+ }
+
+}
diff --git a/src/java/org/apache/ivy/ant/FixDepsTask.java b/src/java/org/apache/ivy/ant/FixDepsTask.java
new file mode 100644
index 0000000..1be9222
--- /dev/null
+++ b/src/java/org/apache/ivy/ant/FixDepsTask.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.ant;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleId;
+import org.apache.ivy.core.report.ResolveReport;
+import org.apache.ivy.plugins.parser.xml.XmlModuleDescriptorWriter;
+import org.apache.tools.ant.BuildException;
+
+public class FixDepsTask extends IvyPostResolveTask {
+
+ private File dest;
+
+ private List/* <Keep> */keeps = new ArrayList();
+
+ public void setToFile(File dest) {
+ this.dest = dest;
+ }
+
+ public static class Keep {
+
+ private String org;
+
+ private String module;
+
+ public void setOrg(String org) {
+ this.org = org;
+ }
+
+ public void setModule(String module) {
+ this.module = module;
+ }
+ }
+
+ public Keep createKeep() {
+ Keep k = new Keep();
+ keeps.add(k);
+ return k;
+ }
+
+ public void doExecute() throws BuildException {
+ prepareAndCheck();
+
+ if (dest == null) {
+ throw new BuildException("Missing required parameter 'tofile'");
+ }
+ if (dest.exists() && dest.isDirectory()) {
+ throw new BuildException("The destination file '" + dest.getAbsolutePath()
+ + "' already exist and is a folder");
+ }
+
+ ResolveReport report = getResolvedReport();
+
+ List/* <ModuleId> */midToKeep = new ArrayList();
+ for (int i = 0; i < keeps.size(); i++) {
+ midToKeep.add(ModuleId.newInstance(((Keep) keeps.get(i)).org,
+ ((Keep) keeps.get(i)).module));
+ }
+
+ ModuleDescriptor md = report.toFixedModuleDescriptor(getSettings(), midToKeep);
+ try {
+ XmlModuleDescriptorWriter.write(md, dest);
+ } catch (IOException e) {
+ throw new BuildException("Failed to write into the file " + dest.getAbsolutePath()
+ + " (" + e.getMessage() + ")", e);
+ }
+ }
+
+}
diff --git a/src/java/org/apache/ivy/ant/IvyAntSettings.java b/src/java/org/apache/ivy/ant/IvyAntSettings.java
new file mode 100644
index 0000000..660ebbe
--- /dev/null
+++ b/src/java/org/apache/ivy/ant/IvyAntSettings.java
@@ -0,0 +1,391 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.ant;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.text.ParseException;
+import java.util.Properties;
+
+import org.apache.ivy.Ivy;
+import org.apache.ivy.core.settings.IvySettings;
+import org.apache.ivy.core.settings.IvyVariableContainer;
+import org.apache.ivy.util.Message;
+import org.apache.ivy.util.url.CredentialsStore;
+import org.apache.ivy.util.url.URLHandler;
+import org.apache.ivy.util.url.URLHandlerDispatcher;
+import org.apache.ivy.util.url.URLHandlerRegistry;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.Property;
+import org.apache.tools.ant.types.DataType;
+
+public class IvyAntSettings extends DataType {
+
+ public static class Credentials {
+ private String realm;
+
+ private String host;
+
+ private String username;
+
+ private String passwd;
+
+ public String getPasswd() {
+ return this.passwd;
+ }
+
+ public void setPasswd(String passwd) {
+ this.passwd = passwd;
+ }
+
+ public String getRealm() {
+ return this.realm;
+ }
+
+ public void setRealm(String realm) {
+ this.realm = format(realm);
+ }
+
+ public String getHost() {
+ return this.host;
+ }
+
+ public void setHost(String host) {
+ this.host = format(host);
+ }
+
+ public String getUsername() {
+ return this.username;
+ }
+
+ public void setUsername(String userName) {
+ this.username = format(userName);
+ }
+ }
+
+ private Ivy ivyEngine = null;
+
+ private File file = null;
+
+ private URL url = null;
+
+ private String realm = null;
+
+ private String host = null;
+
+ private String userName = null;
+
+ private String passwd = null;
+
+ private String id = "ivy.instance";
+
+ private boolean autoRegistered = false;
+
+ /**
+ * Returns the default ivy settings of this classloader. If it doesn't exist yet, a new one is
+ * created using the given project to back the VariableContainer.
+ *
+ * @param project
+ * TODO add text.
+ * @return An IvySetting instance.
+ */
+ public static IvyAntSettings getDefaultInstance(ProjectComponent task) {
+ Project project = task.getProject();
+ Object defaultInstanceObj = project.getReference("ivy.instance");
+ if (defaultInstanceObj != null
+ && defaultInstanceObj.getClass().getClassLoader() != IvyAntSettings.class
+ .getClassLoader()) {
+ task.log("ivy.instance reference an ivy:settings defined in an other classloader. "
+ + "An new default one will be used in this project.", Project.MSG_WARN);
+ defaultInstanceObj = null;
+ }
+ if (defaultInstanceObj != null && !(defaultInstanceObj instanceof IvyAntSettings)) {
+ throw new BuildException("ivy.instance reference a "
+ + defaultInstanceObj.getClass().getName()
+ + " an not an IvyAntSettings. Please don't use this reference id ()");
+ }
+ if (defaultInstanceObj == null) {
+ task.log("No ivy:settings found for the default reference 'ivy.instance'. "
+ + "A default instance will be used", Project.MSG_VERBOSE);
+
+ IvyAntSettings settings = new IvyAntSettings();
+ settings.setProject(project);
+ project.addReference("ivy.instance", settings);
+ settings.createIvyEngine(task);
+ return settings;
+ } else {
+ return (IvyAntSettings) defaultInstanceObj;
+ }
+ }
+
+ /*
+ * Keep this for backwards compatibility!
+ */
+ public static IvyAntSettings getDefaultInstance(Task task) {
+ return getDefaultInstance((ProjectComponent) task);
+ }
+
+ public File getFile() {
+ return file;
+ }
+
+ public URL getUrl() {
+ return url;
+ }
+
+ public String getPasswd() {
+ return passwd;
+ }
+
+ public void setPasswd(String aPasswd) {
+ passwd = aPasswd;
+ }
+
+ public String getRealm() {
+ return realm;
+ }
+
+ public void setRealm(String aRealm) {
+ realm = format(aRealm);
+ }
+
+ public String getHost() {
+ return host;
+ }
+
+ public void setHost(String aHost) {
+ host = format(aHost);
+ }
+
+ public String getUsername() {
+ return userName;
+ }
+
+ public void setUsername(String aUserName) {
+ userName = format(aUserName);
+ }
+
+ public void setProject(Project p) {
+ super.setProject(p);
+
+ if ("ivy.instance".equals(id) && !getProject().getReferences().containsKey(id)) {
+ // register ourselfs as default settings, just in case the id attribute is not set
+ getProject().addReference("ivy.instance", this);
+ autoRegistered = true;
+ }
+ }
+
+ private static String format(String str) {
+ return str == null ? str : (str.trim().length() == 0 ? null : str.trim());
+ }
+
+ public void addConfiguredCredentials(Credentials c) {
+ CredentialsStore.INSTANCE.addCredentials(c.getRealm(), c.getHost(), c.getUsername(),
+ c.getPasswd());
+ }
+
+ public void setFile(File file) {
+ this.file = file;
+ }
+
+ public void setUrl(String confUrl) throws MalformedURLException {
+ this.url = new URL(confUrl);
+ }
+
+ public void setUrl(URL url) {
+ this.url = url;
+ }
+
+ /*
+ * This is usually not necessary to define a reference in Ant, but it's the only way to know the
+ * id of the settings, which we use to set ant properties.
+ */
+ public void setId(String id) {
+ if (autoRegistered && getProject().getReference(this.id) == this) {
+ getProject().getReferences().remove(this.id);
+ autoRegistered = false;
+ }
+ this.id = id;
+
+ if (getProject() != null) {
+ getProject().addReference(this.id, this);
+ }
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ /**
+ * Return the configured Ivy instance.
+ *
+ * @return Returns the configured Ivy instance.
+ */
+ public Ivy getConfiguredIvyInstance(ProjectComponent task) {
+ if (ivyEngine == null) {
+ createIvyEngine(task);
+ }
+ return ivyEngine;
+ }
+
+ /*
+ * Keep this for backwards compatibility!
+ */
+ public Ivy getConfiguredIvyInstance(Task task) {
+ return getConfiguredIvyInstance((ProjectComponent) task);
+ }
+
+ void createIvyEngine(final ProjectComponent task) {
+ Project project = task.getProject();
+ Property prop = new Property() {
+ public void execute() throws BuildException {
+ addProperties(getDefaultProperties(task));
+ }
+ };
+ prop.setProject(project);
+ prop.init();
+ prop.execute();
+
+ IvyAntVariableContainer ivyAntVariableContainer = new IvyAntVariableContainer(project);
+ IvySettings settings = new IvySettings(ivyAntVariableContainer);
+ settings.setBaseDir(project.getBaseDir());
+
+ if (file == null && url == null) {
+ defineDefaultSettingFile(ivyAntVariableContainer, task);
+ }
+
+ Ivy ivy = Ivy.newInstance(settings);
+ try {
+ ivy.pushContext();
+ AntMessageLogger.register(task, ivy);
+
+ Message.showInfo();
+ configureURLHandler();
+ if (file != null) {
+ if (!file.exists()) {
+ throw new BuildException("settings file does not exist: " + file);
+ }
+ ivy.configure(file);
+ } else {
+ if (url == null) {
+ throw new AssertionError(
+ "ivy setting should have either a file, either an url,"
+ + " and if not defineDefaultSettingFile must set it.");
+ }
+ ivy.configure(url);
+ }
+ ivyAntVariableContainer.updateProject(id);
+ ivyEngine = ivy;
+ } catch (ParseException e) {
+ throw new BuildException("impossible to configure ivy:settings with given "
+ + (file != null ? "file: " + file : "url: " + url) + " : " + e, e);
+ } catch (IOException e) {
+ throw new BuildException("impossible to configure ivy:settings with given "
+ + (file != null ? "file: " + file : "url: " + url) + " : " + e, e);
+ } finally {
+ ivy.popContext();
+ }
+ }
+
+ protected Properties getDefaultProperties(ProjectComponent task) {
+ URL url = IvySettings.getDefaultPropertiesURL();
+ // this is copy of loadURL code from ant Property task (not available in 1.5.1)
+ Properties props = new Properties();
+ task.log("Loading " + url, Project.MSG_VERBOSE);
+ try {
+ InputStream is = url.openStream();
+ try {
+ props.load(is);
+ } finally {
+ if (is != null) {
+ is.close();
+ }
+ }
+ } catch (IOException ex) {
+ throw new BuildException(ex);
+ }
+ return props;
+ }
+
+ /**
+ * Set file or url to its default value
+ *
+ * @param variableContainer
+ */
+ private void defineDefaultSettingFile(IvyVariableContainer variableContainer,
+ ProjectComponent task) {
+ String settingsFileName = variableContainer.getVariable("ivy.conf.file");
+ if (settingsFileName != null
+ && !settingsFileName.equals(variableContainer.getVariable("ivy.settings.file"))) {
+ task.log("DEPRECATED: 'ivy.conf.file' is deprecated, use 'ivy.settings.file' instead",
+ Project.MSG_INFO);
+ } else {
+ settingsFileName = variableContainer.getVariable("ivy.settings.file");
+ }
+ File[] settingsLocations = new File[] {
+ new File(getProject().getBaseDir(), settingsFileName),
+ new File(getProject().getBaseDir(), "ivyconf.xml"), new File(settingsFileName),
+ new File("ivyconf.xml")};
+ for (int i = 0; i < settingsLocations.length; i++) {
+ file = settingsLocations[i];
+ task.log("searching settings file: trying " + file, Project.MSG_VERBOSE);
+ if (file.exists()) {
+ break;
+ }
+ }
+ if (!file.exists()) {
+ file = null;
+ if (Boolean.valueOf(getProject().getProperty("ivy.14.compatible")).booleanValue()) {
+ task.log("no settings file found, using Ivy 1.4 default...", Project.MSG_VERBOSE);
+ url = IvySettings.getDefault14SettingsURL();
+ } else {
+ String settingsFileUrl = variableContainer.getVariable("ivy.settings.url");
+ if (settingsFileUrl != null) {
+ try {
+ url = new URL(settingsFileUrl);
+ } catch (MalformedURLException e) {
+ throw new BuildException(
+ "Impossible to configure ivy:settings with given url: "
+ + settingsFileUrl + ": " + e.getMessage(), e);
+ }
+ } else {
+ task.log("no settings file found, using default...", Project.MSG_VERBOSE);
+ url = IvySettings.getDefaultSettingsURL();
+ }
+ }
+ }
+ }
+
+ private void configureURLHandler() {
+ // TODO : the credentialStore should also be scoped
+ CredentialsStore.INSTANCE.addCredentials(getRealm(), getHost(), getUsername(), getPasswd());
+
+ URLHandlerDispatcher dispatcher = new URLHandlerDispatcher();
+ URLHandler httpHandler = URLHandlerRegistry.getHttp();
+ dispatcher.setDownloader("http", httpHandler);
+ dispatcher.setDownloader("https", httpHandler);
+ URLHandlerRegistry.setDefault(dispatcher);
+ }
+
+}
diff --git a/src/java/org/apache/ivy/ant/IvyAntVariableContainer.java b/src/java/org/apache/ivy/ant/IvyAntVariableContainer.java
new file mode 100644
index 0000000..fa25a8d
--- /dev/null
+++ b/src/java/org/apache/ivy/ant/IvyAntVariableContainer.java
@@ -0,0 +1,106 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.ant;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.ivy.core.settings.IvyVariableContainer;
+import org.apache.ivy.core.settings.IvyVariableContainerImpl;
+import org.apache.ivy.util.Message;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Property;
+
+class IvyAntVariableContainer extends IvyVariableContainerImpl implements IvyVariableContainer {
+
+ private Map overwrittenProperties = new HashMap();
+
+ private Project project;
+
+ public IvyAntVariableContainer(Project project) {
+ this.project = project;
+ }
+
+ public String getVariable(String name) {
+ String r = (String) overwrittenProperties.get(name);
+ if (r == null) {
+ r = project.getProperty(name);
+ }
+ if (r == null) {
+ r = super.getVariable(name);
+ }
+ return r;
+ }
+
+ public void setVariable(String varName, String value, boolean overwrite) {
+ if (overwrite) {
+ Message.debug("setting '" + varName + "' to '" + value + "'");
+ overwrittenProperties.put(varName, substitute(value));
+ } else {
+ super.setVariable(varName, value, overwrite);
+ }
+ }
+
+ /**
+ * Updates the Ant Project used in this container with variables set in Ivy.
+ *
+ * All variables defined in Ivy will be set in the Ant project under two names:
+ * <ul>
+ * <li>the name of the variable</li>
+ * <li>the name of the variable suffxied with a dot + the given id, if the given id is not null</li>
+ * </ul>
+ *
+ * @param id
+ * The identifier of the settings in which the variables have been set, which should
+ * be used as property names suffix
+ */
+ public void updateProject(String id) {
+ Map r = new HashMap(super.getVariables());
+ r.putAll(overwrittenProperties);
+ for (Iterator it = r.entrySet().iterator(); it.hasNext();) {
+ Entry entry = (Entry) it.next();
+
+ setPropertyIfNotSet((String) entry.getKey(), (String) entry.getValue());
+ if (id != null) {
+ setPropertyIfNotSet((String) entry.getKey() + "." + id, (String) entry.getValue());
+ }
+ }
+
+ if (getEnvironmentPrefix() != null) {
+ Property propTask = new Property();
+ propTask.setProject(project);
+ propTask.setEnvironment(getEnvironmentPrefix());
+ propTask.init();
+ propTask.execute();
+ }
+ }
+
+ private void setPropertyIfNotSet(String property, String value) {
+ if (project.getProperty(property) == null) {
+ project.setProperty(property, value);
+ }
+ }
+
+ public Object clone() {
+ IvyAntVariableContainer result = (IvyAntVariableContainer) super.clone();
+ result.overwrittenProperties = (HashMap) ((HashMap) this.overwrittenProperties).clone();
+ return result;
+ }
+}
diff --git a/src/java/org/apache/ivy/ant/IvyArtifactProperty.java b/src/java/org/apache/ivy/ant/IvyArtifactProperty.java
new file mode 100644
index 0000000..5c598f9
--- /dev/null
+++ b/src/java/org/apache/ivy/ant/IvyArtifactProperty.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.ant;
+
+import java.io.File;
+
+import org.apache.ivy.core.IvyPatternHelper;
+import org.apache.ivy.core.cache.ResolutionCacheManager;
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.resolve.ResolveOptions;
+import org.apache.ivy.plugins.report.XmlReportParser;
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Set a set of ant properties according to the last artifact resolved
+ */
+public class IvyArtifactProperty extends IvyPostResolveTask {
+ private String name;
+
+ private String value;
+
+ private boolean overwrite = false;
+
+ public String getName() {
+ return this.name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getValue() {
+ return this.value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ public void setOverwrite(boolean overwrite) {
+ this.overwrite = overwrite;
+
+ }
+
+ public void doExecute() throws BuildException {
+ prepareAndCheck();
+
+ try {
+ ResolutionCacheManager cacheMgr = getIvyInstance().getResolutionCacheManager();
+ String[] confs = splitConfs(getConf());
+ String resolveId = getResolveId();
+ if (resolveId == null) {
+ resolveId = ResolveOptions.getDefaultResolveId(getResolvedModuleId());
+ }
+ XmlReportParser parser = new XmlReportParser();
+ for (int i = 0; i < confs.length; i++) {
+ File report = cacheMgr.getConfigurationResolveReportInCache(resolveId, confs[i]);
+ parser.parse(report);
+
+ Artifact[] artifacts = parser.getArtifacts();
+ for (int j = 0; j < artifacts.length; j++) {
+ Artifact artifact = artifacts[j];
+ String name = IvyPatternHelper.substitute(getSettings().substitute(getName()),
+ artifact, confs[i]);
+ String value = IvyPatternHelper.substitute(
+ getSettings().substitute(getValue()), artifact, confs[i]);
+ setProperty(name, value);
+ }
+ }
+ } catch (Exception ex) {
+ throw new BuildException("impossible to add artifact properties: " + ex, ex);
+ }
+ }
+
+ private void setProperty(String name, String value) {
+ if (overwrite) {
+ getProject().setProperty(name, value);
+ } else {
+ getProject().setNewProperty(name, value);
+ }
+ }
+}
diff --git a/src/java/org/apache/ivy/ant/IvyArtifactReport.java b/src/java/org/apache/ivy/ant/IvyArtifactReport.java
new file mode 100644
index 0000000..88520f1
--- /dev/null
+++ b/src/java/org/apache/ivy/ant/IvyArtifactReport.java
@@ -0,0 +1,281 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.ant;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.text.ParseException;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerFactoryConfigurationError;
+import javax.xml.transform.sax.SAXTransformerFactory;
+import javax.xml.transform.sax.TransformerHandler;
+import javax.xml.transform.stream.StreamResult;
+
+import org.apache.ivy.core.cache.ArtifactOrigin;
+import org.apache.ivy.core.cache.RepositoryCacheManager;
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.report.ArtifactDownloadReport;
+import org.apache.ivy.core.resolve.IvyNode;
+import org.apache.ivy.core.resolve.ResolveOptions;
+import org.apache.ivy.core.resolve.ResolvedModuleRevision;
+import org.apache.ivy.core.retrieve.RetrieveOptions;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+/**
+ * Generates a report of all artifacts involved during the last resolve.
+ */
+public class IvyArtifactReport extends IvyPostResolveTask {
+ private File tofile;
+
+ private String pattern;
+
+ public File getTofile() {
+ return tofile;
+ }
+
+ public void setTofile(File aFile) {
+ tofile = aFile;
+ }
+
+ public String getPattern() {
+ return pattern;
+ }
+
+ public void setPattern(String aPattern) {
+ pattern = aPattern;
+ }
+
+ public void doExecute() throws BuildException {
+ prepareAndCheck();
+ if (tofile == null) {
+ throw new BuildException(
+ "no destination file name: please provide it through parameter 'tofile'");
+ }
+
+ pattern = getProperty(pattern, getSettings(), "ivy.retrieve.pattern");
+
+ try {
+ String[] confs = splitConfs(getConf());
+ ModuleDescriptor md = null;
+ if (getResolveId() != null) {
+ md = (ModuleDescriptor) getResolvedDescriptor(getResolveId());
+ } else {
+ md = (ModuleDescriptor) getResolvedDescriptor(getOrganisation(), getModule(), false);
+ }
+ IvyNode[] dependencies = getIvyInstance().getResolveEngine().getDependencies(
+ md,
+ ((ResolveOptions) new ResolveOptions().setLog(getLog())).setConfs(confs)
+ .setResolveId(getResolveId()).setValidate(doValidate(getSettings())), null);
+
+ Map artifactsToCopy = getIvyInstance().getRetrieveEngine().determineArtifactsToCopy(
+ ModuleRevisionId.newInstance(getOrganisation(), getModule(), getRevision()),
+ pattern,
+ ((RetrieveOptions) new RetrieveOptions().setLog(getLog())).setConfs(confs)
+ .setResolveId(getResolveId()));
+
+ Map moduleRevToArtifactsMap = new HashMap();
+ for (Iterator iter = artifactsToCopy.keySet().iterator(); iter.hasNext();) {
+ ArtifactDownloadReport artifact = (ArtifactDownloadReport) iter.next();
+ Set moduleRevArtifacts = (Set) moduleRevToArtifactsMap.get(artifact.getArtifact()
+ .getModuleRevisionId());
+ if (moduleRevArtifacts == null) {
+ moduleRevArtifacts = new HashSet();
+ moduleRevToArtifactsMap.put(artifact.getArtifact().getModuleRevisionId(),
+ moduleRevArtifacts);
+ }
+ moduleRevArtifacts.add(artifact);
+ }
+
+ generateXml(dependencies, moduleRevToArtifactsMap, artifactsToCopy);
+ } catch (ParseException e) {
+ log(e.getMessage(), Project.MSG_ERR);
+ throw new BuildException("syntax errors in ivy file: " + e, e);
+ } catch (IOException e) {
+ throw new BuildException("impossible to generate report: " + e, e);
+ }
+ }
+
+ private void generateXml(IvyNode[] dependencies, Map moduleRevToArtifactsMap,
+ Map artifactsToCopy) {
+ try {
+ FileOutputStream fileOuputStream = new FileOutputStream(tofile);
+ try {
+ TransformerHandler saxHandler = createTransformerHandler(fileOuputStream);
+
+ saxHandler.startDocument();
+ saxHandler.startElement(null, "modules", "modules", new AttributesImpl());
+
+ for (int i = 0; i < dependencies.length; i++) {
+ IvyNode dependency = dependencies[i];
+ if (dependency.getModuleRevision() == null || dependency.isCompletelyEvicted()) {
+ continue;
+ }
+
+ startModule(saxHandler, dependency);
+
+ Set artifactsOfModuleRev = (Set) moduleRevToArtifactsMap.get(dependency
+ .getModuleRevision().getId());
+ if (artifactsOfModuleRev != null) {
+ for (Iterator iter = artifactsOfModuleRev.iterator(); iter.hasNext();) {
+ ArtifactDownloadReport artifact = (ArtifactDownloadReport) iter.next();
+
+ RepositoryCacheManager cache = dependency.getModuleRevision()
+ .getArtifactResolver().getRepositoryCacheManager();
+
+ startArtifact(saxHandler, artifact.getArtifact());
+
+ writeOriginLocationIfPresent(cache, saxHandler, artifact);
+ writeCacheLocationIfPresent(cache, saxHandler, artifact);
+
+ Set artifactDestPaths = (Set) artifactsToCopy.get(artifact);
+ for (Iterator iterator = artifactDestPaths.iterator(); iterator
+ .hasNext();) {
+ String artifactDestPath = (String) iterator.next();
+ writeRetrieveLocation(saxHandler, artifactDestPath);
+ }
+ saxHandler.endElement(null, "artifact", "artifact");
+ }
+ }
+ saxHandler.endElement(null, "module", "module");
+ }
+ saxHandler.endElement(null, "modules", "modules");
+ saxHandler.endDocument();
+ } finally {
+ fileOuputStream.close();
+ }
+ } catch (SAXException e) {
+ throw new BuildException("impossible to generate report", e);
+ } catch (TransformerConfigurationException e) {
+ throw new BuildException("impossible to generate report", e);
+ } catch (IOException e) {
+ throw new BuildException("impossible to generate report", e);
+ }
+ }
+
+ private TransformerHandler createTransformerHandler(FileOutputStream fileOuputStream)
+ throws TransformerFactoryConfigurationError, TransformerConfigurationException,
+ SAXException {
+ SAXTransformerFactory transformerFact = (SAXTransformerFactory) SAXTransformerFactory
+ .newInstance();
+ TransformerHandler saxHandler = transformerFact.newTransformerHandler();
+ saxHandler.getTransformer().setOutputProperty(OutputKeys.ENCODING, "UTF-8");
+ saxHandler.getTransformer().setOutputProperty(OutputKeys.INDENT, "yes");
+ saxHandler.setResult(new StreamResult(fileOuputStream));
+ return saxHandler;
+ }
+
+ private void startModule(TransformerHandler saxHandler, IvyNode dependency) throws SAXException {
+ AttributesImpl moduleAttrs = new AttributesImpl();
+ moduleAttrs.addAttribute(null, "organisation", "organisation", "CDATA", dependency
+ .getModuleId().getOrganisation());
+ moduleAttrs.addAttribute(null, "name", "name", "CDATA", dependency.getModuleId().getName());
+ ResolvedModuleRevision moduleRevision = dependency.getModuleRevision();
+ moduleAttrs.addAttribute(null, "rev", "rev", "CDATA", moduleRevision.getId().getRevision());
+ moduleAttrs.addAttribute(null, "status", "status", "CDATA", moduleRevision.getDescriptor()
+ .getStatus());
+ saxHandler.startElement(null, "module", "module", moduleAttrs);
+ }
+
+ private void startArtifact(TransformerHandler saxHandler, Artifact artifact)
+ throws SAXException {
+ AttributesImpl artifactAttrs = new AttributesImpl();
+ artifactAttrs.addAttribute(null, "name", "name", "CDATA", artifact.getName());
+ artifactAttrs.addAttribute(null, "ext", "ext", "CDATA", artifact.getExt());
+ artifactAttrs.addAttribute(null, "type", "type", "CDATA", artifact.getType());
+ saxHandler.startElement(null, "artifact", "artifact", artifactAttrs);
+ }
+
+ private void writeOriginLocationIfPresent(RepositoryCacheManager cache,
+ TransformerHandler saxHandler, ArtifactDownloadReport artifact) throws IOException,
+ SAXException {
+ ArtifactOrigin origin = artifact.getArtifactOrigin();
+ if (!ArtifactOrigin.isUnknown(origin)) {
+ String originName = origin.getLocation();
+ boolean isOriginLocal = origin.isLocal();
+
+ String originLocation;
+ AttributesImpl originLocationAttrs = new AttributesImpl();
+ if (isOriginLocal) {
+ originLocationAttrs.addAttribute(null, "is-local", "is-local", "CDATA", "true");
+ originLocation = originName.replace('\\', '/');
+ } else {
+ originLocationAttrs.addAttribute(null, "is-local", "is-local", "CDATA", "false");
+ originLocation = originName;
+ }
+ saxHandler
+ .startElement(null, "origin-location", "origin-location", originLocationAttrs);
+ char[] originLocationAsChars = originLocation.toCharArray();
+ saxHandler.characters(originLocationAsChars, 0, originLocationAsChars.length);
+ saxHandler.endElement(null, "origin-location", "origin-location");
+ }
+ }
+
+ private void writeCacheLocationIfPresent(RepositoryCacheManager cache,
+ TransformerHandler saxHandler, ArtifactDownloadReport artifact) throws SAXException {
+ File archiveInCache = artifact.getLocalFile();
+
+ if (archiveInCache != null) {
+ saxHandler.startElement(null, "cache-location", "cache-location", new AttributesImpl());
+ char[] archiveInCacheAsChars = archiveInCache.getPath().replace('\\', '/')
+ .toCharArray();
+ saxHandler.characters(archiveInCacheAsChars, 0, archiveInCacheAsChars.length);
+ saxHandler.endElement(null, "cache-location", "cache-location");
+ }
+ }
+
+ private void writeRetrieveLocation(TransformerHandler saxHandler, String artifactDestPath)
+ throws SAXException {
+ artifactDestPath = removeLeadingPath(getProject().getBaseDir(), new File(artifactDestPath));
+
+ saxHandler.startElement(null, "retrieve-location", "retrieve-location",
+ new AttributesImpl());
+ char[] artifactDestPathAsChars = artifactDestPath.replace('\\', '/').toCharArray();
+ saxHandler.characters(artifactDestPathAsChars, 0, artifactDestPathAsChars.length);
+ saxHandler.endElement(null, "retrieve-location", "retrieve-location");
+ }
+
+ // method largely inspired by ant 1.6.5 FileUtils method
+ public String removeLeadingPath(File leading, File path) {
+ String l = leading.getAbsolutePath();
+ String p = path.getAbsolutePath();
+ if (l.equals(p)) {
+ return "";
+ }
+
+ // ensure that l ends with a /
+ // so we never think /foo was a parent directory of /foobar
+ if (!l.endsWith(File.separator)) {
+ l += File.separator;
+ }
+ return (p.startsWith(l)) ? p.substring(l.length()) : p;
+ }
+
+}
diff --git a/src/java/org/apache/ivy/ant/IvyBuildList.java b/src/java/org/apache/ivy/ant/IvyBuildList.java
new file mode 100644
index 0000000..0620b49
--- /dev/null
+++ b/src/java/org/apache/ivy/ant/IvyBuildList.java
@@ -0,0 +1,569 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.ant;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+import org.apache.ivy.Ivy;
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleId;
+import org.apache.ivy.core.settings.IvySettings;
+import org.apache.ivy.core.sort.SortOptions;
+import org.apache.ivy.plugins.parser.ModuleDescriptorParserRegistry;
+import org.apache.ivy.util.Message;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.Path;
+
+/**
+ * Creates an ant filelist of files (usually build.xml) ordered according to the dependencies
+ * declared in ivy files.
+ */
+public class IvyBuildList extends IvyTask {
+ public static final class OnMissingDescriptor {
+ public static final String HEAD = "head";
+
+ public static final String TAIL = "tail";
+
+ public static final String SKIP = "skip";
+
+ public static final String FAIL = "fail";
+
+ public static final String WARN = "warn";
+
+ private OnMissingDescriptor() {
+ }
+ }
+
+ public static final String DESCRIPTOR_REQUIRED = "required";
+
+ private List buildFileSets = new ArrayList(); // List (FileSet)
+
+ private String reference;
+
+ private boolean haltOnError = true;
+
+ private String onMissingDescriptor = OnMissingDescriptor.HEAD;
+
+ private boolean reverse = false;
+
+ private String ivyFilePath;
+
+ private String root = "*";
+
+ private boolean excludeRoot = false;
+
+ private String leaf = "*";
+
+ private String delimiter = ",";
+
+ private boolean excludeLeaf = false;
+
+ private boolean onlydirectdep = false;
+
+ private String restartFrom = "*";
+
+ public void addFileset(FileSet buildFiles) {
+ buildFileSets.add(buildFiles);
+ }
+
+ public String getReference() {
+ return reference;
+ }
+
+ public void setReference(String reference) {
+ this.reference = reference;
+ }
+
+ public String getRoot() {
+ return root;
+ }
+
+ public void setRoot(String root) {
+ this.root = root;
+ }
+
+ public boolean isExcludeRoot() {
+ return excludeRoot;
+ }
+
+ public void setExcludeRoot(boolean root) {
+ excludeRoot = root;
+ }
+
+ public String getLeaf() {
+ return leaf;
+ }
+
+ public void setLeaf(String leaf) {
+ this.leaf = leaf;
+ }
+
+ public boolean isExcludeLeaf() {
+ return excludeLeaf;
+ }
+
+ public void setExcludeLeaf(boolean excludeLeaf) {
+ this.excludeLeaf = excludeLeaf;
+ }
+
+ public String getDelimiter() {
+ return delimiter;
+ }
+
+ public void setDelimiter(String delimiter) {
+ this.delimiter = delimiter;
+ }
+
+ public boolean getOnlydirectdep() {
+ return onlydirectdep;
+ }
+
+ public void setOnlydirectdep(boolean onlydirectdep) {
+ this.onlydirectdep = onlydirectdep;
+ }
+
+ public void doExecute() throws BuildException {
+ if (reference == null) {
+ throw new BuildException("reference should be provided in ivy build list");
+ }
+ if (buildFileSets.isEmpty()) {
+ throw new BuildException(
+ "at least one nested fileset should be provided in ivy build list");
+ }
+
+ Ivy ivy = getIvyInstance();
+ IvySettings settings = ivy.getSettings();
+
+ ivyFilePath = getProperty(ivyFilePath, settings, "ivy.buildlist.ivyfilepath");
+
+ Path path = new Path(getProject());
+
+ Map buildFiles = new HashMap(); // Map (ModuleDescriptor -> File buildFile)
+ List independent = new ArrayList();
+ List noDescriptor = new ArrayList();
+ Collection mds = new ArrayList();
+
+ Set rootModuleNames = new LinkedHashSet();
+ if (!"*".equals(root)) {
+ StringTokenizer st = new StringTokenizer(root, delimiter);
+ while (st.hasMoreTokens()) {
+ rootModuleNames.add(st.nextToken());
+ }
+ }
+
+ Set leafModuleNames = new LinkedHashSet();
+ if (!"*".equals(leaf)) {
+ StringTokenizer st = new StringTokenizer(leaf, delimiter);
+ while (st.hasMoreTokens()) {
+ leafModuleNames.add(st.nextToken());
+ }
+ }
+
+ Set restartFromModuleNames = new LinkedHashSet();
+ if (!"*".equals(restartFrom)) {
+ StringTokenizer st = new StringTokenizer(restartFrom, delimiter);
+ // Only accept one (first) module
+ restartFromModuleNames.add(st.nextToken());
+ }
+
+ for (ListIterator iter = buildFileSets.listIterator(); iter.hasNext();) {
+ FileSet fs = (FileSet) iter.next();
+ DirectoryScanner ds = fs.getDirectoryScanner(getProject());
+ String[] builds = ds.getIncludedFiles();
+ for (int i = 0; i < builds.length; i++) {
+ File buildFile = new File(ds.getBasedir(), builds[i]);
+ File ivyFile = getIvyFileFor(buildFile);
+ if (!ivyFile.exists()) {
+ onMissingDescriptor(buildFile, ivyFile, noDescriptor);
+ } else {
+ try {
+ ModuleDescriptor md = ModuleDescriptorParserRegistry.getInstance()
+ .parseDescriptor(settings, ivyFile.toURI().toURL(),
+ doValidate(settings));
+ buildFiles.put(md, buildFile);
+ mds.add(md);
+ Message.debug("Add " + md.getModuleRevisionId().getModuleId());
+ } catch (Exception ex) {
+ if (haltOnError) {
+ throw new BuildException("impossible to parse ivy file for "
+ + buildFile + ": ivyfile=" + ivyFile + " exception=" + ex, ex);
+ } else {
+ Message.warn("impossible to parse ivy file for " + buildFile
+ + ": ivyfile=" + ivyFile + " exception=" + ex.getMessage());
+ Message.info("\t=> adding it at the beginning of the path");
+ independent.add(buildFile);
+ }
+ }
+ }
+ }
+ }
+
+ List leafModuleDescriptors = convertModuleNamesToModuleDescriptors(mds, leafModuleNames,
+ "leaf");
+ List rootModuleDescriptors = convertModuleNamesToModuleDescriptors(mds, rootModuleNames,
+ "root");
+ List restartFromModuleDescriptors = convertModuleNamesToModuleDescriptors(mds,
+ restartFromModuleNames, "restartFrom");
+
+ if (!rootModuleDescriptors.isEmpty()) {
+ Message.info("Filtering modules based on roots " + rootModuleNames);
+ mds = filterModulesFromRoot(mds, rootModuleDescriptors);
+ }
+ if (!leafModuleDescriptors.isEmpty()) {
+ Message.info("Filtering modules based on leafs " + leafModuleNames);
+ mds = filterModulesFromLeaf(mds, leafModuleDescriptors);
+ }
+
+ List sortedModules = ivy.sortModuleDescriptors(mds, SortOptions.DEFAULT);
+
+ if (!OnMissingDescriptor.TAIL.equals(onMissingDescriptor)) {
+ for (ListIterator iter = noDescriptor.listIterator(); iter.hasNext();) {
+ File buildFile = (File) iter.next();
+ addBuildFile(path, buildFile);
+ }
+ }
+ for (ListIterator iter = independent.listIterator(); iter.hasNext();) {
+ File buildFile = (File) iter.next();
+ addBuildFile(path, buildFile);
+ }
+ if (isReverse()) {
+ Collections.reverse(sortedModules);
+ }
+ // Remove modules that are before the restartFrom point
+ // Independent modules (without valid ivy file) can not be addressed
+ // so they are not removed from build path.
+ if (!restartFromModuleDescriptors.isEmpty()) {
+ boolean foundRestartFrom = false;
+ List keptModules = new ArrayList();
+ ModuleDescriptor restartFromModuleDescriptor = (ModuleDescriptor) restartFromModuleDescriptors
+ .get(0);
+ for (ListIterator iter = sortedModules.listIterator(); iter.hasNext();) {
+ ModuleDescriptor md = (ModuleDescriptor) iter.next();
+ if (md.equals(restartFromModuleDescriptor)) {
+ foundRestartFrom = true;
+ }
+ if (foundRestartFrom) {
+ keptModules.add(md);
+ }
+ }
+ sortedModules = keptModules;
+ }
+ StringBuffer order = new StringBuffer();
+ for (ListIterator iter = sortedModules.listIterator(); iter.hasNext();) {
+ ModuleDescriptor md = (ModuleDescriptor) iter.next();
+ order.append(md.getModuleRevisionId().getModuleId());
+ if (iter.hasNext()) {
+ order.append(", ");
+ }
+ File buildFile = (File) buildFiles.get(md);
+ addBuildFile(path, buildFile);
+ }
+ if (OnMissingDescriptor.TAIL.equals(onMissingDescriptor)) {
+ for (ListIterator iter = noDescriptor.listIterator(); iter.hasNext();) {
+ File buildFile = (File) iter.next();
+ addBuildFile(path, buildFile);
+ }
+ }
+
+ getProject().addReference(getReference(), path);
+ getProject().setProperty("ivy.sorted.modules", order.toString());
+ }
+
+ private void onMissingDescriptor(File buildFile, File ivyFile, List noDescriptor) {
+ if (OnMissingDescriptor.SKIP.equals(onMissingDescriptor)) {
+ Message.debug("skipping " + buildFile + ": descriptor " + ivyFile + " doesn't exist");
+ } else if (OnMissingDescriptor.FAIL.equals(onMissingDescriptor)) {
+ throw new BuildException(
+ "a module has no module descriptor and onMissingDescriptor=fail. "
+ + "Build file: " + buildFile + ". Expected descriptor: " + ivyFile);
+ } else {
+ if (OnMissingDescriptor.WARN.equals(onMissingDescriptor)) {
+ Message.warn("a module has no module descriptor. " + "Build file: " + buildFile
+ + ". Expected descriptor: " + ivyFile);
+ }
+ Message.verbose("no descriptor for "
+ + buildFile
+ + ": descriptor="
+ + ivyFile
+ + ": adding it at the "
+ + (OnMissingDescriptor.TAIL.equals(onMissingDescriptor) ? "tail" : "head"
+ + " of the path"));
+ Message.verbose("\t(change onMissingDescriptor if you want to take another action");
+ noDescriptor.add(buildFile);
+ }
+ }
+
+ private List convertModuleNamesToModuleDescriptors(Collection mds, Set moduleNames, String kind) {
+ List result = new ArrayList();
+ Set foundModuleNames = new HashSet();
+
+ for (Iterator it = mds.iterator(); it.hasNext();) {
+ ModuleDescriptor md = (ModuleDescriptor) it.next();
+ String name = md.getModuleRevisionId().getModuleId().getName();
+ if (moduleNames.contains(name)) {
+ foundModuleNames.add(name);
+ result.add(md);
+ }
+ }
+
+ if (foundModuleNames.size() < moduleNames.size()) {
+ Set missingModules = new HashSet(moduleNames);
+ missingModules.removeAll(foundModuleNames);
+
+ StringBuffer missingNames = new StringBuffer();
+ String sep = "";
+ for (Iterator it = missingModules.iterator(); it.hasNext();) {
+ missingNames.append(sep);
+ missingNames.append(it.next());
+ sep = ", ";
+ }
+
+ throw new BuildException("unable to find " + kind + " module(s) "
+ + missingNames.toString() + " in build fileset");
+ }
+
+ return result;
+ }
+
+ /**
+ * Returns a collection of ModuleDescriptors that are conatined in the input collection of
+ * ModuleDescriptors and upon which the root module depends
+ *
+ * @param mds
+ * input collection of ModuleDescriptors
+ * @param rootmd
+ * root module
+ * @return filtered list of modules
+ */
+ private Collection filterModulesFromRoot(Collection mds, List rootmds) {
+ // Make a map of ModuleId objects -> ModuleDescriptors
+ Map moduleIdMap = new HashMap();
+ for (Iterator iter = mds.iterator(); iter.hasNext();) {
+ ModuleDescriptor md = ((ModuleDescriptor) iter.next());
+ moduleIdMap.put(md.getModuleRevisionId().getModuleId(), md);
+ }
+
+ // recursively process the nodes
+ Set toKeep = new LinkedHashSet();
+
+ Iterator it = rootmds.iterator();
+ while (it.hasNext()) {
+ ModuleDescriptor rootmd = (ModuleDescriptor) it.next();
+ processFilterNodeFromRoot(rootmd, toKeep, moduleIdMap);
+ // With the excluderoot attribute set to true, take the rootmd out of the toKeep set.
+ if (excludeRoot) {
+ // Only for logging purposes
+ Message.verbose("Excluded module "
+ + rootmd.getModuleRevisionId().getModuleId().getName());
+ } else {
+ toKeep.add(rootmd);
+ }
+ }
+
+ // just for logging
+ for (Iterator iter = toKeep.iterator(); iter.hasNext();) {
+ ModuleDescriptor md = ((ModuleDescriptor) iter.next());
+ Message.verbose("Kept module " + md.getModuleRevisionId().getModuleId().getName());
+ }
+
+ return toKeep;
+ }
+
+ /**
+ * Adds the current node to the toKeep collection and then processes the each of the direct
+ * dependencies of this node that appear in the moduleIdMap (indicating that the dependency is
+ * part of this BuildList)
+ *
+ * @param node
+ * the node to be processed
+ * @param toKeep
+ * the set of ModuleDescriptors that should be kept
+ * @param moduleIdMap
+ * reference mapping of moduleId to ModuleDescriptor that are part of the BuildList
+ */
+ private void processFilterNodeFromRoot(ModuleDescriptor node, Set toKeep, Map moduleIdMap) {
+ // toKeep.add(node);
+
+ DependencyDescriptor[] deps = node.getDependencies();
+ for (int i = 0; i < deps.length; i++) {
+ ModuleId id = deps[i].getDependencyId();
+ ModuleDescriptor md = (ModuleDescriptor) moduleIdMap.get(id);
+ // we test if this module id has a module descriptor, and if it isn't already in the
+ // toKeep Set, in which there's probably a circular dependency
+ if (md != null && !toKeep.contains(md)) {
+ toKeep.add(md);
+ if (!getOnlydirectdep()) {
+ processFilterNodeFromRoot(md, toKeep, moduleIdMap);
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns a collection of ModuleDescriptors that are conatined in the input collection of
+ * ModuleDescriptors which depends on the leaf module
+ *
+ * @param mds
+ * input collection of ModuleDescriptors
+ * @param leafmd
+ * leaf module
+ * @return filtered list of modules
+ */
+ private Collection filterModulesFromLeaf(Collection mds, List leafmds) {
+ // Make a map of ModuleId objects -> ModuleDescriptors
+ Map moduleIdMap = new HashMap();
+ for (Iterator iter = mds.iterator(); iter.hasNext();) {
+ ModuleDescriptor md = ((ModuleDescriptor) iter.next());
+ moduleIdMap.put(md.getModuleRevisionId().getModuleId(), md);
+ }
+
+ // recursively process the nodes
+ Set toKeep = new LinkedHashSet();
+ Iterator it = leafmds.iterator();
+ while (it.hasNext()) {
+ ModuleDescriptor leafmd = (ModuleDescriptor) it.next();
+ // With the excludeleaf attribute set to true, take the rootmd out of the toKeep set.
+ if (excludeLeaf) {
+ Message.verbose("Excluded module "
+ + leafmd.getModuleRevisionId().getModuleId().getName());
+ } else {
+ toKeep.add(leafmd);
+ }
+ processFilterNodeFromLeaf(leafmd, toKeep, moduleIdMap);
+ }
+
+ // just for logging
+ for (Iterator iter = toKeep.iterator(); iter.hasNext();) {
+ ModuleDescriptor md = ((ModuleDescriptor) iter.next());
+ Message.verbose("Kept module " + md.getModuleRevisionId().getModuleId().getName());
+ }
+
+ return toKeep;
+ }
+
+ /**
+ * Search in the moduleIdMap modules depending on node, add them to the toKeep set and process
+ * them recursively.
+ *
+ * @param node
+ * the node to be processed
+ * @param toKeep
+ * the set of ModuleDescriptors that should be kept
+ * @param moduleIdMap
+ * reference mapping of moduleId to ModuleDescriptor that are part of the BuildList
+ */
+ private void processFilterNodeFromLeaf(ModuleDescriptor node, Set toKeep, Map moduleIdMap) {
+ for (Iterator iter = moduleIdMap.values().iterator(); iter.hasNext();) {
+ ModuleDescriptor md = (ModuleDescriptor) iter.next();
+ DependencyDescriptor[] deps = md.getDependencies();
+ for (int i = 0; i < deps.length; i++) {
+ ModuleId id = deps[i].getDependencyId();
+ if (node.getModuleRevisionId().getModuleId().equals(id) && !toKeep.contains(md)) {
+ toKeep.add(md);
+ if (!getOnlydirectdep()) {
+ processFilterNodeFromLeaf(md, toKeep, moduleIdMap);
+ }
+ }
+ }
+ }
+ }
+
+ private void addBuildFile(Path path, File buildFile) {
+ FileSet fs = new FileSet();
+ fs.setFile(buildFile);
+ path.addFileset(fs);
+ }
+
+ private File getIvyFileFor(File buildFile) {
+ return new File(buildFile.getParentFile(), ivyFilePath);
+ }
+
+ public boolean isHaltonerror() {
+ return haltOnError;
+ }
+
+ public void setHaltonerror(boolean haltOnError) {
+ this.haltOnError = haltOnError;
+ }
+
+ public String getIvyfilepath() {
+ return ivyFilePath;
+ }
+
+ public void setIvyfilepath(String ivyFilePath) {
+ this.ivyFilePath = ivyFilePath;
+ }
+
+ public String getOnMissingDescriptor() {
+ return onMissingDescriptor;
+ }
+
+ public void setOnMissingDescriptor(String onMissingDescriptor) {
+ this.onMissingDescriptor = onMissingDescriptor;
+ }
+
+ /**
+ * @deprecated use {@link #getOnMissingDescriptor()} instead.
+ */
+ @Deprecated
+ public boolean isSkipbuildwithoutivy() {
+ return onMissingDescriptor == OnMissingDescriptor.SKIP;
+ }
+
+ /**
+ * @deprecated use {@link #setOnMissingDescriptor(String)} instead.
+ */
+ @Deprecated
+ public void setSkipbuildwithoutivy(boolean skipBuildFilesWithoutIvy) {
+ Message.deprecated("skipbuildwithoutivy is deprecated, use onMissingDescriptor instead.");
+ this.onMissingDescriptor = skipBuildFilesWithoutIvy ? OnMissingDescriptor.SKIP
+ : OnMissingDescriptor.FAIL;
+ }
+
+ public boolean isReverse() {
+ return reverse;
+ }
+
+ public void setReverse(boolean reverse) {
+ this.reverse = reverse;
+ }
+
+ public String getRestartFrom() {
+ return restartFrom;
+ }
+
+ public void setRestartFrom(String restartFrom) {
+ this.restartFrom = restartFrom;
+ }
+
+}
diff --git a/src/java/org/apache/ivy/ant/IvyBuildNumber.java b/src/java/org/apache/ivy/ant/IvyBuildNumber.java
new file mode 100644
index 0000000..7f7ce7d
--- /dev/null
+++ b/src/java/org/apache/ivy/ant/IvyBuildNumber.java
@@ -0,0 +1,386 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.ant;
+
+import java.util.List;
+import java.util.ListIterator;
+
+import org.apache.ivy.Ivy;
+import org.apache.ivy.core.module.id.ModuleId;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.resolve.ResolvedModuleRevision;
+import org.apache.ivy.core.search.SearchEngine;
+import org.apache.ivy.core.settings.IvySettings;
+import org.apache.ivy.plugins.latest.ArtifactInfo;
+import org.apache.ivy.plugins.latest.LatestStrategy;
+import org.apache.ivy.plugins.matcher.ExactOrRegexpPatternMatcher;
+import org.apache.ivy.plugins.matcher.ExactPatternMatcher;
+import org.apache.ivy.plugins.matcher.Matcher;
+import org.apache.ivy.plugins.matcher.PatternMatcher;
+import org.apache.ivy.plugins.resolver.DependencyResolver;
+import org.apache.ivy.plugins.version.VersionMatcher;
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Look for the latest module in the repository matching the given criteria, and sets a set of
+ * properties according to what was found.
+ */
+public class IvyBuildNumber extends IvyTask {
+
+ public static class ResolvedModuleRevisionArtifactInfo implements ArtifactInfo {
+ private ModuleRevisionId rmr;
+
+ public ResolvedModuleRevisionArtifactInfo(ModuleRevisionId rmr) {
+ this.rmr = rmr;
+ }
+
+ public String getRevision() {
+ return rmr.getRevision();
+ }
+
+ public long getLastModified() {
+ return -1;
+ }
+
+ }
+
+ private String organisation;
+
+ private String module;
+
+ private String branch;
+
+ private String revision;
+
+ private String revSep = ".";
+
+ private String prefix = "ivy.";
+
+ private String defaultValue = "0";
+
+ private String defaultBuildNumber = "0";
+
+ private String resolver = null;
+
+ public String getModule() {
+ return module;
+ }
+
+ public void setModule(String module) {
+ this.module = module;
+ }
+
+ public String getOrganisation() {
+ return organisation;
+ }
+
+ public void setOrganisation(String organisation) {
+ this.organisation = organisation;
+ }
+
+ public String getRevision() {
+ return revision;
+ }
+
+ public void setRevision(String revision) {
+ this.revision = revision;
+ }
+
+ public String getBranch() {
+ return branch;
+ }
+
+ public void setBranch(String branch) {
+ this.branch = branch;
+ }
+
+ public String getDefault() {
+ return defaultValue;
+ }
+
+ public void setDefault(String default1) {
+ defaultValue = default1;
+ }
+
+ public String getResolver() {
+ return resolver;
+ }
+
+ public void setResolver(String resolver) {
+ this.resolver = resolver;
+ }
+
+ public String getPrefix() {
+ return prefix;
+ }
+
+ public void setPrefix(String prefix) {
+ this.prefix = prefix;
+ }
+
+ public void doExecute() throws BuildException {
+ if (organisation == null) {
+ throw new BuildException("no organisation provided for ivy buildnumber task");
+ }
+ if (module == null) {
+ throw new BuildException("no module name provided for ivy buildnumber task");
+ }
+ if (prefix == null) {
+ throw new BuildException("null prefix not allowed");
+ }
+
+ Ivy ivy = getIvyInstance();
+ IvySettings settings = ivy.getSettings();
+ if (branch == null) {
+ branch = settings.getDefaultBranch(new ModuleId(organisation, module));
+ }
+ if (revision == null || revision.length() == 0) {
+ revision = "latest.integration";
+ } else if (!revision.endsWith("+")) {
+ revision = revision + "+";
+ }
+ if (!prefix.endsWith(".") && prefix.length() > 0) {
+ prefix = prefix + ".";
+ }
+
+ SearchEngine searcher = new SearchEngine(settings);
+
+ PatternMatcher patternMatcher = new PatternMatcher() {
+ private PatternMatcher exact = new ExactPatternMatcher();
+
+ private PatternMatcher regexp = new ExactOrRegexpPatternMatcher();
+
+ public Matcher getMatcher(String expression) {
+ if (expression.equals(organisation) || expression.equals(module)
+ || expression.equals(branch)) {
+ return exact.getMatcher(expression);
+ }
+ return regexp.getMatcher(expression);
+ }
+
+ public String getName() {
+ return "buildnumber-matcher";
+ }
+ };
+ ModuleRevisionId[] revisions;
+ if (resolver == null) {
+ revisions = searcher.listModules(
+ ModuleRevisionId.newInstance(organisation, module, branch, ".*"), patternMatcher);
+ } else {
+ DependencyResolver depResolver = settings.getResolver(resolver);
+ if (depResolver == null) {
+ throw new BuildException("Unknown resolver: " + resolver);
+ }
+ revisions = searcher.listModules(depResolver,
+ ModuleRevisionId.newInstance(organisation, module, branch, ".*"), patternMatcher);
+ }
+
+ ArtifactInfo[] infos = new ArtifactInfo[revisions.length];
+ for (int i = 0; i < revisions.length; i++) {
+ infos[i] = new ResolvedModuleRevisionArtifactInfo(revisions[i]);
+ }
+
+ VersionMatcher matcher = settings.getVersionMatcher();
+ LatestStrategy latestStrategy = settings.getLatestStrategy("latest-revision");
+ List sorted = latestStrategy.sort(infos);
+
+ ModuleRevisionId askedMrid = ModuleRevisionId.newInstance(organisation, module, branch,
+ revision);
+
+ String foundRevision = null;
+ for (ListIterator iter = sorted.listIterator(sorted.size()); iter.hasPrevious();) {
+ ResolvedModuleRevisionArtifactInfo info = (ResolvedModuleRevisionArtifactInfo) iter
+ .previous();
+
+ if (!matcher.accept(askedMrid, info.rmr)) {
+ continue;
+ }
+
+ if (matcher.needModuleDescriptor(askedMrid, info.rmr)) {
+ ResolvedModuleRevision rmr = ivy.findModule(info.rmr);
+ if (matcher.accept(askedMrid, rmr.getDescriptor())) {
+ foundRevision = info.rmr.getRevision();
+ }
+ } else {
+ foundRevision = info.rmr.getRevision();
+ }
+
+ if (foundRevision != null) {
+ break;
+ }
+ }
+
+ NewRevision newRevision = computeNewRevision(foundRevision);
+ setProperty("revision", newRevision.getRevision());
+ setProperty("new.revision", newRevision.getNewRevision());
+ setProperty("build.number", newRevision.getBuildNumber());
+ setProperty("new.build.number", newRevision.getNewBuildNumber());
+ }
+
+ private void setProperty(String propertyName, String value) {
+ if (value != null) {
+ getProject().setProperty(prefix + propertyName, value);
+ }
+ }
+
+ private NewRevision computeNewRevision(String revision) {
+ String revPrefix = "latest.integration".equals(this.revision) ? "" : this.revision
+ .substring(0, this.revision.length() - 1);
+ if (revision != null && !revision.startsWith(revPrefix)) {
+ throw new BuildException("invalid exception found in repository: '" + revision
+ + "' for '" + revPrefix + "'");
+ }
+ if (revision == null) {
+ if (revPrefix.length() > 0) {
+ return new NewRevision(revision, revPrefix
+ + (revPrefix.endsWith(revSep) ? defaultBuildNumber : revSep
+ + defaultBuildNumber), null, defaultBuildNumber);
+ } else {
+ Range r = findLastNumber(defaultValue);
+ if (r == null) { // no number found
+ return new NewRevision(revision, defaultValue, null, null);
+ } else {
+ long n = Long.parseLong(defaultValue.substring(r.getStartIndex(),
+ r.getEndIndex()));
+ return new NewRevision(revision, defaultValue, null, String.valueOf(n));
+ }
+ }
+ }
+ Range r;
+ if (revPrefix.length() == 0) {
+ r = findLastNumber(revision);
+ if (r == null) {
+ return new NewRevision(revision, revision
+ + (revision.endsWith(revSep) ? "1" : revSep + "1"), null, "1");
+ }
+ } else {
+ r = findFirstNumber(revision, revPrefix.length());
+ if (r == null) {
+ return new NewRevision(revision, revPrefix
+ + (revPrefix.endsWith(revSep) ? "1" : revSep + "1"), null, "1");
+ }
+ }
+ long n = Long.parseLong(revision.substring(r.getStartIndex(), r.getEndIndex())) + 1;
+ return new NewRevision(revision, revision.substring(0, r.getStartIndex()) + n,
+ String.valueOf(n - 1), String.valueOf(n));
+ }
+
+ private Range findFirstNumber(String str, int startIndex) {
+ // let's find the first digit in the string
+ int startNumberIndex = startIndex;
+ while (startNumberIndex < str.length() && !Character.isDigit(str.charAt(startNumberIndex))) {
+ startNumberIndex++;
+ }
+ if (startNumberIndex == str.length()) {
+ return null;
+ }
+ // let's find the end of the number
+ int endNumberIndex = startNumberIndex + 1;
+ while (endNumberIndex < str.length() && Character.isDigit(str.charAt(endNumberIndex))) {
+ endNumberIndex++;
+ }
+ return new Range(startNumberIndex, endNumberIndex);
+ }
+
+ private Range findLastNumber(String str) {
+ int endNumberIndex = str.length() - 1;
+ while (endNumberIndex >= 0 && !Character.isDigit(str.charAt(endNumberIndex))) {
+ endNumberIndex--;
+ }
+ int startNumberIndex = endNumberIndex == -1 ? -1 : endNumberIndex - 1;
+ while (startNumberIndex >= 0 && Character.isDigit(str.charAt(startNumberIndex))) {
+ startNumberIndex--;
+ }
+ endNumberIndex++;
+ startNumberIndex++;
+ if (startNumberIndex == endNumberIndex) { // no number found
+ return null;
+ } else {
+ return new Range(startNumberIndex, endNumberIndex);
+ }
+ }
+
+ private static class Range {
+ private int startIndex;
+
+ private int endIndex;
+
+ public Range(int startIndex, int endIndex) {
+ this.startIndex = startIndex;
+ this.endIndex = endIndex;
+ }
+
+ public int getStartIndex() {
+ return startIndex;
+ }
+
+ public int getEndIndex() {
+ return endIndex;
+ }
+ }
+
+ private static class NewRevision {
+ private String revision;
+
+ private String newRevision;
+
+ private String buildNumber;
+
+ private String newBuildNumber;
+
+ public NewRevision(String revision, String newRevision, String buildNumber,
+ String newBuildNumber) {
+ this.revision = revision;
+ this.newRevision = newRevision;
+ this.buildNumber = buildNumber;
+ this.newBuildNumber = newBuildNumber;
+ }
+
+ public String getRevision() {
+ return revision;
+ }
+
+ public String getNewRevision() {
+ return newRevision;
+ }
+
+ public String getBuildNumber() {
+ return buildNumber;
+ }
+
+ public String getNewBuildNumber() {
+ return newBuildNumber;
+ }
+ }
+
+ public String getRevSep() {
+ return revSep;
+ }
+
+ public void setRevSep(String revSep) {
+ this.revSep = revSep;
+ }
+
+ public String getDefaultBuildNumber() {
+ return defaultBuildNumber;
+ }
+
+ public void setDefaultBuildNumber(String defaultBuildNumber) {
+ this.defaultBuildNumber = defaultBuildNumber;
+ }
+}
diff --git a/src/java/org/apache/ivy/ant/IvyCacheFileset.java b/src/java/org/apache/ivy/ant/IvyCacheFileset.java
new file mode 100644
index 0000000..891a336
--- /dev/null
+++ b/src/java/org/apache/ivy/ant/IvyCacheFileset.java
@@ -0,0 +1,204 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.ant;
+
+import java.io.File;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+import org.apache.ivy.core.report.ArtifactDownloadReport;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.PatternSet.NameEntry;
+
+/**
+ * Creates an ant fileset consisting in all artifacts found during a resolve. Note that this task is
+ * not compatible with the useOrigin mode.
+ */
+public class IvyCacheFileset extends IvyCacheTask {
+ private String setid;
+
+ public String getSetid() {
+ return setid;
+ }
+
+ public void setSetid(String id) {
+ setid = id;
+ }
+
+ public void setUseOrigin(boolean useOrigin) {
+ if (useOrigin) {
+ throw new UnsupportedOperationException(
+ "the cachefileset task does not support the useOrigin mode, since filesets "
+ + "require to have only one root directory. Please use the the cachepath "
+ + "task instead");
+ }
+ }
+
+ public void doExecute() throws BuildException {
+ prepareAndCheck();
+ if (setid == null) {
+ throw new BuildException("setid is required in ivy cachefileset");
+ }
+ try {
+ List paths = getArtifactReports();
+ File base = null;
+ for (Iterator iter = paths.iterator(); iter.hasNext();) {
+ ArtifactDownloadReport a = (ArtifactDownloadReport) iter.next();
+ if (a.getLocalFile() != null) {
+ base = getBaseDir(base, a.getLocalFile());
+ }
+ }
+
+ FileSet fileset;
+ if (base == null) {
+ fileset = new EmptyFileSet();
+ } else {
+ fileset = new FileSet();
+ fileset.setDir(base);
+ for (Iterator iter = paths.iterator(); iter.hasNext();) {
+ ArtifactDownloadReport a = (ArtifactDownloadReport) iter.next();
+ if (a.getLocalFile() != null) {
+ NameEntry ne = fileset.createInclude();
+ ne.setName(getPath(base, a.getLocalFile()));
+ }
+ }
+ }
+
+ fileset.setProject(getProject());
+ getProject().addReference(setid, fileset);
+ } catch (Exception ex) {
+ throw new BuildException("impossible to build ivy cache fileset: " + ex, ex);
+ }
+ }
+
+ /**
+ * Returns the path of the file relative to the given base directory.
+ *
+ * @param base
+ * the parent directory to which the file must be evaluated.
+ * @param file
+ * the file for which the path should be returned
+ * @return the path of the file relative to the given base directory.
+ */
+ private String getPath(File base, File file) {
+ String absoluteBasePath = base.getAbsolutePath();
+
+ int beginIndex = absoluteBasePath.length();
+
+ // checks if the basePath ends with the file separator (which can for instance
+ // happen if the basePath is the root on unix)
+ if (!absoluteBasePath.endsWith(File.separator)) {
+ beginIndex++; // skip the seperator char as well
+ }
+
+ return file.getAbsolutePath().substring(beginIndex);
+ }
+
+ /**
+ * Returns the common base directory between a current base directory and a given file.
+ * <p>
+ * The returned base directory must be a parent of both the current base and the given file.
+ * </p>
+ *
+ * @param base
+ * the current base directory, may be null.
+ * @param file
+ * the file for which the new base directory should be returned.
+ * @return the common base directory between a current base directory and a given file.
+ */
+ File getBaseDir(File base, File file) {
+ if (base == null) {
+ return file.getParentFile().getAbsoluteFile();
+ } else {
+ Iterator bases = getParents(base).iterator();
+ Iterator fileParents = getParents(file.getAbsoluteFile()).iterator();
+ File result = null;
+ while (bases.hasNext() && fileParents.hasNext()) {
+ File next = (File) bases.next();
+ if (next.equals(fileParents.next())) {
+ result = next;
+ } else {
+ break;
+ }
+ }
+ return result;
+ }
+ }
+
+ /**
+ * @return a list of files, starting with the root and ending with the file itself
+ */
+ private LinkedList/* <File> */getParents(File file) {
+ LinkedList r = new LinkedList();
+ while (file != null) {
+ r.addFirst(file);
+ file = file.getParentFile();
+ }
+ return r;
+ }
+
+ private static class EmptyFileSet extends FileSet {
+
+ private DirectoryScanner ds = new EmptyDirectoryScanner();
+
+ public Iterator iterator() {
+ return new EmptyIterator();
+ }
+
+ public Object clone() {
+ return new EmptyFileSet();
+ }
+
+ public int size() {
+ return 0;
+ }
+
+ public DirectoryScanner getDirectoryScanner(Project project) {
+ return ds;
+ }
+ }
+
+ private static class EmptyIterator implements Iterator {
+
+ public boolean hasNext() {
+ return false;
+ }
+
+ public Object next() {
+ throw new NoSuchElementException("EmptyFileSet Iterator");
+ }
+
+ public void remove() {
+ throw new IllegalStateException("EmptyFileSet Iterator");
+ }
+
+ }
+
+ private static class EmptyDirectoryScanner extends DirectoryScanner {
+
+ public String[] getIncludedFiles() {
+ return new String[0];
+ }
+
+ }
+}
diff --git a/src/java/org/apache/ivy/ant/IvyCachePath.java b/src/java/org/apache/ivy/ant/IvyCachePath.java
new file mode 100644
index 0000000..3fe7645
--- /dev/null
+++ b/src/java/org/apache/ivy/ant/IvyCachePath.java
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.ant;
+
+import java.io.File;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.ivy.core.report.ArtifactDownloadReport;
+import org.apache.ivy.osgi.core.BundleInfo;
+import org.apache.ivy.osgi.core.ManifestParser;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Path;
+
+/**
+ * Creates an ant path consisting in all artifacts found during a resolve.
+ */
+public class IvyCachePath extends IvyCacheTask {
+
+ private String pathid;
+
+ private String id;
+
+ private boolean osgi = false;
+
+ public String getPathid() {
+ return pathid;
+ }
+
+ public void setPathid(String id) {
+ pathid = id;
+ }
+
+ public void setOsgi(boolean osgi) {
+ this.osgi = osgi;
+ }
+
+ /**
+ * @deprecated use setPathid instead
+ * @param id
+ */
+ @Deprecated
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public void doExecute() throws BuildException {
+ prepareAndCheck();
+ if (pathid == null) {
+ if (id != null) {
+ pathid = id;
+ log("ID IS DEPRECATED, PLEASE USE PATHID INSTEAD", Project.MSG_WARN);
+ } else {
+ throw new BuildException("pathid is required in ivy classpath");
+ }
+ }
+ try {
+ Path path = new Path(getProject());
+ getProject().addReference(pathid, path);
+ for (Iterator iter = getArtifactReports().iterator(); iter.hasNext();) {
+ ArtifactDownloadReport a = (ArtifactDownloadReport) iter.next();
+ File f = a.getLocalFile();
+ if (a.getUnpackedLocalFile() != null) {
+ f = a.getUnpackedLocalFile();
+ }
+ addToPath(path, f);
+ }
+ } catch (Exception ex) {
+ throw new BuildException("impossible to build ivy path: " + ex, ex);
+ }
+
+ }
+
+ protected void addToPath(Path path, File f) throws Exception {
+ if (!osgi || !f.isDirectory()) {
+ path.createPathElement().setLocation(f);
+ return;
+ }
+ File manifest = new File(f, "META-INF/MANIFEST.MF");
+ if (!manifest.exists()) {
+ path.createPathElement().setLocation(f);
+ return;
+ }
+ BundleInfo bundleInfo = ManifestParser.parseManifest(manifest);
+ List/* <String> */cp = bundleInfo.getClasspath();
+ if (cp == null) {
+ path.createPathElement().setLocation(f);
+ return;
+ }
+ for (int i = 0; i < cp.size(); i++) {
+ String p = (String) cp.get(i);
+ if (p.equals(".")) {
+ path.createPathElement().setLocation(f);
+ } else {
+ path.createPathElement().setLocation(new File(f, p));
+ }
+ }
+ }
+
+}
diff --git a/src/java/org/apache/ivy/ant/IvyCacheTask.java b/src/java/org/apache/ivy/ant/IvyCacheTask.java
new file mode 100644
index 0000000..2360860
--- /dev/null
+++ b/src/java/org/apache/ivy/ant/IvyCacheTask.java
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.ant;
+
+import java.io.File;
+import java.io.IOException;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.ivy.core.cache.ResolutionCacheManager;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.report.ArtifactDownloadReport;
+import org.apache.ivy.core.report.ConfigurationResolveReport;
+import org.apache.ivy.core.report.ResolveReport;
+import org.apache.ivy.core.resolve.ResolveOptions;
+import org.apache.ivy.plugins.report.XmlReportParser;
+import org.apache.ivy.util.Message;
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Base class for the cache path related classes: cachepath and cachefileset. Most of the behviour
+ * is common to the two, since only the produced element differs.
+ */
+public abstract class IvyCacheTask extends IvyPostResolveTask {
+
+ protected List<ArtifactDownloadReport> getArtifactReports() throws BuildException,
+ ParseException, IOException {
+ Collection<ArtifactDownloadReport> artifacts = getAllArtifactReports();
+ List<ArtifactDownloadReport> ret = new ArrayList<ArtifactDownloadReport>();
+ for (ArtifactDownloadReport artifactReport : artifacts) {
+ if (getArtifactFilter().accept(artifactReport.getArtifact())) {
+ ret.add(artifactReport);
+ }
+ }
+
+ return ret;
+ }
+
+ private Collection<ArtifactDownloadReport> getAllArtifactReports() throws ParseException,
+ IOException {
+ String[] confs = splitConfs(getConf());
+ Collection<ArtifactDownloadReport> all = new LinkedHashSet<ArtifactDownloadReport>();
+
+ ResolveReport report = getResolvedReport();
+ if (report != null) {
+ Message.debug("using internal report instance to get artifacts list");
+ for (int i = 0; i < confs.length; i++) {
+ ConfigurationResolveReport configurationReport = report
+ .getConfigurationReport(confs[i]);
+ if (configurationReport == null) {
+ throw new BuildException("bad confs provided: " + confs[i]
+ + " not found among " + Arrays.asList(report.getConfigurations()));
+ }
+ Set<ModuleRevisionId> revisions = configurationReport.getModuleRevisionIds();
+ for (ModuleRevisionId revId : revisions) {
+ ArtifactDownloadReport[] aReports = configurationReport
+ .getDownloadReports(revId);
+ all.addAll(Arrays.asList(aReports));
+ }
+ }
+ } else {
+ Message.debug("using stored report to get artifacts list");
+
+ XmlReportParser parser = new XmlReportParser();
+ ResolutionCacheManager cacheMgr = getIvyInstance().getResolutionCacheManager();
+ String resolvedId = getResolveId();
+ if (resolvedId == null) {
+ resolvedId = ResolveOptions.getDefaultResolveId(getResolvedModuleId());
+ }
+ for (int i = 0; i < confs.length; i++) {
+ File reportFile = cacheMgr.getConfigurationResolveReportInCache(resolvedId,
+ confs[i]);
+ parser.parse(reportFile);
+
+ ArtifactDownloadReport[] aReports = parser.getArtifactReports();
+ all.addAll(Arrays.asList(aReports));
+ }
+ }
+ return all;
+ }
+}
diff --git a/src/java/org/apache/ivy/ant/IvyCheck.java b/src/java/org/apache/ivy/ant/IvyCheck.java
new file mode 100644
index 0000000..c101274
--- /dev/null
+++ b/src/java/org/apache/ivy/ant/IvyCheck.java
@@ -0,0 +1,98 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.ant;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.ivy.Ivy;
+import org.apache.ivy.util.Message;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.types.FileSet;
+
+/**
+ * Checks the given ivy file using current configuration to see if all dependencies are available,
+ * with good confs. If a resolver name is given, it also checks that the declared publications are
+ * available in the corresponding resolver. Note that the check is not performed recursively, i.e.
+ * if a dependency has itself dependencies badly described or not available, this check will not
+ * discover it.
+ */
+public class IvyCheck extends IvyTask {
+ private File file = null;
+
+ private List filesets = new ArrayList();
+
+ private String resolvername;
+
+ public File getFile() {
+ return file;
+ }
+
+ public void setFile(File file) {
+ this.file = file;
+ }
+
+ /**
+ * Adds a set of files to check.
+ *
+ * @param set
+ * a set of files to check
+ */
+ public void addFileset(FileSet set) {
+ filesets.add(set);
+ }
+
+ public String getResolvername() {
+ return resolvername;
+ }
+
+ public void setResolvername(String resolverName) {
+ resolvername = resolverName;
+ }
+
+ public void doExecute() throws BuildException {
+ try {
+ Ivy ivy = getIvyInstance();
+ if (file != null) {
+ if (ivy.check(file.toURI().toURL(), resolvername)) {
+ Message.verbose("checked " + file + ": OK");
+ }
+ }
+ for (int i = 0; i < filesets.size(); i++) {
+ FileSet fs = (FileSet) filesets.get(i);
+ DirectoryScanner ds = fs.getDirectoryScanner(getProject());
+
+ File fromDir = fs.getDir(getProject());
+
+ String[] srcFiles = ds.getIncludedFiles();
+ for (int j = 0; j < srcFiles.length; j++) {
+ File file = new File(fromDir, srcFiles[j]);
+ if (ivy.check(file.toURI().toURL(), resolvername)) {
+ Message.verbose("checked " + file + ": OK");
+ }
+ }
+ }
+ } catch (MalformedURLException e) {
+ throw new BuildException("impossible to convert a file to an url! " + e, e);
+ }
+ }
+
+}
diff --git a/src/java/org/apache/ivy/ant/IvyCleanCache.java b/src/java/org/apache/ivy/ant/IvyCleanCache.java
new file mode 100644
index 0000000..251f3e2
--- /dev/null
+++ b/src/java/org/apache/ivy/ant/IvyCleanCache.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.ant;
+
+import org.apache.ivy.core.cache.RepositoryCacheManager;
+import org.apache.ivy.core.settings.IvySettings;
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Cleans the content of Ivy cache(s).
+ */
+public class IvyCleanCache extends IvyTask {
+ public static final String ALL = "*";
+
+ public static final String NONE = "NONE";
+
+ private boolean resolution = true;
+
+ private String cache = ALL;
+
+ public String getCache() {
+ return cache;
+ }
+
+ /**
+ * Sets the name of the repository cache to clean, '*' for all caches, 'NONE' for no repository
+ * cache cleaning at all.
+ *
+ * @param cache
+ * the name of the cache to clean. Must not be <code>null</code>.
+ */
+ public void setCache(String cache) {
+ this.cache = cache;
+ }
+
+ public boolean isResolution() {
+ return resolution;
+ }
+
+ /**
+ * Sets weither the resolution cache should be cleaned or not.
+ *
+ * @param resolution
+ * <code>true</code> if the resolution cache should be cleaned, <code>false</code>
+ * otherwise.
+ */
+ public void setResolution(boolean resolution) {
+ this.resolution = resolution;
+ }
+
+ public void doExecute() throws BuildException {
+ IvySettings settings = getIvyInstance().getSettings();
+ if (isResolution()) {
+ settings.getResolutionCacheManager().clean();
+ }
+ if (ALL.equals(getCache())) {
+ RepositoryCacheManager[] caches = settings.getRepositoryCacheManagers();
+ for (int i = 0; i < caches.length; i++) {
+ caches[i].clean();
+ }
+ } else if (!NONE.equals(getCache())) {
+ RepositoryCacheManager cache = settings.getRepositoryCacheManager(getCache());
+ if (cache == null) {
+ throw new BuildException("unknown cache '" + getCache() + "'");
+ } else {
+ cache.clean();
+ }
+ }
+ }
+}
diff --git a/src/java/org/apache/ivy/ant/IvyConfigure.java b/src/java/org/apache/ivy/ant/IvyConfigure.java
new file mode 100644
index 0000000..5e05adc
--- /dev/null
+++ b/src/java/org/apache/ivy/ant/IvyConfigure.java
@@ -0,0 +1,157 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.ant;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.Collection;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+
+/**
+ * Configure Ivy with an ivysettings.xml file
+ */
+public class IvyConfigure extends Task {
+
+ /**
+ * Use to override a previous definition of settings with the same id
+ */
+ public static final String OVERRIDE_TRUE = "true";
+
+ /**
+ * Use to avoid overriding a previous definition of settings with the same id
+ */
+ public static final String OVERRIDE_FALSE = "false";
+
+ /**
+ * Use to raise an error if attempting to override a previous definition of settings with the
+ * same id
+ */
+ public static final String OVERRIDE_NOT_ALLOWED = "notallowed";
+
+ private static final Collection OVERRIDE_VALUES = Arrays.asList(new String[] {OVERRIDE_TRUE,
+ OVERRIDE_FALSE, OVERRIDE_NOT_ALLOWED});
+
+ private String override = OVERRIDE_NOT_ALLOWED;
+
+ private IvyAntSettings settings = new IvyAntSettings();
+
+ public void setSettingsId(String settingsId) {
+ settings.setId(settingsId);
+ }
+
+ public String getSettingsId() {
+ return settings.getId();
+ }
+
+ public void setOverride(String override) {
+ if (!OVERRIDE_VALUES.contains(override)) {
+ throw new IllegalArgumentException("invalid override value '" + override + "'. "
+ + "Valid values are " + OVERRIDE_VALUES);
+ }
+ this.override = override;
+ }
+
+ public String getOverride() {
+ return override;
+ }
+
+ public File getFile() {
+ return settings.getFile();
+ }
+
+ public void setFile(File file) {
+ settings.setFile(file);
+ }
+
+ public URL getUrl() {
+ return settings.getUrl();
+ }
+
+ public void setUrl(String url) throws MalformedURLException {
+ settings.setUrl(url);
+ }
+
+ public void setUrl(URL url) {
+ if (url == null) {
+ throw new NullPointerException("Cannot set a null URL");
+ }
+ settings.setUrl(url);
+ }
+
+ public String getRealm() {
+ return settings.getRealm();
+ }
+
+ public void setRealm(String realm) {
+ settings.setRealm(realm);
+ }
+
+ public String getHost() {
+ return settings.getHost();
+ }
+
+ public void setHost(String host) {
+ settings.setHost(host);
+ }
+
+ public String getUserName() {
+ return settings.getUsername();
+ }
+
+ public void setUserName(String userName) {
+ settings.setUsername(userName);
+ }
+
+ public String getPasswd() {
+ return settings.getPasswd();
+ }
+
+ public void setPasswd(String passwd) {
+ settings.setPasswd(passwd);
+ }
+
+ public void execute() throws BuildException {
+ String settingsId = settings.getId();
+ Object otherRef = getProject().getReference(settingsId);
+
+ if ((otherRef != null) && OVERRIDE_NOT_ALLOWED.equals(override)) {
+ throw new BuildException(
+ "Overriding a previous definition of ivy:settings with the id '" + settingsId
+ + "' is not allowed when using override='" + OVERRIDE_NOT_ALLOWED
+ + "'.");
+ }
+
+ if ((otherRef != null) && OVERRIDE_FALSE.equals(override)) {
+ verbose("A settings definition is already available for " + settingsId + ": skipping");
+ return;
+ }
+
+ settings.setProject(getProject());
+ getProject().addReference(settingsId, settings);
+ settings.createIvyEngine(this);
+ }
+
+ private void verbose(String msg) {
+ log(msg, Project.MSG_VERBOSE);
+ }
+}
diff --git a/src/java/org/apache/ivy/ant/IvyConflict.java b/src/java/org/apache/ivy/ant/IvyConflict.java
new file mode 100644
index 0000000..a4a2a1d
--- /dev/null
+++ b/src/java/org/apache/ivy/ant/IvyConflict.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.ant;
+
+import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleId;
+import org.apache.ivy.core.settings.IvySettings;
+import org.apache.ivy.plugins.conflict.ConflictManager;
+import org.apache.ivy.plugins.conflict.FixedConflictManager;
+import org.apache.ivy.plugins.matcher.PatternMatcher;
+
+public class IvyConflict {
+
+ private String org;
+
+ private String module;
+
+ private String manager;
+
+ private String rev;
+
+ private String matcher;
+
+ public void setOrg(String org) {
+ this.org = org;
+ }
+
+ public void setModule(String module) {
+ this.module = module;
+ }
+
+ public void setManager(String manager) {
+ this.manager = manager;
+ }
+
+ public void setRev(String rev) {
+ this.rev = rev;
+ }
+
+ public void setMatcher(String matcher) {
+ this.matcher = matcher;
+ }
+
+ void addConflict(DefaultModuleDescriptor md, IvySettings settings) {
+ String matcherName = matcher == null ? PatternMatcher.EXACT : matcher;
+ String orgPattern = org == null ? PatternMatcher.ANY_EXPRESSION : org;
+ String modulePattern = module == null ? PatternMatcher.ANY_EXPRESSION : module;
+ ConflictManager cm = null;
+ if (rev != null) {
+ String[] revs = rev.split(",");
+ for (int i = 0; i < revs.length; i++) {
+ revs[i] = revs[i].trim();
+ }
+ cm = new FixedConflictManager(revs);
+ } else if (manager != null) {
+ cm = settings.getConflictManager(manager);
+ }
+ md.addConflictManager(new ModuleId(orgPattern, modulePattern),
+ settings.getMatcher(matcherName), cm);
+ }
+
+}
diff --git a/src/java/org/apache/ivy/ant/IvyConvertPom.java b/src/java/org/apache/ivy/ant/IvyConvertPom.java
new file mode 100644
index 0000000..cfd2209
--- /dev/null
+++ b/src/java/org/apache/ivy/ant/IvyConvertPom.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.ant;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.text.ParseException;
+
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.plugins.parser.m2.PomModuleDescriptorParser;
+import org.apache.ivy.plugins.repository.url.URLResource;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+
+/**
+ * Convert a pom to an ivy file
+ */
+public class IvyConvertPom extends IvyTask {
+ private File pomFile = null;
+
+ private File ivyFile = null;
+
+ public File getPomFile() {
+ return pomFile;
+ }
+
+ public void setPomFile(File file) {
+ pomFile = file;
+ }
+
+ public File getIvyFile() {
+ return ivyFile;
+ }
+
+ public void setIvyFile(File ivyFile) {
+ this.ivyFile = ivyFile;
+ }
+
+ public void doExecute() throws BuildException {
+ try {
+ if (pomFile == null) {
+ throw new BuildException("source pom file is required for convertpom task");
+ }
+ if (ivyFile == null) {
+ throw new BuildException("destination ivy file is required for convertpom task");
+ }
+ ModuleDescriptor md = PomModuleDescriptorParser.getInstance().parseDescriptor(
+ getSettings(), pomFile.toURI().toURL(), false);
+ PomModuleDescriptorParser.getInstance().toIvyFile(pomFile.toURI().toURL().openStream(),
+ new URLResource(pomFile.toURI().toURL()), getIvyFile(), md);
+ } catch (MalformedURLException e) {
+ throw new BuildException("unable to convert given pom file to url: " + pomFile + ": "
+ + e, e);
+ } catch (ParseException e) {
+ log(e.getMessage(), Project.MSG_ERR);
+ throw new BuildException("syntax errors in pom file " + pomFile + ": " + e, e);
+ } catch (Exception e) {
+ throw new BuildException("impossible convert given pom file to ivy file: " + e
+ + " from=" + pomFile + " to=" + ivyFile, e);
+ }
+ }
+}
diff --git a/src/java/org/apache/ivy/ant/IvyDeliver.java b/src/java/org/apache/ivy/ant/IvyDeliver.java
new file mode 100644
index 0000000..06473c9
--- /dev/null
+++ b/src/java/org/apache/ivy/ant/IvyDeliver.java
@@ -0,0 +1,452 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.ant;
+
+import java.io.File;
+import java.util.Date;
+
+import org.apache.ivy.Ivy;
+import org.apache.ivy.core.deliver.DefaultPublishingDRResolver;
+import org.apache.ivy.core.deliver.DeliverOptions;
+import org.apache.ivy.core.deliver.PublishingDependencyRevisionResolver;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.module.status.StatusManager;
+import org.apache.ivy.core.settings.IvySettings;
+import org.apache.ivy.util.DateUtil;
+import org.apache.ivy.util.Message;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.taskdefs.CallTarget;
+import org.apache.tools.ant.taskdefs.Echo;
+import org.apache.tools.ant.taskdefs.Input;
+import org.apache.tools.ant.taskdefs.Property;
+
+/**
+ * Trigger the delivery of a module, which may consist in a recursive delivery of dependencies and
+ * on the replacement in the ivy file of dynamic revisions (like latest.integration) by static ones.
+ */
+public class IvyDeliver extends IvyTask {
+ private final class DeliverDRResolver extends DefaultPublishingDRResolver {
+ public String resolve(ModuleDescriptor published, String publishedStatus,
+ ModuleRevisionId depMrid, String depStatus) {
+ if (StatusManager.getCurrent().isIntegration(publishedStatus)) {
+ // published status is integration one, nothing to ask
+ return super.resolve(published, publishedStatus, depMrid, depStatus);
+ }
+
+ // we are publishing a delivery (a non integration module)
+
+ if (!StatusManager.getCurrent().isIntegration(depStatus)) {
+ // dependency is already a delivery, nothing to ask
+ return super.resolve(published, publishedStatus, depMrid, depStatus);
+ }
+
+ // the dependency is not a delivery
+
+ String statusProperty = depMrid.getName() + "." + depMrid.getRevision() + ".status";
+ String versionProperty = depMrid.getName() + "." + depMrid.getRevision() + ".version";
+ String deliveredProperty = depMrid.getName() + "." + depMrid.getRevision()
+ + ".delivered";
+
+ String version = getProject().getProperty(versionProperty);
+ String status = getProject().getProperty(statusProperty);
+ String delivered = getProject().getProperty(deliveredProperty);
+ Message.debug("found version = " + version + " status=" + status + " delivered="
+ + delivered);
+ if (version != null && status != null) {
+ if ("true".equals(delivered)) {
+ // delivery has already been done : just return the value
+ return version;
+ } else {
+ deliverDependency(depMrid, version, status, depStatus);
+ loadDeliveryList();
+ return version;
+ }
+ }
+
+ /**
+ * By setting these properties: recursive.delivery.status and
+ * recursive.delivery.version, then if the specific status/version is not found, then we
+ * will use the status/version set in these global properties. This will apply to all
+ * artifacts in the system. This patch is meant to be used for recursive deliveries so
+ * that all deliveries will use the global status/version unless a more specific one is
+ * set.
+ */
+ String globalStatusProperty = "recursive.delivery.status";
+ String globalVersionProperty = "recursive.delivery.version";
+ version = getProject().getProperty(globalVersionProperty);
+ status = getProject().getProperty(globalStatusProperty);
+ if (version != null && status != null) {
+ // found global delivery properties
+ delivered = getProject().getProperty(
+ "recursive." + depMrid.getName() + ".delivered");
+ Message.debug("found global version = " + version + " and global status=" + status
+ + " - delivered = " + delivered);
+ if ("true".equals(delivered)) {
+ // delivery has already been done : just return the value
+ return version;
+ } else {
+ getProject().setProperty(statusProperty, status);
+ deliverDependency(depMrid, version, status, depStatus);
+ loadDeliveryList();
+ return version;
+ }
+ }
+
+ // we must ask the user what version and status he want to have
+ // for the dependency
+ Input input = (Input) getProject().createTask("input");
+ input.setOwningTarget(getOwningTarget());
+ input.init();
+
+ // ask status
+ input.setMessage(depMrid.getName() + " " + depMrid.getRevision()
+ + ": please enter a status: ");
+ input.setValidargs(StatusManager.getCurrent().getDeliveryStatusListString());
+ input.setAddproperty(statusProperty);
+ input.perform();
+ status = getProject().getProperty(statusProperty);
+ appendDeliveryList(statusProperty + " = " + status);
+
+ // ask version
+ input.setMessage(depMrid.getName() + " " + depMrid.getRevision()
+ + ": please enter a version: ");
+ input.setValidargs(null);
+ input.setAddproperty(versionProperty);
+ input.perform();
+
+ version = getProject().getProperty(versionProperty);
+ appendDeliveryList(versionProperty + " = " + version);
+ deliverDependency(depMrid, version, status, depStatus);
+
+ loadDeliveryList();
+
+ return version;
+ }
+
+ public void deliverDependency(ModuleRevisionId depMrid, String version, String status,
+ String depStatus) {
+ // call deliver target if any
+ if (deliverTarget != null && deliverTarget.trim().length() > 0) {
+
+ CallTarget ct = (CallTarget) getProject().createTask("antcall");
+ ct.setOwningTarget(getOwningTarget());
+ ct.init();
+ ct.setTarget(deliverTarget);
+ ct.setInheritAll(true);
+ ct.setInheritRefs(true);
+ Property param = ct.createParam();
+ param.setName("dependency.name");
+ param.setValue(depMrid.getName());
+ param = ct.createParam();
+ param.setName("dependency.published.status");
+ param.setValue(status);
+ param = ct.createParam();
+ param.setName("dependency.published.version");
+ param.setValue(version);
+ param = ct.createParam();
+ param.setName("dependency.version");
+ param.setValue(depMrid.getRevision());
+ param = ct.createParam();
+ param.setName("dependency.status");
+ param.setValue(depStatus == null ? "null" : depStatus);
+
+ ct.perform();
+
+ String deliveredProperty = depMrid.getName() + "." + depMrid.getRevision()
+ + ".delivered";
+ getProject().setProperty(deliveredProperty, "true");
+ appendDeliveryList(deliveredProperty + " = true");
+
+ getProject().setProperty("recursive." + depMrid.getName() + ".delivered", "true");
+ appendDeliveryList("recursive." + depMrid.getName() + ".delivered" + " = true");
+ }
+ }
+
+ }
+
+ private String organisation;
+
+ private String module;
+
+ private String revision;
+
+ private String pubRevision;
+
+ private String deliverpattern;
+
+ private String status;
+
+ private String pubdate;
+
+ private String deliverTarget;
+
+ private File deliveryList;
+
+ private boolean replacedynamicrev = true;
+
+ private boolean replaceForcedRev = false;
+
+ private String resolveId;
+
+ private String conf;
+
+ private String pubBranch;
+
+ private boolean generateRevConstraint = true;
+
+ private boolean merge = true;
+
+ public void setCache(File cache) {
+ cacheAttributeNotSupported();
+ }
+
+ public String getDeliverpattern() {
+ return deliverpattern;
+ }
+
+ public void setDeliverpattern(String destivypattern) {
+ this.deliverpattern = destivypattern;
+ }
+
+ public String getModule() {
+ return module;
+ }
+
+ public void setModule(String module) {
+ this.module = module;
+ }
+
+ public String getOrganisation() {
+ return organisation;
+ }
+
+ public void setOrganisation(String organisation) {
+ this.organisation = organisation;
+ }
+
+ public String getPubdate() {
+ return pubdate;
+ }
+
+ public void setPubdate(String pubdate) {
+ this.pubdate = pubdate;
+ }
+
+ public String getPubrevision() {
+ return pubRevision;
+ }
+
+ public void setPubrevision(String pubRevision) {
+ this.pubRevision = pubRevision;
+ }
+
+ public String getPubbranch() {
+ return pubBranch;
+ }
+
+ public void setPubbranch(String pubBranch) {
+ this.pubBranch = pubBranch;
+ }
+
+ public String getRevision() {
+ return revision;
+ }
+
+ public void setRevision(String revision) {
+ this.revision = revision;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public void setDelivertarget(String deliverTarget) {
+ this.deliverTarget = deliverTarget;
+ }
+
+ public void setDeliveryList(File deliveryList) {
+ this.deliveryList = deliveryList;
+ }
+
+ public boolean isReplacedynamicrev() {
+ return replacedynamicrev;
+ }
+
+ public void setReplacedynamicrev(boolean replacedynamicrev) {
+ this.replacedynamicrev = replacedynamicrev;
+ }
+
+ public boolean isReplaceForcedRev() {
+ return replaceForcedRev;
+ }
+
+ public void setReplaceForcedRev(boolean replaceForcedRev) {
+ this.replaceForcedRev = replaceForcedRev;
+ }
+
+ public String getResolveId() {
+ return resolveId;
+ }
+
+ public void setResolveId(String resolveId) {
+ this.resolveId = resolveId;
+ }
+
+ public String getConf() {
+ return conf;
+ }
+
+ public void setConf(String confs) {
+ conf = confs;
+ }
+
+ public boolean isGenerateRevConstraint() {
+ return generateRevConstraint;
+ }
+
+ public void setGenerateRevConstraint(boolean generateRevConstraint) {
+ this.generateRevConstraint = generateRevConstraint;
+ }
+
+ public boolean isMerge() {
+ return merge;
+ }
+
+ public void setMerge(boolean merge) {
+ this.merge = merge;
+ }
+
+ public void doExecute() throws BuildException {
+ Ivy ivy = getIvyInstance();
+ IvySettings settings = ivy.getSettings();
+
+ organisation = getProperty(organisation, settings, "ivy.organisation", resolveId);
+ module = getProperty(module, settings, "ivy.module", resolveId);
+ revision = getProperty(revision, settings, "ivy.revision", resolveId);
+ pubBranch = getProperty(pubBranch, settings, "ivy.deliver.branch");
+ pubRevision = getProperty(pubRevision, settings, "ivy.deliver.revision");
+ deliverpattern = getProperty(deliverpattern, settings, "ivy.deliver.ivy.pattern");
+ status = getProperty(status, settings, "ivy.status");
+ if (deliveryList == null) {
+ String deliveryListPath = getProperty(settings, "ivy.delivery.list.file");
+ if (deliveryListPath == null) {
+ deliveryList = new File(System.getProperty("java.io.tmpdir")
+ + "/delivery.properties");
+ } else {
+ deliveryList = getProject().resolveFile(settings.substitute(deliveryListPath));
+ }
+ }
+ if (resolveId == null) {
+ if (organisation == null) {
+ throw new BuildException("no organisation provided for ivy deliver task: "
+ + "It can either be set explicitely via the attribute 'organisation' "
+ + "or via 'ivy.organisation' property or a prior call to <resolve/>");
+ }
+ if (module == null) {
+ throw new BuildException("no module name provided for ivy deliver task: "
+ + "It can either be set explicitely via the attribute 'module' "
+ + "or via 'ivy.module' property or a prior call to <resolve/>");
+ }
+ }
+ if (revision == null) {
+ revision = Ivy.getWorkingRevision();
+ }
+ Date pubdate = getPubDate(this.pubdate, new Date());
+ if (pubRevision == null) {
+ if (revision.startsWith("working@")) {
+ pubRevision = DateUtil.format(pubdate);
+ } else {
+ pubRevision = revision;
+ }
+ }
+ if (deliverpattern == null) {
+ throw new BuildException(
+ "deliver ivy pattern is missing: either provide it as parameters "
+ + "or through ivy.deliver.ivy.pattern properties");
+ }
+ if (status == null) {
+ throw new BuildException(
+ "no status provided: either provide it as parameter or through "
+ + "the ivy.status.default property");
+ }
+
+ ModuleRevisionId mrid = null;
+ if (resolveId == null) {
+ mrid = ModuleRevisionId.newInstance(organisation, module, revision);
+ }
+ boolean isLeading = false;
+ try {
+ if (!deliveryList.exists()) {
+ isLeading = true;
+ }
+
+ loadDeliveryList();
+
+ PublishingDependencyRevisionResolver drResolver;
+ if (deliverTarget != null && deliverTarget.trim().length() > 0) {
+ drResolver = new DeliverDRResolver();
+ } else {
+ drResolver = new DefaultPublishingDRResolver();
+ }
+
+ DeliverOptions options = new DeliverOptions(status, pubdate, drResolver,
+ doValidate(settings), replacedynamicrev, splitConfs(conf))
+ .setResolveId(resolveId).setReplaceForcedRevisions(isReplaceForcedRev())
+ .setGenerateRevConstraint(generateRevConstraint).setMerge(merge)
+ .setPubBranch(pubBranch);
+ if (mrid == null) {
+ ivy.deliver(pubRevision, deliverpattern, options);
+ } else {
+ ivy.deliver(mrid, pubRevision, deliverpattern, options);
+ }
+ } catch (Exception e) {
+ throw new BuildException("impossible to deliver " + mrid == null ? resolveId : mrid
+ + ": " + e, e);
+ } finally {
+ if (isLeading) {
+ if (deliveryList.exists()) {
+ deliveryList.delete();
+ }
+ }
+ }
+ }
+
+ private void loadDeliveryList() {
+ Property property = (Property) getProject().createTask("property");
+ property.setOwningTarget(getOwningTarget());
+ property.init();
+ property.setFile(deliveryList);
+ property.perform();
+ }
+
+ private void appendDeliveryList(String msg) {
+ Echo echo = (Echo) getProject().createTask("echo");
+ echo.setOwningTarget(getOwningTarget());
+ echo.init();
+ echo.setFile(deliveryList);
+ echo.setMessage(msg + "\n");
+ echo.setAppend(true);
+ echo.perform();
+ }
+
+}
diff --git a/src/java/org/apache/ivy/ant/IvyDependency.java b/src/java/org/apache/ivy/ant/IvyDependency.java
new file mode 100644
index 0000000..dbc8f21
--- /dev/null
+++ b/src/java/org/apache/ivy/ant/IvyDependency.java
@@ -0,0 +1,193 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.ant;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.DefaultExcludeRule;
+import org.apache.ivy.core.module.descriptor.DefaultIncludeRule;
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.settings.IvySettings;
+import org.apache.tools.ant.BuildException;
+
+public class IvyDependency {
+
+ private List/* <IvyDependencyConf> */confs = new ArrayList();
+
+ private List/* <IvyDependencyArtifact> */artifacts = new ArrayList();
+
+ private List/* <IvyDependencyExclude> */excludes = new ArrayList();
+
+ private List/* <IvyDependencyIncludes> */includes = new ArrayList();
+
+ private String org;
+
+ private String name;
+
+ private String rev;
+
+ private String branch;
+
+ private String conf;
+
+ private boolean changing;
+
+ private boolean force;
+
+ private boolean transitive = true;
+
+ public IvyDependencyConf createConf() {
+ IvyDependencyConf c = new IvyDependencyConf();
+ confs.add(c);
+ return c;
+ }
+
+ public IvyDependencyArtifact createArtifact() {
+ IvyDependencyArtifact artifact = new IvyDependencyArtifact();
+ artifacts.add(artifact);
+ return artifact;
+ }
+
+ public IvyDependencyExclude createExclude() {
+ IvyDependencyExclude exclude = new IvyDependencyExclude();
+ excludes.add(exclude);
+ return exclude;
+ }
+
+ public IvyDependencyInclude createInclude() {
+ IvyDependencyInclude include = new IvyDependencyInclude();
+ includes.add(include);
+ return include;
+ }
+
+ public String getOrg() {
+ return org;
+ }
+
+ public void setOrg(String org) {
+ this.org = org;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getRev() {
+ return rev;
+ }
+
+ public void setRev(String rev) {
+ this.rev = rev;
+ }
+
+ public String getBranch() {
+ return branch;
+ }
+
+ public void setBranch(String branch) {
+ this.branch = branch;
+ }
+
+ public String getConf() {
+ return conf;
+ }
+
+ public void setConf(String conf) {
+ this.conf = conf;
+ }
+
+ public boolean isChanging() {
+ return changing;
+ }
+
+ public void setChanging(boolean changing) {
+ this.changing = changing;
+ }
+
+ public boolean isForce() {
+ return force;
+ }
+
+ public void setForce(boolean force) {
+ this.force = force;
+ }
+
+ public boolean isTransitive() {
+ return transitive;
+ }
+
+ public void setTransitive(boolean transitive) {
+ this.transitive = transitive;
+ }
+
+ DependencyDescriptor asDependencyDescriptor(ModuleDescriptor md, String masterConf,
+ IvySettings settings) {
+ if (org == null) {
+ throw new BuildException("'org' is required on ");
+ }
+ if (name == null) {
+ throw new BuildException("'name' is required when using inline mode");
+ }
+ ModuleRevisionId mrid = ModuleRevisionId.newInstance(org, name, branch, rev);
+ DefaultDependencyDescriptor dd = new DefaultDependencyDescriptor(md, mrid, force, changing,
+ transitive);
+ if (conf != null) {
+ dd.addDependencyConfiguration(masterConf, conf);
+ } else {
+ dd.addDependencyConfiguration(masterConf, "*");
+ }
+
+ Iterator itConfs = confs.iterator();
+ while (itConfs.hasNext()) {
+ IvyDependencyConf c = (IvyDependencyConf) itConfs.next();
+ c.addConf(dd, masterConf);
+ }
+
+ Iterator itArtifacts = artifacts.iterator();
+ while (itArtifacts.hasNext()) {
+ IvyDependencyArtifact artifact = (IvyDependencyArtifact) itArtifacts.next();
+ artifact.addArtifact(dd, masterConf);
+ }
+
+ Iterator itExcludes = excludes.iterator();
+ while (itExcludes.hasNext()) {
+ IvyDependencyExclude exclude = (IvyDependencyExclude) itExcludes.next();
+ DefaultExcludeRule rule = exclude.asRule(settings);
+ dd.addExcludeRule(masterConf, rule);
+ }
+
+ Iterator itIncludes = includes.iterator();
+ while (itIncludes.hasNext()) {
+ IvyDependencyInclude include = (IvyDependencyInclude) itIncludes.next();
+ DefaultIncludeRule rule = include.asRule(settings);
+ dd.addIncludeRule(masterConf, rule);
+ }
+
+ return dd;
+ }
+
+}
diff --git a/src/java/org/apache/ivy/ant/IvyDependencyArtifact.java b/src/java/org/apache/ivy/ant/IvyDependencyArtifact.java
new file mode 100644
index 0000000..e9d7197
--- /dev/null
+++ b/src/java/org/apache/ivy/ant/IvyDependencyArtifact.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.ant;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.apache.ivy.core.module.descriptor.DefaultDependencyArtifactDescriptor;
+import org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor;
+import org.apache.ivy.plugins.matcher.PatternMatcher;
+import org.apache.tools.ant.BuildException;
+
+public class IvyDependencyArtifact {
+
+ private String name;
+
+ private String type;
+
+ private String ext;
+
+ private String url;
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public void setExt(String ext) {
+ this.ext = ext;
+ }
+
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
+ void addArtifact(DefaultDependencyDescriptor dd, String masterConf) {
+ String typePattern = type == null ? PatternMatcher.ANY_EXPRESSION : type;
+ String extPattern = ext == null ? typePattern : ext;
+ URL u;
+ try {
+ u = url == null ? null : new URL(url);
+ } catch (MalformedURLException e) {
+ throw new BuildException("Malformed url in the artifact: " + e.getMessage(), e);
+ }
+ DefaultDependencyArtifactDescriptor dad = new DefaultDependencyArtifactDescriptor(dd, name,
+ typePattern, extPattern, u, null);
+ dd.addDependencyArtifact(masterConf, dad);
+ }
+}
diff --git a/src/java/org/apache/ivy/ant/IvyDependencyConf.java b/src/java/org/apache/ivy/ant/IvyDependencyConf.java
new file mode 100644
index 0000000..c42c956
--- /dev/null
+++ b/src/java/org/apache/ivy/ant/IvyDependencyConf.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.ant;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor;
+
+public class IvyDependencyConf {
+
+ private List/* <IvyDependencyConfMapped> */mappeds = new ArrayList();
+
+ public static class IvyDependencyConfMapped {
+ private String name;
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ }
+
+ private String mapped;
+
+ public void setMapped(String mapped) {
+ this.mapped = mapped;
+ }
+
+ public IvyDependencyConfMapped createMapped() {
+ IvyDependencyConfMapped m = new IvyDependencyConfMapped();
+ mappeds.add(m);
+ return m;
+ }
+
+ void addConf(DefaultDependencyDescriptor dd, String masterConf) {
+ if (mapped != null) {
+ String[] mappeds = mapped.split(",");
+ for (int i = 0; i < mappeds.length; i++) {
+ dd.addDependencyConfiguration(masterConf, mappeds[i].trim());
+ }
+ }
+ Iterator itMappeds = mappeds.iterator();
+ while (itMappeds.hasNext()) {
+ IvyDependencyConfMapped m = (IvyDependencyConfMapped) itMappeds.next();
+ dd.addDependencyConfiguration(masterConf, m.name);
+ }
+ }
+}
diff --git a/src/java/org/apache/ivy/ant/IvyDependencyExclude.java b/src/java/org/apache/ivy/ant/IvyDependencyExclude.java
new file mode 100644
index 0000000..5976fdc
--- /dev/null
+++ b/src/java/org/apache/ivy/ant/IvyDependencyExclude.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.ant;
+
+import org.apache.ivy.core.module.descriptor.DefaultExcludeRule;
+import org.apache.ivy.core.module.id.ArtifactId;
+import org.apache.ivy.core.module.id.ModuleId;
+import org.apache.ivy.core.settings.IvySettings;
+import org.apache.ivy.plugins.matcher.PatternMatcher;
+
+public class IvyDependencyExclude {
+
+ private String org;
+
+ private String module;
+
+ private String name;
+
+ private String type;
+
+ private String ext;
+
+ private String matcher;
+
+ public void setOrg(String org) {
+ this.org = org;
+ }
+
+ public void setModule(String module) {
+ this.module = module;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public void setExt(String ext) {
+ this.ext = ext;
+ }
+
+ public void setMatcher(String matcher) {
+ this.matcher = matcher;
+ }
+
+ DefaultExcludeRule asRule(IvySettings settings) {
+ String matcherName = matcher == null ? PatternMatcher.EXACT : matcher;
+ String orgPattern = org == null ? PatternMatcher.ANY_EXPRESSION : org;
+ String modulePattern = module == null ? PatternMatcher.ANY_EXPRESSION : module;
+ String namePattern = name == null ? PatternMatcher.ANY_EXPRESSION : name;
+ String typePattern = type == null ? PatternMatcher.ANY_EXPRESSION : type;
+ String extPattern = ext == null ? typePattern : ext;
+ ArtifactId aid = new ArtifactId(new ModuleId(orgPattern, modulePattern), namePattern,
+ typePattern, extPattern);
+ return new DefaultExcludeRule(aid, settings.getMatcher(matcherName), null);
+ }
+}
diff --git a/src/java/org/apache/ivy/ant/IvyDependencyInclude.java b/src/java/org/apache/ivy/ant/IvyDependencyInclude.java
new file mode 100644
index 0000000..c5e5cf1
--- /dev/null
+++ b/src/java/org/apache/ivy/ant/IvyDependencyInclude.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.ant;
+
+import org.apache.ivy.core.module.descriptor.DefaultIncludeRule;
+import org.apache.ivy.core.module.id.ArtifactId;
+import org.apache.ivy.core.module.id.ModuleId;
+import org.apache.ivy.core.settings.IvySettings;
+import org.apache.ivy.plugins.matcher.PatternMatcher;
+
+public class IvyDependencyInclude {
+
+ private String name;
+
+ private String type;
+
+ private String ext;
+
+ private String matcher;
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public void setExt(String ext) {
+ this.ext = ext;
+ }
+
+ public void setMatcher(String matcher) {
+ this.matcher = matcher;
+ }
+
+ DefaultIncludeRule asRule(IvySettings settings) {
+ String matcherName = matcher == null ? PatternMatcher.EXACT : matcher;
+ String namePattern = name == null ? PatternMatcher.ANY_EXPRESSION : name;
+ String typePattern = type == null ? PatternMatcher.ANY_EXPRESSION : type;
+ String extPattern = ext == null ? typePattern : ext;
+ ArtifactId aid = new ArtifactId(new ModuleId(PatternMatcher.ANY_EXPRESSION,
+ PatternMatcher.ANY_EXPRESSION), namePattern, typePattern, extPattern);
+ return new DefaultIncludeRule(aid, settings.getMatcher(matcherName), null);
+ }
+
+}
diff --git a/src/java/org/apache/ivy/ant/IvyDependencyTree.java b/src/java/org/apache/ivy/ant/IvyDependencyTree.java
new file mode 100644
index 0000000..f7c9fee
--- /dev/null
+++ b/src/java/org/apache/ivy/ant/IvyDependencyTree.java
@@ -0,0 +1,132 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.ant;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.report.ResolveReport;
+import org.apache.ivy.core.resolve.IvyNode;
+import org.apache.ivy.core.resolve.IvyNodeCallers.Caller;
+import org.apache.ivy.core.resolve.IvyNodeEviction.EvictionData;
+import org.apache.tools.ant.BuildException;
+
+public class IvyDependencyTree extends IvyPostResolveTask {
+
+ private Map/* <ModuleRevisionId, List<IvyNode>> */dependencies = new HashMap/*
+ * <ModuleRevisionId,
+ * List<IvyNode>>
+ */();
+
+ private boolean showEvicted = false;
+
+ public void doExecute() throws BuildException {
+ prepareAndCheck();
+ ResolveReport report = getResolvedReport();
+ log("Dependency tree for " + report.getResolveId());
+ ModuleRevisionId mrid = report.getModuleDescriptor().getModuleRevisionId();
+ // make dependency tree easier to fetch informations
+ for (Iterator iterator = report.getDependencies().iterator(); iterator.hasNext();) {
+ IvyNode dependency = (IvyNode) iterator.next();
+ populateDependencyTree(dependency, mrid, report);
+ }
+ printDependencies((List) dependencies.get(mrid), 0);
+ }
+
+ private void printDependencies(List/* <IvyNode> */dependencyList, int indent) {
+ for (Iterator iterator = dependencyList.iterator(); iterator.hasNext();) {
+ IvyNode dependency = (IvyNode) iterator.next();
+ boolean evicted = dependency.isEvicted(getConf());
+ if (evicted && !showEvicted) {
+ continue;
+ }
+ StringBuffer sb = new StringBuffer();
+ if (indent > 0) {
+ for (int i = 0; i < indent; i++) {
+ if (i == indent - 1 && !iterator.hasNext() && !hasDependencies(dependency)) {
+ sb.append(" ");
+ } else {
+ sb.append("| ");
+ }
+
+ }
+ }
+ if (iterator.hasNext()) {
+ sb.append("+- ");
+ } else {
+ sb.append("\\- ");
+ }
+ sb.append(dependency.getId().toString());
+ if (evicted && showEvicted) {
+ EvictionData evictedData = dependency.getEvictedData(getConf());
+ if (evictedData.isTransitivelyEvicted()) {
+ sb.append(" transitively");
+ } else {
+ sb.append(" evicted by ");
+ sb.append(evictedData.getSelected());
+ sb.append(" in ").append(evictedData.getParent());
+ if (evictedData.getDetail() != null) {
+ sb.append(" ").append(evictedData.getDetail());
+ }
+ }
+ }
+ log(sb.toString());
+
+ printDependencies((List) dependencies.get(dependency.getId()), indent + 1);
+ }
+ }
+
+ private boolean hasDependencies(IvyNode dependency) {
+ List dependencyList = (List) dependencies.get(dependency.getId());
+ return dependencyList.size() > 0;
+ }
+
+ private void populateDependencyTree(IvyNode dependency, ModuleRevisionId currentMrid,
+ ResolveReport report) {
+ registerNodeIfNecessary(dependency.getId());
+ for (int i = 0; i < dependency.getAllCallers().length; i++) {
+ Caller caller = dependency.getAllCallers()[i];
+ addDependency(caller.getModuleRevisionId(), dependency);
+ }
+ }
+
+ private void registerNodeIfNecessary(ModuleRevisionId moduleRevisionId) {
+ if (!dependencies.containsKey(moduleRevisionId)) {
+ dependencies.put(moduleRevisionId, new ArrayList/* <IvyNode> */());
+ }
+ }
+
+ private void addDependency(ModuleRevisionId moduleRevisionId, IvyNode dependency) {
+ registerNodeIfNecessary(moduleRevisionId);
+ List/* <IvyNode> */list = (List) dependencies.get(moduleRevisionId);
+ list.add(dependency);
+ }
+
+ public boolean isShowEvicted() {
+ return showEvicted;
+ }
+
+ public void setShowEvicted(boolean showEvicted) {
+ this.showEvicted = showEvicted;
+ }
+
+}
diff --git a/src/java/org/apache/ivy/ant/IvyDependencyUpdateChecker.java b/src/java/org/apache/ivy/ant/IvyDependencyUpdateChecker.java
new file mode 100644
index 0000000..d416e57
--- /dev/null
+++ b/src/java/org/apache/ivy/ant/IvyDependencyUpdateChecker.java
@@ -0,0 +1,214 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.ant;
+
+import java.io.IOException;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.ivy.core.module.descriptor.Configuration;
+import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.report.ResolveReport;
+import org.apache.ivy.core.resolve.IvyNode;
+import org.apache.ivy.core.resolve.ResolveOptions;
+import org.apache.tools.ant.BuildException;
+
+public class IvyDependencyUpdateChecker extends IvyPostResolveTask {
+
+ private String revisionToCheck = "latest.integration";
+
+ private boolean download = false;
+
+ private boolean checkIfChanged = false;
+
+ private boolean showTransitive = false;
+
+ public void doExecute() throws BuildException {
+ prepareAndCheck();
+
+ ModuleDescriptor originalModuleDescriptor = getResolvedReport().getModuleDescriptor();
+ // clone module descriptor
+ DefaultModuleDescriptor latestModuleDescriptor = new DefaultModuleDescriptor(
+ originalModuleDescriptor.getModuleRevisionId(),
+ originalModuleDescriptor.getStatus(), originalModuleDescriptor.getPublicationDate());
+ // copy configurations
+ for (int i = 0; i < originalModuleDescriptor.getConfigurations().length; i++) {
+ Configuration configuration = originalModuleDescriptor.getConfigurations()[i];
+ latestModuleDescriptor.addConfiguration(configuration);
+ }
+ // clone dependency and add new one with the requested revisionToCheck
+ for (int i = 0; i < originalModuleDescriptor.getDependencies().length; i++) {
+ DependencyDescriptor dependencyDescriptor = originalModuleDescriptor.getDependencies()[i];
+ ModuleRevisionId upToDateMrid = ModuleRevisionId.newInstance(
+ dependencyDescriptor.getDependencyRevisionId(), revisionToCheck);
+ latestModuleDescriptor.addDependency(dependencyDescriptor.clone(upToDateMrid));
+ }
+
+ // resolve
+ ResolveOptions resolveOptions = new ResolveOptions();
+ resolveOptions.setDownload(isDownload());
+ resolveOptions.setLog(getLog());
+ resolveOptions.setConfs(splitConfs(getConf()));
+ resolveOptions.setCheckIfChanged(checkIfChanged);
+
+ ResolveReport latestReport;
+ try {
+ latestReport = getIvyInstance().getResolveEngine().resolve(latestModuleDescriptor,
+ resolveOptions);
+
+ displayDependencyUpdates(getResolvedReport(), latestReport);
+ if (showTransitive) {
+ displayNewDependencyOnLatest(getResolvedReport(), latestReport);
+ displayMissingDependencyOnLatest(getResolvedReport(), latestReport);
+ }
+
+ } catch (ParseException e) {
+ throw new BuildException("impossible to resolve dependencies:\n\t" + e, e);
+ } catch (IOException e) {
+ throw new BuildException("impossible to resolve dependencies:\n\t" + e, e);
+ }
+
+ }
+
+ private void displayDependencyUpdates(ResolveReport originalReport, ResolveReport latestReport) {
+ log("Dependencies updates available :");
+ boolean dependencyUpdateDetected = false;
+ for (Iterator iterator = latestReport.getDependencies().iterator(); iterator.hasNext();) {
+ IvyNode latest = (IvyNode) iterator.next();
+ for (Iterator iterator2 = originalReport.getDependencies().iterator(); iterator2
+ .hasNext();) {
+ IvyNode originalDependency = (IvyNode) iterator2.next();
+ if (originalDependency.getModuleId().equals(latest.getModuleId())) {
+ if (!originalDependency.getResolvedId().getRevision()
+ .equals(latest.getResolvedId().getRevision())) {
+ // is this dependency a transitive dependency ? or direct dependency
+ // (unfortunatly
+ // .isTranstive() methods doesn't have the same meaning)
+ boolean isTransitiveDependency = latest.getDependencyDescriptor(latest
+ .getRoot()) == null;
+ if ((!isTransitiveDependency) || (isTransitiveDependency && showTransitive)) {
+ StringBuffer sb = new StringBuffer();
+ sb.append("\t")//
+ .append(originalDependency.getResolvedId().getOrganisation()) //
+ .append('#')//
+ .append(originalDependency.getResolvedId().getName())//
+ .append(isTransitiveDependency ? " (transitive)" : "") //
+ .append("\t")//
+ .append(originalDependency.getResolvedId().getRevision())//
+ .append(" -> ")//
+ .append(latest.getResolvedId().getRevision());
+ log(sb.toString());
+ dependencyUpdateDetected = true;
+ }
+ }
+
+ }
+ }
+ }
+ if (!dependencyUpdateDetected) {
+ log("\tAll dependencies are up to date");
+ }
+ }
+
+ private void displayMissingDependencyOnLatest(ResolveReport originalReport,
+ ResolveReport latestReport) {
+ List/* <ModuleRevisionId> */listOfMissingDependencyOnLatest = new ArrayList/*
+ * <ModuleRevisionId
+ * >
+ */();
+ for (Iterator iterator = originalReport.getDependencies().iterator(); iterator.hasNext();) {
+ IvyNode originalDependency = (IvyNode) iterator.next();
+ boolean dependencyFound = false;
+ for (Iterator iterator2 = latestReport.getDependencies().iterator(); iterator2
+ .hasNext();) {
+ IvyNode latest = (IvyNode) iterator2.next();
+ if (originalDependency.getModuleId().equals(latest.getModuleId())) {
+ dependencyFound = true;
+ }
+ }
+ if (!dependencyFound) {
+ listOfMissingDependencyOnLatest.add(originalDependency.getId());
+ }
+ }
+
+ if (listOfMissingDependencyOnLatest.size() > 0) {
+ log("List of missing dependency on latest resolve :");
+ for (Iterator iterator = listOfMissingDependencyOnLatest.iterator(); iterator.hasNext();) {
+ ModuleRevisionId moduleRevisionId = (ModuleRevisionId) iterator.next();
+ log("\t" + moduleRevisionId.toString());
+ }
+ }
+ }
+
+ private void displayNewDependencyOnLatest(ResolveReport originalReport,
+ ResolveReport latestReport) {
+ List/* <ModuleRevisionId> */listOfNewDependencyOnLatest = new ArrayList/* <ModuleRevisionId> */();
+ for (Iterator iterator = latestReport.getDependencies().iterator(); iterator.hasNext();) {
+ IvyNode latest = (IvyNode) iterator.next();
+
+ boolean dependencyFound = false;
+ for (Iterator iterator2 = originalReport.getDependencies().iterator(); iterator2
+ .hasNext();) {
+ IvyNode originalDependency = (IvyNode) iterator2.next();
+ if (originalDependency.getModuleId().equals(latest.getModuleId())) {
+ dependencyFound = true;
+ }
+ }
+ if (!dependencyFound) {
+ listOfNewDependencyOnLatest.add(latest.getId());
+ }
+ }
+ if (listOfNewDependencyOnLatest.size() > 0) {
+ log("List of new dependency on latest resolve :");
+ for (Iterator iterator = listOfNewDependencyOnLatest.iterator(); iterator.hasNext();) {
+ ModuleRevisionId moduleRevisionId = (ModuleRevisionId) iterator.next();
+ log("\t" + moduleRevisionId.toString());
+ }
+ }
+ }
+
+ public String getRevisionToCheck() {
+ return revisionToCheck;
+ }
+
+ public void setRevisionToCheck(String revisionToCheck) {
+ this.revisionToCheck = revisionToCheck;
+ }
+
+ public boolean isDownload() {
+ return download;
+ }
+
+ public void setDownload(boolean download) {
+ this.download = download;
+ }
+
+ public boolean isShowTransitive() {
+ return showTransitive;
+ }
+
+ public void setShowTransitive(boolean showTransitive) {
+ this.showTransitive = showTransitive;
+ }
+
+}
diff --git a/src/java/org/apache/ivy/ant/IvyExclude.java b/src/java/org/apache/ivy/ant/IvyExclude.java
new file mode 100644
index 0000000..ad13c07
--- /dev/null
+++ b/src/java/org/apache/ivy/ant/IvyExclude.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.ant;
+
+import org.apache.ivy.core.module.descriptor.DefaultExcludeRule;
+import org.apache.ivy.core.module.id.ArtifactId;
+import org.apache.ivy.core.module.id.ModuleId;
+import org.apache.ivy.core.settings.IvySettings;
+import org.apache.ivy.plugins.matcher.PatternMatcher;
+
+public class IvyExclude {
+
+ private String org;
+
+ private String module;
+
+ private String artifact;
+
+ private String type;
+
+ private String ext;
+
+ private String matcher;
+
+ public void setOrg(String org) {
+ this.org = org;
+ }
+
+ public void setModule(String module) {
+ this.module = module;
+ }
+
+ public void setArtifact(String artifact) {
+ this.artifact = artifact;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public void setExt(String ext) {
+ this.ext = ext;
+ }
+
+ public void setMatcher(String matcher) {
+ this.matcher = matcher;
+ }
+
+ DefaultExcludeRule asRule(IvySettings settings) {
+ String matcherName = matcher == null ? PatternMatcher.EXACT : matcher;
+ String orgPattern = org == null ? PatternMatcher.ANY_EXPRESSION : org;
+ String modulePattern = module == null ? PatternMatcher.ANY_EXPRESSION : module;
+ String artifactPattern = artifact == null ? PatternMatcher.ANY_EXPRESSION : artifact;
+ String typePattern = type == null ? PatternMatcher.ANY_EXPRESSION : type;
+ String extPattern = ext == null ? typePattern : ext;
+ ArtifactId aid = new ArtifactId(new ModuleId(orgPattern, modulePattern), artifactPattern,
+ typePattern, extPattern);
+ return new DefaultExcludeRule(aid, settings.getMatcher(matcherName), null);
+ }
+}
diff --git a/src/java/org/apache/ivy/ant/IvyExtractFromSources.java b/src/java/org/apache/ivy/ant/IvyExtractFromSources.java
new file mode 100644
index 0000000..a7e6cd5
--- /dev/null
+++ b/src/java/org/apache/ivy/ant/IvyExtractFromSources.java
@@ -0,0 +1,217 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.ant;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.filters.LineContainsRegExp;
+import org.apache.tools.ant.filters.TokenFilter;
+import org.apache.tools.ant.taskdefs.Concat;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.FilterChain;
+import org.apache.tools.ant.types.RegularExpression;
+
+/**
+ * Extracts imports from a set of java sources and generate corresponding ivy file
+ */
+public class IvyExtractFromSources extends Task {
+ public static class Ignore {
+ private String packageName;
+
+ public String getPackage() {
+ return packageName;
+ }
+
+ public void setPackage(String package1) {
+ packageName = package1;
+ }
+ }
+
+ private String organisation;
+
+ private String module;
+
+ private String revision;
+
+ private String status;
+
+ private List ignoredPackaged = new ArrayList(); // List (String package)
+
+ private Map mapping = new HashMap(); // Map (String package -> ModuleRevisionId)
+
+ private Concat concat = new Concat();
+
+ private File to;
+
+ public void addConfiguredIgnore(Ignore ignore) {
+ ignoredPackaged.add(ignore.getPackage());
+ }
+
+ public File getTo() {
+ return to;
+ }
+
+ public void setTo(File to) {
+ this.to = to;
+ }
+
+ public String getModule() {
+ return module;
+ }
+
+ public void setModule(String module) {
+ this.module = module;
+ }
+
+ public String getOrganisation() {
+ return organisation;
+ }
+
+ public void setOrganisation(String organisation) {
+ this.organisation = organisation;
+ }
+
+ public String getRevision() {
+ return revision;
+ }
+
+ public void setRevision(String revision) {
+ this.revision = revision;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public void addConfiguredMapping(PackageMapping mapping) {
+ this.mapping.put(mapping.getPackage(), mapping.getModuleRevisionId());
+ }
+
+ public void addFileSet(FileSet fileSet) {
+ concat.addFileset(fileSet);
+ }
+
+ public void execute() throws BuildException {
+ configureConcat();
+ Writer out = new StringWriter();
+ concat.setWriter(out);
+ concat.execute();
+ Set importsSet = new HashSet(Arrays.asList(out.toString().split("\n")));
+ Set dependencies = new HashSet();
+ for (Iterator iter = importsSet.iterator(); iter.hasNext();) {
+ String pack = ((String) iter.next()).trim();
+ ModuleRevisionId mrid = getMapping(pack);
+ if (mrid != null) {
+ dependencies.add(mrid);
+ }
+ }
+ try {
+ PrintWriter writer = new PrintWriter(new FileOutputStream(to));
+ writer.println("<ivy-module version=\"1.0\">");
+ writer.println("\t<info organisation=\"" + organisation + "\"");
+ writer.println("\t module=\"" + module + "\"");
+ if (revision != null) {
+ writer.println("\t revision=\"" + revision + "\"");
+ }
+ if (status != null) {
+ writer.println("\t status=\"" + status + "\"");
+ } else {
+ writer.println("\t status=\"integration\"");
+ }
+ writer.println("\t/>");
+ if (!dependencies.isEmpty()) {
+ writer.println("\t<dependencies>");
+ for (Iterator iter = dependencies.iterator(); iter.hasNext();) {
+ ModuleRevisionId mrid = (ModuleRevisionId) iter.next();
+ writer.println("\t\t<dependency org=\"" + mrid.getOrganisation() + "\" name=\""
+ + mrid.getName() + "\" rev=\"" + mrid.getRevision() + "\"/>");
+ }
+ writer.println("\t</dependencies>");
+ }
+ writer.println("</ivy-module>");
+ writer.close();
+ log(dependencies.size() + " dependencies put in " + to);
+ } catch (FileNotFoundException e) {
+ throw new BuildException("impossible to create file " + to + ": " + e, e);
+ }
+ }
+
+ /**
+ * @param pack
+ * @return
+ */
+ private ModuleRevisionId getMapping(String pack) {
+ String askedPack = pack;
+ ModuleRevisionId ret = null;
+ while (ret == null && pack.length() > 0) {
+ if (ignoredPackaged.contains(pack)) {
+ return null;
+ }
+ ret = (ModuleRevisionId) mapping.get(pack);
+ int lastDotIndex = pack.lastIndexOf('.');
+ if (lastDotIndex != -1) {
+ pack = pack.substring(0, lastDotIndex);
+ } else {
+ break;
+ }
+ }
+ if (ret == null) {
+ log("no mapping found for " + askedPack, Project.MSG_VERBOSE);
+ }
+ return ret;
+ }
+
+ private void configureConcat() {
+ concat.setProject(getProject());
+ concat.setTaskName(getTaskName());
+ FilterChain filterChain = new FilterChain();
+ LineContainsRegExp lcre = new LineContainsRegExp();
+ RegularExpression regexp = new RegularExpression();
+ regexp.setPattern("^import .+;");
+ lcre.addConfiguredRegexp(regexp);
+ filterChain.add(lcre);
+ TokenFilter tf = new TokenFilter();
+ TokenFilter.ReplaceRegex rre = new TokenFilter.ReplaceRegex();
+ rre.setPattern("import (.+);.*");
+ rre.setReplace("\\1");
+ tf.add(rre);
+ filterChain.add(tf);
+ concat.addFilterChain(filterChain);
+ }
+}
diff --git a/src/java/org/apache/ivy/ant/IvyFindRevision.java b/src/java/org/apache/ivy/ant/IvyFindRevision.java
new file mode 100644
index 0000000..32171cf
--- /dev/null
+++ b/src/java/org/apache/ivy/ant/IvyFindRevision.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.ant;
+
+import org.apache.ivy.Ivy;
+import org.apache.ivy.core.module.id.ModuleId;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.resolve.ResolvedModuleRevision;
+import org.apache.ivy.core.settings.IvySettings;
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Look for the latest module in the repository matching the given criteria, and sets a set of
+ * properties according to what was found.
+ */
+public class IvyFindRevision extends IvyTask {
+ private String organisation;
+
+ private String module;
+
+ private String branch;
+
+ private String revision;
+
+ private String property = "ivy.revision";
+
+ public String getModule() {
+ return module;
+ }
+
+ public void setModule(String module) {
+ this.module = module;
+ }
+
+ public String getOrganisation() {
+ return organisation;
+ }
+
+ public void setOrganisation(String organisation) {
+ this.organisation = organisation;
+ }
+
+ public String getRevision() {
+ return revision;
+ }
+
+ public void setRevision(String revision) {
+ this.revision = revision;
+ }
+
+ public String getBranch() {
+ return branch;
+ }
+
+ public void setBranch(String branch) {
+ this.branch = branch;
+ }
+
+ public String getProperty() {
+ return property;
+ }
+
+ public void setProperty(String prefix) {
+ this.property = prefix;
+ }
+
+ public void doExecute() throws BuildException {
+ if (organisation == null) {
+ throw new BuildException("no organisation provided for ivy findrevision task");
+ }
+ if (module == null) {
+ throw new BuildException("no module name provided for ivy findrevision task");
+ }
+ if (revision == null) {
+ throw new BuildException("no revision provided for ivy findrevision task");
+ }
+
+ Ivy ivy = getIvyInstance();
+ IvySettings settings = ivy.getSettings();
+ if (branch == null) {
+ branch = settings.getDefaultBranch(new ModuleId(organisation, module));
+ }
+ ResolvedModuleRevision rmr = ivy.findModule(ModuleRevisionId.newInstance(organisation,
+ module, branch, revision));
+ if (rmr != null) {
+ getProject().setProperty(property, rmr.getId().getRevision());
+ }
+ }
+}
diff --git a/src/java/org/apache/ivy/ant/IvyInfo.java b/src/java/org/apache/ivy/ant/IvyInfo.java
new file mode 100644
index 0000000..0bbf939
--- /dev/null
+++ b/src/java/org/apache/ivy/ant/IvyInfo.java
@@ -0,0 +1,212 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.ant;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.ivy.Ivy;
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.Configuration;
+import org.apache.ivy.core.module.descriptor.Configuration.Visibility;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleId;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.resolve.ResolvedModuleRevision;
+import org.apache.ivy.core.settings.IvySettings;
+import org.apache.ivy.plugins.parser.ModuleDescriptorParserRegistry;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+
+/**
+ * Parses information about an ivy file and make them available in ant.
+ */
+public class IvyInfo extends IvyTask {
+ private File file = null;
+
+ private String organisation;
+
+ private String module;
+
+ private String branch;
+
+ private String revision;
+
+ private String property = "ivy";
+
+ public File getFile() {
+ return file;
+ }
+
+ public void setFile(File file) {
+ this.file = file;
+ }
+
+ public String getModule() {
+ return module;
+ }
+
+ public void setModule(String module) {
+ this.module = module;
+ }
+
+ public String getOrganisation() {
+ return organisation;
+ }
+
+ public void setOrganisation(String organisation) {
+ this.organisation = organisation;
+ }
+
+ public String getRevision() {
+ return revision;
+ }
+
+ public void setRevision(String revision) {
+ this.revision = revision;
+ }
+
+ public String getBranch() {
+ return branch;
+ }
+
+ public void setBranch(String branch) {
+ this.branch = branch;
+ }
+
+ public String getProperty() {
+ return property;
+ }
+
+ public void setProperty(String prefix) {
+ this.property = prefix;
+ }
+
+ public void doExecute() throws BuildException {
+ Ivy ivy = getIvyInstance();
+ IvySettings settings = ivy.getSettings();
+
+ try {
+ if (organisation != null || module != null || revision != null || branch != null) {
+ if (organisation == null) {
+ throw new BuildException("no organisation provided for ivy info task");
+ }
+ if (module == null) {
+ throw new BuildException("no module name provided for ivy info task");
+ }
+ if (revision == null) {
+ throw new BuildException("no revision provided for ivy info task");
+ }
+
+ if (branch == null) {
+ settings.getDefaultBranch(new ModuleId(organisation, module));
+ }
+ ResolvedModuleRevision rmr = ivy.findModule(ModuleRevisionId.newInstance(
+ organisation, module, branch, revision));
+ if (rmr != null) {
+ ModuleDescriptor md = rmr.getDescriptor();
+ ModuleRevisionId mrid = rmr.getId();
+ setProperties(md, mrid);
+ }
+ } else {
+ if (file == null) {
+ file = getProject().resolveFile(getProperty(settings, "ivy.dep.file"));
+ }
+ ModuleDescriptor md = ModuleDescriptorParserRegistry.getInstance().parseDescriptor(
+ settings, file.toURI().toURL(), doValidate(settings));
+ ModuleRevisionId mrid = md.getModuleRevisionId();
+ setProperties(md, mrid);
+ }
+ } catch (MalformedURLException e) {
+ throw new BuildException("unable to convert given ivy file to url: " + file + ": " + e,
+ e);
+ } catch (ParseException e) {
+ log(e.getMessage(), Project.MSG_ERR);
+ throw new BuildException("syntax errors in ivy file: " + e, e);
+ } catch (Exception e) {
+ throw new BuildException("impossible to resolve dependencies: " + e, e);
+ }
+ }
+
+ private void setProperties(ModuleDescriptor md, ModuleRevisionId mrid) {
+ getProject().setProperty(property + ".organisation", mrid.getOrganisation());
+ getProject().setProperty(property + ".module", mrid.getName());
+ if (mrid.getBranch() != null) {
+ getProject().setProperty(property + ".branch", mrid.getBranch());
+ }
+ getProject().setProperty(property + ".revision", mrid.getRevision());
+ getProject().setProperty(property + ".status", md.getStatus());
+ if (md.getPublicationDate() != null) {
+ getProject().setProperty(property + ".publication",
+ Long.toString(md.getPublicationDate().getTime()));
+ }
+
+ Map extra = mrid.getExtraAttributes();
+ for (Iterator iter = extra.entrySet().iterator(); iter.hasNext();) {
+ Entry entry = (Entry) iter.next();
+ getProject().setProperty(property + ".extra." + entry.getKey(),
+ (String) entry.getValue());
+ }
+
+ getProject().setProperty(property + ".configurations",
+ mergeConfs(md.getConfigurationsNames()));
+
+ // store the public configurations in a separate property
+ Configuration[] configs = md.getConfigurations();
+ List publicConfigsList = new ArrayList();
+ for (int i = 0; i < configs.length; i++) {
+ String name = configs[i].getName();
+ if (Visibility.PUBLIC.equals(configs[i].getVisibility())) {
+ publicConfigsList.add(name);
+ }
+
+ if (configs[i].getDescription() != null) {
+ getProject().setProperty(property + ".configuration." + name + ".desc",
+ configs[i].getDescription());
+ }
+ }
+ String[] publicConfigs = (String[]) publicConfigsList.toArray(new String[publicConfigsList
+ .size()]);
+ getProject().setProperty(property + ".public.configurations", mergeConfs(publicConfigs));
+
+ Artifact[] artifacts = md.getAllArtifacts();
+ for (int i = 0; i < artifacts.length; i++) {
+ int id = i + 1;
+ getProject()
+ .setProperty(property + ".artifact." + id + ".name", artifacts[i].getName());
+ getProject()
+ .setProperty(property + ".artifact." + id + ".type", artifacts[i].getType());
+ getProject().setProperty(property + ".artifact." + id + ".ext", artifacts[i].getExt());
+ getProject().setProperty(property + ".artifact." + id + ".conf",
+ mergeConfs(artifacts[i].getConfigurations()));
+
+ Map artiExtra = artifacts[i].getExtraAttributes();
+ for (Iterator iter = artiExtra.entrySet().iterator(); iter.hasNext();) {
+ Entry entry = (Entry) iter.next();
+ getProject().setProperty(property + ".artifact." + id + ".extra." + entry.getKey(),
+ (String) entry.getValue());
+ }
+ }
+ }
+}
diff --git a/src/java/org/apache/ivy/ant/IvyInstall.java b/src/java/org/apache/ivy/ant/IvyInstall.java
new file mode 100644
index 0000000..1982e19
--- /dev/null
+++ b/src/java/org/apache/ivy/ant/IvyInstall.java
@@ -0,0 +1,228 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.ant;
+
+import java.io.File;
+
+import org.apache.ivy.Ivy;
+import org.apache.ivy.core.install.InstallOptions;
+import org.apache.ivy.core.module.id.ModuleId;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.report.ResolveReport;
+import org.apache.ivy.core.settings.IvySettings;
+import org.apache.ivy.plugins.matcher.PatternMatcher;
+import org.apache.ivy.util.filter.FilterHelper;
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Allow to install a module or a set of module from repository to another one.
+ */
+public class IvyInstall extends IvyTask {
+ private String organisation;
+
+ private String module;
+
+ private String revision;
+
+ private String branch;
+
+ private String conf = "*";
+
+ private boolean overwrite = false;
+
+ private String from;
+
+ private String to;
+
+ private boolean transitive;
+
+ private String type;
+
+ private String matcher = PatternMatcher.EXACT;
+
+ private boolean haltOnFailure = true;
+
+ private boolean installOriginalMetadata = false;
+
+ public void doExecute() throws BuildException {
+ Ivy ivy = getIvyInstance();
+ IvySettings settings = ivy.getSettings();
+ if (organisation == null) {
+ throw new BuildException("no organisation provided for ivy publish task: "
+ + "It can either be set explicitely via the attribute 'organisation' "
+ + "or via 'ivy.organisation' property or a prior call to <resolve/>");
+ }
+ if (module == null && PatternMatcher.EXACT.equals(matcher)) {
+ throw new BuildException("no module name provided for ivy publish task: "
+ + "It can either be set explicitely via the attribute 'module' "
+ + "or via 'ivy.module' property or a prior call to <resolve/>");
+ } else if (module == null && !PatternMatcher.EXACT.equals(matcher)) {
+ module = PatternMatcher.ANY_EXPRESSION;
+ }
+ if (revision == null && PatternMatcher.EXACT.equals(matcher)) {
+ throw new BuildException("no module revision provided for ivy publish task: "
+ + "It can either be set explicitely via the attribute 'revision' "
+ + "or via 'ivy.revision' property or a prior call to <resolve/>");
+ } else if (revision == null && !PatternMatcher.EXACT.equals(matcher)) {
+ revision = PatternMatcher.ANY_EXPRESSION;
+ }
+ if (branch == null && PatternMatcher.EXACT.equals(matcher)) {
+ branch = settings.getDefaultBranch(ModuleId.newInstance(organisation, module));
+ } else if (branch == null && !PatternMatcher.EXACT.equals(matcher)) {
+ branch = PatternMatcher.ANY_EXPRESSION;
+ }
+ if (from == null) {
+ throw new BuildException(
+ "no from resolver name: please provide it through parameter 'from'");
+ }
+ if (to == null) {
+ throw new BuildException(
+ "no to resolver name: please provide it through parameter 'to'");
+ }
+ ModuleRevisionId mrid = ModuleRevisionId
+ .newInstance(organisation, module, branch, revision);
+
+ ResolveReport report;
+ try {
+ report = ivy.install(
+ mrid,
+ from,
+ to,
+ new InstallOptions().setTransitive(transitive).setValidate(doValidate(settings))
+ .setOverwrite(overwrite).setConfs(conf.split(","))
+ .setArtifactFilter(FilterHelper.getArtifactTypeFilter(type))
+ .setMatcherName(matcher)
+ .setInstallOriginalMetadata(installOriginalMetadata));
+ } catch (Exception e) {
+ throw new BuildException("impossible to install " + mrid + ": " + e, e);
+ }
+
+ if (report.hasError() && isHaltonfailure()) {
+ throw new BuildException(
+ "Problem happened while installing modules - see output for details");
+ }
+ }
+
+ public boolean isHaltonfailure() {
+ return haltOnFailure;
+ }
+
+ public void setHaltonfailure(boolean haltOnFailure) {
+ this.haltOnFailure = haltOnFailure;
+ }
+
+ public void setCache(File cache) {
+ cacheAttributeNotSupported();
+ }
+
+ public String getModule() {
+ return module;
+ }
+
+ public void setModule(String module) {
+ this.module = module;
+ }
+
+ public String getBranch() {
+ return branch;
+ }
+
+ public void setBranch(String branch) {
+ this.branch = branch;
+ }
+
+ public String getOrganisation() {
+ return organisation;
+ }
+
+ public void setOrganisation(String organisation) {
+ this.organisation = organisation;
+ }
+
+ public String getRevision() {
+ return revision;
+ }
+
+ public void setRevision(String revision) {
+ this.revision = revision;
+ }
+
+ public boolean isOverwrite() {
+ return overwrite;
+ }
+
+ public void setOverwrite(boolean overwrite) {
+ this.overwrite = overwrite;
+ }
+
+ public String getFrom() {
+ return from;
+ }
+
+ public void setFrom(String from) {
+ this.from = from;
+ }
+
+ public String getTo() {
+ return to;
+ }
+
+ public void setTo(String to) {
+ this.to = to;
+ }
+
+ public boolean isTransitive() {
+ return transitive;
+ }
+
+ public void setTransitive(boolean transitive) {
+ this.transitive = transitive;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public String getMatcher() {
+ return matcher;
+ }
+
+ public void setMatcher(String matcher) {
+ this.matcher = matcher;
+ }
+
+ public String getConf() {
+ return conf;
+ }
+
+ public void setConf(String conf) {
+ this.conf = conf;
+ }
+
+ public boolean isInstallOriginalMetadata() {
+ return installOriginalMetadata;
+ }
+
+ public void setInstallOriginalMetadata(boolean installOriginalMetadata) {
+ this.installOriginalMetadata = installOriginalMetadata;
+ }
+}
diff --git a/src/java/org/apache/ivy/ant/IvyListModules.java b/src/java/org/apache/ivy/ant/IvyListModules.java
new file mode 100644
index 0000000..a516571
--- /dev/null
+++ b/src/java/org/apache/ivy/ant/IvyListModules.java
@@ -0,0 +1,158 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.ant;
+
+import org.apache.ivy.Ivy;
+import org.apache.ivy.core.IvyPatternHelper;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.search.SearchEngine;
+import org.apache.ivy.core.settings.IvySettings;
+import org.apache.ivy.plugins.matcher.PatternMatcher;
+import org.apache.ivy.plugins.resolver.DependencyResolver;
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Look for modules in the repository matching the given criteria, and sets a set of properties
+ * according to what was found.
+ */
+public class IvyListModules extends IvyTask {
+ private String organisation;
+
+ private String module;
+
+ private String branch = PatternMatcher.ANY_EXPRESSION;
+
+ private String revision;
+
+ private String matcher = PatternMatcher.EXACT_OR_REGEXP;
+
+ private String property;
+
+ private String value;
+
+ private String resolver;
+
+ public String getMatcher() {
+ return matcher;
+ }
+
+ public void setMatcher(String matcher) {
+ this.matcher = matcher;
+ }
+
+ public String getModule() {
+ return module;
+ }
+
+ public void setModule(String module) {
+ this.module = module;
+ }
+
+ public String getProperty() {
+ return property;
+ }
+
+ public void setProperty(String name) {
+ this.property = name;
+ }
+
+ public String getOrganisation() {
+ return organisation;
+ }
+
+ public void setOrganisation(String organisation) {
+ this.organisation = organisation;
+ }
+
+ public String getRevision() {
+ return revision;
+ }
+
+ public void setRevision(String revision) {
+ this.revision = revision;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ public String getBranch() {
+ return branch;
+ }
+
+ public void setBranch(String branch) {
+ this.branch = branch;
+ }
+
+ public void setResolver(String resolver) {
+ this.resolver = resolver;
+ }
+
+ public String getResolver() {
+ return resolver;
+ }
+
+ public void doExecute() throws BuildException {
+ if (organisation == null) {
+ throw new BuildException("no organisation provided for ivy listmodules task");
+ }
+ if (module == null) {
+ throw new BuildException("no module name provided for ivy listmodules task");
+ }
+ if (revision == null) {
+ throw new BuildException("no revision provided for ivy listmodules task");
+ }
+ if (property == null) {
+ throw new BuildException("no property provided for ivy listmodules task");
+ }
+ if (value == null) {
+ throw new BuildException("no value provided for ivy listmodules task");
+ }
+
+ Ivy ivy = getIvyInstance();
+ IvySettings settings = ivy.getSettings();
+
+ SearchEngine searcher = new SearchEngine(settings);
+ PatternMatcher patternMatcher = settings.getMatcher(matcher);
+
+ ModuleRevisionId[] mrids;
+ if (resolver == null) {
+ mrids = searcher.listModules(
+ ModuleRevisionId.newInstance(organisation, module, branch, revision),
+ patternMatcher);
+ } else {
+ DependencyResolver depResolver = settings.getResolver(resolver);
+ if (depResolver == null) {
+ throw new BuildException("Unknown resolver: " + resolver);
+ }
+ mrids = searcher.listModules(depResolver,
+ ModuleRevisionId.newInstance(organisation, module, branch, revision),
+ patternMatcher);
+ }
+
+ for (int i = 0; i < mrids.length; i++) {
+ String name = IvyPatternHelper.substitute(settings.substitute(property), mrids[i]);
+ String value = IvyPatternHelper.substitute(settings.substitute(this.value), mrids[i]);
+ getProject().setProperty(name, value);
+ }
+ }
+}
diff --git a/src/java/org/apache/ivy/ant/IvyMakePom.java b/src/java/org/apache/ivy/ant/IvyMakePom.java
new file mode 100644
index 0000000..406b155
--- /dev/null
+++ b/src/java/org/apache/ivy/ant/IvyMakePom.java
@@ -0,0 +1,308 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.ant;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.plugins.parser.m2.PomModuleDescriptorWriter;
+import org.apache.ivy.plugins.parser.m2.PomWriterOptions;
+import org.apache.ivy.plugins.parser.xml.XmlModuleDescriptorParser;
+import org.apache.ivy.util.FileUtil;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+
+/**
+ * Convert an ivy file to a pom
+ */
+public class IvyMakePom extends IvyTask {
+ public class Mapping {
+ private String conf;
+
+ private String scope;
+
+ public String getConf() {
+ return conf;
+ }
+
+ public void setConf(String conf) {
+ this.conf = conf;
+ }
+
+ public String getScope() {
+ return scope;
+ }
+
+ public void setScope(String scope) {
+ this.scope = scope;
+ }
+ }
+
+ public class Dependency {
+ private String group = null;
+
+ private String artifact = null;
+
+ private String version = null;
+
+ private String scope = null;
+
+ private String type = null;
+
+ private String classifier = null;
+
+ private boolean optional = false;
+
+ public String getGroup() {
+ return group;
+ }
+
+ public void setGroup(String group) {
+ this.group = group;
+ }
+
+ public String getArtifact() {
+ return artifact;
+ }
+
+ public void setArtifact(String artifact) {
+ this.artifact = artifact;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public void setVersion(String version) {
+ this.version = version;
+ }
+
+ public String getScope() {
+ return scope;
+ }
+
+ public void setScope(String scope) {
+ this.scope = scope;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public String getClassifier() {
+ return classifier;
+ }
+
+ public void setClassifier(String classifier) {
+ this.classifier = classifier;
+ }
+
+ public boolean getOptional() {
+ return optional;
+ }
+
+ public void setOptional(boolean optional) {
+ this.optional = optional;
+ }
+ }
+
+ private String artifactName;
+
+ private String artifactPackaging;
+
+ private File pomFile = null;
+
+ private File headerFile = null;
+
+ private File templateFile = null;
+
+ private boolean printIvyInfo = true;
+
+ private String conf;
+
+ private File ivyFile = null;
+
+ private String description;
+
+ private Collection mappings = new ArrayList();
+
+ private Collection dependencies = new ArrayList();
+
+ public File getPomFile() {
+ return pomFile;
+ }
+
+ public void setPomFile(File file) {
+ pomFile = file;
+ }
+
+ public File getIvyFile() {
+ return ivyFile;
+ }
+
+ public void setIvyFile(File ivyFile) {
+ this.ivyFile = ivyFile;
+ }
+
+ public File getHeaderFile() {
+ return headerFile;
+ }
+
+ public void setHeaderFile(File headerFile) {
+ this.headerFile = headerFile;
+ }
+
+ public File getTemplateFile() {
+ return templateFile;
+ }
+
+ public void setTemplateFile(File templateFile) {
+ this.templateFile = templateFile;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public boolean isPrintIvyInfo() {
+ return printIvyInfo;
+ }
+
+ public void setPrintIvyInfo(boolean printIvyInfo) {
+ this.printIvyInfo = printIvyInfo;
+ }
+
+ public String getConf() {
+ return conf;
+ }
+
+ public void setConf(String conf) {
+ this.conf = conf;
+ }
+
+ public String getArtifactName() {
+ return artifactName;
+ }
+
+ public void setArtifactName(String artifactName) {
+ this.artifactName = artifactName;
+ }
+
+ public String getArtifactPackaging() {
+ return artifactPackaging;
+ }
+
+ public void setArtifactPackaging(String artifactPackaging) {
+ this.artifactPackaging = artifactPackaging;
+ }
+
+ public Mapping createMapping() {
+ Mapping mapping = new Mapping();
+ this.mappings.add(mapping);
+ return mapping;
+ }
+
+ public Dependency createDependency() {
+ Dependency dependency = new Dependency();
+ this.dependencies.add(dependency);
+ return dependency;
+ }
+
+ public void doExecute() throws BuildException {
+ try {
+ if (ivyFile == null) {
+ throw new BuildException("source ivy file is required for makepom task");
+ }
+ if (pomFile == null) {
+ throw new BuildException("destination pom file is required for makepom task");
+ }
+ ModuleDescriptor md = XmlModuleDescriptorParser.getInstance().parseDescriptor(
+ getSettings(), ivyFile.toURI().toURL(), false);
+ PomModuleDescriptorWriter.write(md, pomFile, getPomWriterOptions());
+ } catch (MalformedURLException e) {
+ throw new BuildException("unable to convert given ivy file to url: " + ivyFile + ": "
+ + e, e);
+ } catch (ParseException e) {
+ log(e.getMessage(), Project.MSG_ERR);
+ throw new BuildException("syntax errors in ivy file " + ivyFile + ": " + e, e);
+ } catch (Exception e) {
+ throw new BuildException("impossible convert given ivy file to pom file: " + e
+ + " from=" + ivyFile + " to=" + pomFile, e);
+ }
+ }
+
+ private PomWriterOptions getPomWriterOptions() throws IOException {
+ PomWriterOptions options = new PomWriterOptions();
+ options.setConfs(splitConfs(conf)).setArtifactName(getArtifactName())
+ .setArtifactPackaging(getArtifactPackaging()).setPrintIvyInfo(isPrintIvyInfo())
+ .setDescription(getDescription()).setExtraDependencies(getDependencies())
+ .setTemplate(getTemplateFile());
+
+ if (!mappings.isEmpty()) {
+ options.setMapping(new PomWriterOptions.ConfigurationScopeMapping(getMappingsMap()));
+ }
+
+ if (headerFile != null) {
+ options.setLicenseHeader(FileUtil.readEntirely(getHeaderFile()));
+ }
+
+ return options;
+ }
+
+ private Map getMappingsMap() {
+ Map mappingsMap = new HashMap();
+ for (Iterator iter = mappings.iterator(); iter.hasNext();) {
+ Mapping mapping = (Mapping) iter.next();
+ String[] mappingConfs = splitConfs(mapping.getConf());
+ for (int i = 0; i < mappingConfs.length; i++) {
+ if (!mappingsMap.containsKey(mappingConfs[i])) {
+ mappingsMap.put(mappingConfs[i], mapping.getScope());
+ }
+ }
+ }
+ return mappingsMap;
+ }
+
+ private List getDependencies() {
+ List result = new ArrayList();
+ for (Iterator iter = dependencies.iterator(); iter.hasNext();) {
+ Dependency dependency = (Dependency) iter.next();
+ result.add(new PomWriterOptions.ExtraDependency(dependency.getGroup(), dependency
+ .getArtifact(), dependency.getVersion(), dependency.getScope(), dependency
+ .getType(), dependency.getClassifier(), dependency.getOptional()));
+ }
+ return result;
+ }
+}
diff --git a/src/java/org/apache/ivy/ant/IvyOverride.java b/src/java/org/apache/ivy/ant/IvyOverride.java
new file mode 100644
index 0000000..ac7e6ad
--- /dev/null
+++ b/src/java/org/apache/ivy/ant/IvyOverride.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.ant;
+
+import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
+import org.apache.ivy.core.module.descriptor.OverrideDependencyDescriptorMediator;
+import org.apache.ivy.core.module.id.ModuleId;
+import org.apache.ivy.core.settings.IvySettings;
+import org.apache.ivy.plugins.matcher.PatternMatcher;
+
+public class IvyOverride {
+
+ private String org;
+
+ private String module;
+
+ private String branch;
+
+ private String rev;
+
+ private String matcher;
+
+ public void setOrg(String org) {
+ this.org = org;
+ }
+
+ public void setModule(String module) {
+ this.module = module;
+ }
+
+ public void setBranch(String branch) {
+ this.branch = branch;
+ }
+
+ public void setRev(String rev) {
+ this.rev = rev;
+ }
+
+ public void setMatcher(String matcher) {
+ this.matcher = matcher;
+ }
+
+ void addOverride(DefaultModuleDescriptor md, IvySettings settings) {
+ String matcherName = matcher == null ? PatternMatcher.EXACT : matcher;
+ String orgPattern = org == null ? PatternMatcher.ANY_EXPRESSION : org;
+ String modulePattern = module == null ? PatternMatcher.ANY_EXPRESSION : module;
+ md.addDependencyDescriptorMediator(new ModuleId(orgPattern, modulePattern),
+ settings.getMatcher(matcherName), new OverrideDependencyDescriptorMediator(branch, rev));
+ }
+
+}
diff --git a/src/java/org/apache/ivy/ant/IvyPostResolveTask.java b/src/java/org/apache/ivy/ant/IvyPostResolveTask.java
new file mode 100644
index 0000000..0e0e6fe
--- /dev/null
+++ b/src/java/org/apache/ivy/ant/IvyPostResolveTask.java
@@ -0,0 +1,460 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.ant;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Iterator;
+
+import org.apache.ivy.Ivy;
+import org.apache.ivy.core.cache.ResolutionCacheManager;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleId;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.report.ResolveReport;
+import org.apache.ivy.core.resolve.ResolveOptions;
+import org.apache.ivy.core.settings.IvySettings;
+import org.apache.ivy.util.Message;
+import org.apache.ivy.util.StringUtils;
+import org.apache.ivy.util.filter.Filter;
+import org.apache.ivy.util.filter.FilterHelper;
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Base class for tasks needing to be performed after a resolve.
+ */
+public abstract class IvyPostResolveTask extends IvyTask {
+ private String conf;
+
+ private boolean haltOnFailure = true;
+
+ private boolean transitive = true;
+
+ private boolean inline = false;
+
+ private String organisation;
+
+ private String branch = null;
+
+ private String module;
+
+ private String revision = "latest.integration";
+
+ private String resolveId;
+
+ private String type;
+
+ private File file;
+
+ private Filter artifactFilter = null;
+
+ private boolean useOrigin = false;
+
+ private Boolean keep = null;
+
+ private boolean refresh = false;
+
+ private String resolveMode = null;
+
+ private String log = ResolveOptions.LOG_DEFAULT;
+
+ private boolean changing = false;
+
+ private IvyResolve resolve = new IvyResolve();
+
+ public boolean isUseOrigin() {
+ return useOrigin;
+ }
+
+ public void setUseOrigin(boolean useOrigin) {
+ this.useOrigin = useOrigin;
+ }
+
+ public String getLog() {
+ return log;
+ }
+
+ public void setLog(String log) {
+ this.log = log;
+ }
+
+ public IvyDependency createDependency() {
+ return resolve.createDependency();
+ }
+
+ public IvyExclude createExclude() {
+ return resolve.createExclude();
+ }
+
+ public IvyConflict createConflict() {
+ return resolve.createConflict();
+ }
+
+ protected void prepareAndCheck() {
+ Ivy ivy = getIvyInstance();
+ IvySettings settings = ivy.getSettings();
+
+ boolean orgAndModSetManually = (organisation != null) && (module != null);
+
+ organisation = getProperty(organisation, settings, "ivy.organisation");
+ module = getProperty(module, settings, "ivy.module");
+
+ if (file == null) {
+ String fileName = getProperty(settings, "ivy.resolved.file", resolveId);
+ if (fileName != null) {
+ file = getProject().resolveFile(fileName);
+ }
+ }
+
+ if (isInline()) {
+ conf = conf == null ? "*" : conf;
+ if (organisation == null) {
+ throw new BuildException(
+ "no organisation provided for ivy cache task in inline mode: "
+ + "It can either be set explicitely via the attribute 'organisation' "
+ + "or via 'ivy.organisation' property");
+ }
+ if (module == null) {
+ throw new BuildException(
+ "no module name provided for ivy cache task in inline mode: "
+ + "It can either be set explicitely via the attribute 'module' "
+ + "or via 'ivy.module' property");
+ }
+ String[] toResolve = getConfsToResolve(getOrganisation(), getModule() + "-caller",
+ conf, true);
+ // When we make an inline resolution, we can not resolve private confs.
+ for (int i = 0; i < toResolve.length; i++) {
+ if ("*".equals(toResolve[i])) {
+ toResolve[i] = "*(public)";
+ }
+ }
+ if (toResolve.length > 0) {
+ Message.verbose("using inline mode to resolve " + getOrganisation() + " "
+ + getModule() + " " + getRevision() + " ("
+ + StringUtils.join(toResolve, ", ") + ")");
+ IvyResolve resolve = setupResolve(isHaltonfailure(), isUseOrigin());
+ resolve.setOrganisation(getOrganisation());
+ resolve.setModule(getModule());
+ resolve.setBranch(getBranch());
+ resolve.setRevision(getRevision());
+ resolve.setInline(true);
+ resolve.setChanging(isChanging());
+ resolve.setConf(conf);
+ resolve.setResolveId(resolveId);
+ resolve.setTransitive(isTransitive());
+ resolve.execute();
+ } else {
+ Message.verbose("inline resolve already done for " + getOrganisation() + " "
+ + getModule() + " " + getRevision() + " (" + conf + ")");
+ }
+ if ("*".equals(conf)) {
+ conf = StringUtils.join(
+ getResolvedConfigurations(getOrganisation(), getModule() + "-caller", true),
+ ", ");
+ }
+ } else {
+ Message.debug("using standard ensure resolved");
+
+ // if the organization and module has been manually specified, we'll reuse the resolved
+ // data from another build (there is no way to know which configurations were resolved
+ // there (TODO: maybe we can check which reports exist and extract the configurations
+ // from these report names?)
+ if (!orgAndModSetManually) {
+ ensureResolved(settings);
+ }
+
+ conf = getProperty(conf, settings, "ivy.resolved.configurations");
+ if ("*".equals(conf)) {
+ conf = getProperty(settings, "ivy.resolved.configurations");
+ if (conf == null) {
+ throw new BuildException("bad conf provided for ivy cache task: "
+ + "'*' can only be used with a prior call to <resolve/>");
+ }
+ }
+ }
+ organisation = getProperty(organisation, settings, "ivy.organisation");
+ module = getProperty(module, settings, "ivy.module");
+ if (organisation == null) {
+ throw new BuildException("no organisation provided for ivy cache task: "
+ + "It can either be set explicitely via the attribute 'organisation' "
+ + "or via 'ivy.organisation' property or a prior call to <resolve/>");
+ }
+ if (module == null) {
+ throw new BuildException("no module name provided for ivy cache task: "
+ + "It can either be set explicitely via the attribute 'module' "
+ + "or via 'ivy.module' property or a prior call to <resolve/>");
+ }
+ if (conf == null) {
+ throw new BuildException("no conf provided for ivy cache task: "
+ + "It can either be set explicitely via the attribute 'conf' or "
+ + "via 'ivy.resolved.configurations' property or a prior call to <resolve/>");
+ }
+
+ artifactFilter = FilterHelper.getArtifactTypeFilter(type);
+ }
+
+ protected void ensureResolved(IvySettings settings) {
+ String requestedConfigs = getProperty(getConf(), settings, "ivy.resolved.configurations");
+
+ String[] confs = null;
+ if (getResolveId() != null) {
+ confs = getConfsToResolve(getResolveId(), requestedConfigs);
+ } else {
+ confs = getConfsToResolve(getOrganisation(), getModule(), requestedConfigs, false);
+ }
+
+ if (confs.length > 0) {
+ IvyResolve resolve = setupResolve(isHaltonfailure(), isUseOrigin());
+ resolve.setFile(getFile());
+ resolve.setTransitive(isTransitive());
+ resolve.setConf(StringUtils.join(confs, ", "));
+ resolve.setResolveId(getResolveId());
+ resolve.execute();
+ }
+ }
+
+ protected String[] getConfsToResolve(String org, String module, String conf, boolean strict) {
+ ModuleDescriptor reference = (ModuleDescriptor) getResolvedDescriptor(org, module, strict);
+ String[] rconfs = getResolvedConfigurations(org, module, strict);
+ return getConfsToResolve(reference, conf, rconfs);
+ }
+
+ protected String[] getConfsToResolve(String resolveId, String conf) {
+ ModuleDescriptor reference = (ModuleDescriptor) getResolvedDescriptor(resolveId, false);
+ if (reference == null) {
+ // assume the module has been resolved outside this build, resolve the required
+ // configurations again
+ // TODO: find a way to discover which confs were resolved by that previous resolve
+ if (conf == null) {
+ return new String[] {"*"};
+ } else {
+ return splitConfs(conf);
+ }
+ }
+ String[] rconfs = (String[]) getProject().getReference(
+ "ivy.resolved.configurations.ref." + resolveId);
+ return getConfsToResolve(reference, conf, rconfs);
+ }
+
+ private String[] getConfsToResolve(ModuleDescriptor reference, String conf, String[] rconfs) {
+ Message.debug("calculating configurations to resolve");
+
+ if (reference == null) {
+ Message.debug("module not yet resolved, all confs still need to be resolved");
+ if (conf == null) {
+ return new String[] {"*"};
+ } else {
+ return splitConfs(conf);
+ }
+ } else if (conf != null) {
+ String[] confs;
+ if ("*".equals(conf)) {
+ confs = reference.getConfigurationsNames();
+ } else {
+ confs = splitConfs(conf);
+ }
+
+ HashSet rconfsSet = new HashSet(Arrays.asList(rconfs));
+
+ // for each resolved configuration, check if the report still exists
+ ResolutionCacheManager cache = getSettings().getResolutionCacheManager();
+ for (Iterator it = rconfsSet.iterator(); it.hasNext();) {
+ String resolvedConf = (String) it.next();
+ String resolveId = getResolveId();
+ if (resolveId == null) {
+ resolveId = ResolveOptions.getDefaultResolveId(reference);
+ }
+ File report = cache.getConfigurationResolveReportInCache(resolveId, resolvedConf);
+ if (!report.exists()) {
+ // the report doesn't exist any longer, we have to recreate it...
+ it.remove();
+ }
+ }
+
+ HashSet confsSet = new HashSet(Arrays.asList(confs));
+ Message.debug("resolved configurations: " + rconfsSet);
+ Message.debug("asked configurations: " + confsSet);
+ confsSet.removeAll(rconfsSet);
+ Message.debug("to resolve configurations: " + confsSet);
+ return (String[]) confsSet.toArray(new String[confsSet.size()]);
+ } else {
+ Message.debug("module already resolved, no configuration to resolve");
+ return new String[0];
+ }
+
+ }
+
+ protected IvyResolve setupResolve(boolean haltOnFailure, boolean useOrigin) {
+ Message.verbose("no resolved descriptor found: launching default resolve");
+ resolve.setTaskName(getTaskName());
+ resolve.setProject(getProject());
+ resolve.setHaltonfailure(haltOnFailure);
+ resolve.setUseOrigin(useOrigin);
+ resolve.setValidate(doValidate(getSettings()));
+ resolve.setKeep(isKeep());
+ resolve.setRefresh(isRefresh());
+ resolve.setLog(getLog());
+ resolve.setSettingsRef(getSettingsRef());
+ resolve.setResolveMode(getResolveMode());
+ return resolve;
+ }
+
+ protected ModuleRevisionId getResolvedMrid() {
+ return new ModuleRevisionId(getResolvedModuleId(),
+ getRevision() == null ? Ivy.getWorkingRevision() : getRevision());
+ }
+
+ protected ModuleId getResolvedModuleId() {
+ return isInline() ? new ModuleId(getOrganisation(), getModule() + "-caller")
+ : new ModuleId(getOrganisation(), getModule());
+ }
+
+ protected ResolveReport getResolvedReport() {
+ return getResolvedReport(getOrganisation(), isInline() ? getModule() + "-caller"
+ : getModule(), resolveId);
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public String getConf() {
+ return conf;
+ }
+
+ public void setConf(String conf) {
+ this.conf = conf;
+ }
+
+ public String getModule() {
+ return module;
+ }
+
+ public void setModule(String module) {
+ this.module = module;
+ }
+
+ public String getOrganisation() {
+ return organisation;
+ }
+
+ public void setOrganisation(String organisation) {
+ this.organisation = organisation;
+ }
+
+ public String getBranch() {
+ return branch;
+ }
+
+ public void setBranch(String branch) {
+ this.branch = branch;
+ }
+
+ public boolean isHaltonfailure() {
+ return haltOnFailure;
+ }
+
+ public void setHaltonfailure(boolean haltOnFailure) {
+ this.haltOnFailure = haltOnFailure;
+ }
+
+ public void setCache(File cache) {
+ cacheAttributeNotSupported();
+ }
+
+ public String getRevision() {
+ return revision;
+ }
+
+ public void setRevision(String rev) {
+ revision = rev;
+ }
+
+ public Filter getArtifactFilter() {
+ return artifactFilter;
+ }
+
+ public boolean isTransitive() {
+ return transitive;
+ }
+
+ public void setTransitive(boolean transitive) {
+ this.transitive = transitive;
+ }
+
+ public boolean isInline() {
+ return inline;
+ }
+
+ public void setInline(boolean inline) {
+ this.inline = inline;
+ }
+
+ public void setResolveId(String resolveId) {
+ this.resolveId = resolveId;
+ }
+
+ public String getResolveId() {
+ return resolveId;
+ }
+
+ public void setFile(File file) {
+ this.file = file;
+ }
+
+ public File getFile() {
+ return file;
+ }
+
+ public void setKeep(boolean keep) {
+ this.keep = Boolean.valueOf(keep);
+ }
+
+ public boolean isKeep() {
+ return this.keep == null ? !isInline() : this.keep.booleanValue();
+ }
+
+ public void setChanging(boolean changing) {
+ this.changing = changing;
+ }
+
+ public boolean isChanging() {
+ return this.changing;
+ }
+
+ public void setRefresh(boolean refresh) {
+ this.refresh = refresh;
+ }
+
+ public boolean isRefresh() {
+ return refresh;
+ }
+
+ public String getResolveMode() {
+ return resolveMode;
+ }
+
+ public void setResolveMode(String resolveMode) {
+ this.resolveMode = resolveMode;
+ }
+}
diff --git a/src/java/org/apache/ivy/ant/IvyPublish.java b/src/java/org/apache/ivy/ant/IvyPublish.java
new file mode 100644
index 0000000..d46b701
--- /dev/null
+++ b/src/java/org/apache/ivy/ant/IvyPublish.java
@@ -0,0 +1,481 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.ant;
+
+import java.io.File;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.ivy.Ivy;
+import org.apache.ivy.core.IvyPatternHelper;
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.id.ArtifactRevisionId;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.publish.PublishOptions;
+import org.apache.ivy.core.settings.IvySettings;
+import org.apache.ivy.util.DateUtil;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DynamicAttribute;
+
+/**
+ * This task allow to publish a module revision to an Ivy repository.
+ */
+public class IvyPublish extends IvyTask {
+ private String organisation;
+
+ private String module;
+
+ private String revision;
+
+ private String pubRevision;
+
+ private String srcivypattern;
+
+ private String status;
+
+ private String conf = null;
+
+ private String pubdate;
+
+ private String deliverTarget;
+
+ private String publishResolverName = null;
+
+ private List artifactspattern = new ArrayList();
+
+ private File deliveryList;
+
+ private boolean publishivy = true;
+
+ private boolean warnonmissing = true;
+
+ private boolean haltonmissing = true;
+
+ private boolean overwrite = false;
+
+ private boolean update = false;
+
+ private boolean merge = true;
+
+ private boolean replacedynamicrev = true;
+
+ private boolean forcedeliver;
+
+ private Collection artifacts = new ArrayList();
+
+ private String pubBranch;
+
+ public void setCache(File cache) {
+ cacheAttributeNotSupported();
+ }
+
+ public String getSrcivypattern() {
+ return srcivypattern;
+ }
+
+ public void setSrcivypattern(String destivypattern) {
+ srcivypattern = destivypattern;
+ }
+
+ /**
+ * @deprecated use {@link #getSrcivypattern()} instead.
+ */
+ @Deprecated
+ public String getDeliverivypattern() {
+ return srcivypattern;
+ }
+
+ /**
+ * @deprecated use {@link #setSrcivypattern(String)} instead.
+ */
+ @Deprecated
+ public void setDeliverivypattern(String destivypattern) {
+ srcivypattern = destivypattern;
+ }
+
+ public String getModule() {
+ return module;
+ }
+
+ public void setModule(String module) {
+ this.module = module;
+ }
+
+ public String getOrganisation() {
+ return organisation;
+ }
+
+ public void setOrganisation(String organisation) {
+ this.organisation = organisation;
+ }
+
+ public String getPubdate() {
+ return pubdate;
+ }
+
+ public void setPubdate(String pubdate) {
+ this.pubdate = pubdate;
+ }
+
+ public String getPubrevision() {
+ return pubRevision;
+ }
+
+ public void setPubrevision(String pubRevision) {
+ this.pubRevision = pubRevision;
+ }
+
+ public String getPubbranch() {
+ return pubBranch;
+ }
+
+ public void setPubbranch(String pubBranch) {
+ this.pubBranch = pubBranch;
+ }
+
+ public String getRevision() {
+ return revision;
+ }
+
+ public void setRevision(String revision) {
+ this.revision = revision;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public void setConf(String conf) {
+ this.conf = conf;
+ }
+
+ public void setDelivertarget(String deliverTarget) {
+ this.deliverTarget = deliverTarget;
+ }
+
+ public void setDeliveryList(File deliveryList) {
+ this.deliveryList = deliveryList;
+ }
+
+ public String getResolver() {
+ return publishResolverName;
+ }
+
+ public void setResolver(String publishResolverName) {
+ this.publishResolverName = publishResolverName;
+ }
+
+ public String getArtifactspattern() {
+ return (String) (artifactspattern.isEmpty() ? null : artifactspattern.get(0));
+ }
+
+ public void setArtifactspattern(String artifactsPattern) {
+ artifactspattern.clear();
+ artifactspattern.add(artifactsPattern);
+ }
+
+ public void addArtifactspattern(String artifactsPattern) {
+ artifactspattern.add(artifactsPattern);
+ }
+
+ public void addConfiguredArtifacts(ArtifactsPattern p) {
+ artifactspattern.add(p.getPattern());
+ }
+
+ public boolean isReplacedynamicrev() {
+ return replacedynamicrev;
+ }
+
+ public void setReplacedynamicrev(boolean replacedynamicrev) {
+ this.replacedynamicrev = replacedynamicrev;
+ }
+
+ public boolean isMerge() {
+ return merge;
+ }
+
+ public void setMerge(boolean merge) {
+ this.merge = merge;
+ }
+
+ public void doExecute() throws BuildException {
+ Ivy ivy = getIvyInstance();
+ IvySettings settings = ivy.getSettings();
+
+ organisation = getProperty(organisation, settings, "ivy.organisation");
+ module = getProperty(module, settings, "ivy.module");
+ revision = getProperty(revision, settings, "ivy.revision");
+ pubBranch = getProperty(pubBranch, settings, "ivy.deliver.branch");
+ pubRevision = getProperty(pubRevision, settings, "ivy.deliver.revision");
+ if (artifactspattern.isEmpty()) {
+ String p = getProperty(null, settings, "ivy.publish.src.artifacts.pattern");
+ if (p != null) {
+ artifactspattern.add(p);
+ }
+ }
+ if (srcivypattern == null) {
+ srcivypattern = getArtifactspattern();
+ }
+ status = getProperty(status, settings, "ivy.status");
+ if (organisation == null) {
+ throw new BuildException("no organisation provided for ivy publish task: "
+ + "It can either be set explicitely via the attribute 'organisation' "
+ + "or via 'ivy.organisation' property or a prior call to <resolve/>");
+ }
+ if (module == null) {
+ throw new BuildException("no module name provided for ivy publish task: "
+ + "It can either be set explicitely via the attribute 'module' "
+ + "or via 'ivy.module' property or a prior call to <resolve/>");
+ }
+ if (revision == null) {
+ throw new BuildException("no module revision provided for ivy publish task: "
+ + "It can either be set explicitely via the attribute 'revision' "
+ + "or via 'ivy.revision' property or a prior call to <resolve/>");
+ }
+ if (artifactspattern.isEmpty()) {
+ throw new BuildException(
+ "no artifacts pattern: either provide it through parameter or "
+ + "through ivy.publish.src.artifacts.pattern property");
+ }
+ if (publishResolverName == null) {
+ throw new BuildException(
+ "no publish deliver name: please provide it through parameter 'resolver'");
+ }
+ if ("working".equals(revision)) {
+ revision = Ivy.getWorkingRevision();
+ }
+ Date pubdate = getPubDate(this.pubdate, new Date());
+ if (pubRevision == null) {
+ if (revision.startsWith("working@")) {
+ pubRevision = DateUtil.format(pubdate);
+ } else {
+ pubRevision = revision;
+ }
+ }
+ if (status == null) {
+ throw new BuildException("no status provided: either provide it as parameter "
+ + "or through the ivy.status.default property");
+ }
+ ModuleRevisionId mrid = ModuleRevisionId.newInstance(organisation, module, revision);
+ try {
+ File ivyFile = getProject().resolveFile(
+ IvyPatternHelper.substitute(srcivypattern, organisation, module, pubRevision,
+ "ivy", "ivy", "xml"));
+ if (publishivy && (!ivyFile.exists() || forcedeliver)) {
+ IvyDeliver deliver = new IvyDeliver();
+ deliver.setSettingsRef(getSettingsRef());
+ deliver.setTaskName(getTaskName());
+ deliver.setProject(getProject());
+ deliver.setDeliverpattern(getSrcivypattern());
+ deliver.setDelivertarget(deliverTarget);
+ deliver.setDeliveryList(deliveryList);
+ deliver.setModule(getModule());
+ deliver.setOrganisation(getOrganisation());
+ deliver.setPubdate(DateUtil.format(pubdate));
+ deliver.setPubrevision(getPubrevision());
+ deliver.setPubbranch(getPubbranch());
+ deliver.setRevision(getRevision());
+ deliver.setStatus(getStatus());
+ deliver.setValidate(doValidate(settings));
+ deliver.setReplacedynamicrev(isReplacedynamicrev());
+ deliver.setMerge(merge);
+ deliver.setConf(conf);
+
+ deliver.execute();
+ }
+
+ ivy.publish(
+ mrid,
+ artifactspattern,
+ publishResolverName,
+ new PublishOptions()
+ .setPubrevision(getPubrevision())
+ .setPubbranch(getPubbranch())
+ .setSrcIvyPattern(publishivy ? srcivypattern : null)
+ .setStatus(getStatus())
+ .setPubdate(pubdate)
+ .setExtraArtifacts(
+ (Artifact[]) artifacts.toArray(new Artifact[artifacts.size()]))
+ .setValidate(doValidate(settings)).setOverwrite(overwrite)
+ .setUpdate(update).setMerge(merge).setWarnOnMissing(warnonmissing)
+ .setHaltOnMissing(haltonmissing).setConfs(splitConfs(conf)));
+ } catch (Exception e) {
+ if (e instanceof BuildException) {
+ throw (BuildException) e;
+ }
+ throw new BuildException("impossible to publish artifacts for " + mrid + ": " + e, e);
+ }
+ }
+
+ public PublishArtifact createArtifact() {
+ PublishArtifact art = new PublishArtifact();
+ artifacts.add(art);
+ return art;
+ }
+
+ public boolean isPublishivy() {
+ return publishivy;
+ }
+
+ public void setPublishivy(boolean publishivy) {
+ this.publishivy = publishivy;
+ }
+
+ public boolean isWarnonmissing() {
+ return warnonmissing;
+ }
+
+ public void setWarnonmissing(boolean warnonmissing) {
+ this.warnonmissing = warnonmissing;
+ }
+
+ public boolean isHaltonmissing() {
+ return haltonmissing;
+ }
+
+ public void setHaltonmissing(boolean haltonmissing) {
+ this.haltonmissing = haltonmissing;
+ }
+
+ public boolean isOverwrite() {
+ return overwrite;
+ }
+
+ public void setOverwrite(boolean overwrite) {
+ this.overwrite = overwrite;
+ }
+
+ public void setForcedeliver(boolean b) {
+ forcedeliver = b;
+ }
+
+ public boolean isForcedeliver() {
+ return forcedeliver;
+ }
+
+ public boolean isUpdate() {
+ return update;
+ }
+
+ public void setUpdate(boolean update) {
+ this.update = update;
+ }
+
+ public class PublishArtifact implements Artifact, DynamicAttribute {
+ private String ext;
+
+ private String name;
+
+ private String type;
+
+ private Map extra = new HashMap();
+
+ public String[] getConfigurations() {
+ return null;
+ }
+
+ public String getExt() {
+ return ext == null ? type : ext;
+ }
+
+ public ArtifactRevisionId getId() {
+ return null;
+ }
+
+ public ModuleRevisionId getModuleRevisionId() {
+ return null;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Date getPublicationDate() {
+ return null;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public URL getUrl() {
+ return null;
+ }
+
+ public void setExt(String ext) {
+ this.ext = ext;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public String getAttribute(String attName) {
+ return (String) extra.get(attName);
+ }
+
+ public Map getAttributes() {
+ return extra;
+ }
+
+ public String getExtraAttribute(String attName) {
+ return (String) extra.get(attName);
+ }
+
+ public Map getExtraAttributes() {
+ return extra;
+ }
+
+ public Map getQualifiedExtraAttributes() {
+ return extra;
+ }
+
+ public boolean isMetadata() {
+ return false;
+ }
+
+ public void setDynamicAttribute(String name, String value) {
+ extra.put(name, value);
+ }
+ }
+
+ public static class ArtifactsPattern {
+ private String pattern;
+
+ public String getPattern() {
+ return pattern;
+ }
+
+ public void setPattern(String pattern) {
+ this.pattern = pattern;
+ }
+ }
+}
diff --git a/src/java/org/apache/ivy/ant/IvyReport.java b/src/java/org/apache/ivy/ant/IvyReport.java
new file mode 100644
index 0000000..e4f024e
--- /dev/null
+++ b/src/java/org/apache/ivy/ant/IvyReport.java
@@ -0,0 +1,434 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.ant;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.xml.transform.Source;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
+
+import org.apache.ivy.Ivy;
+import org.apache.ivy.core.IvyPatternHelper;
+import org.apache.ivy.core.cache.ResolutionCacheManager;
+import org.apache.ivy.core.module.id.ModuleId;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.resolve.ResolveOptions;
+import org.apache.ivy.core.settings.IvySettings;
+import org.apache.ivy.plugins.report.XmlReportOutputter;
+import org.apache.ivy.plugins.report.XmlReportParser;
+import org.apache.ivy.util.FileUtil;
+import org.apache.ivy.util.Message;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.taskdefs.XSLTProcess;
+import org.apache.tools.ant.util.JAXPUtils;
+
+/**
+ * This ant task let users generates reports (html, xml, graphml, ...) from the last resolve done.
+ */
+public class IvyReport extends IvyTask {
+ private File todir;
+
+ private String organisation;
+
+ private String module;
+
+ private String conf;
+
+ private boolean graph = true;
+
+ private boolean dot = false;
+
+ private boolean xml = false;
+
+ private boolean xsl = true;
+
+ private File xslFile;
+
+ private String outputpattern;
+
+ private String xslext = "html";
+
+ private List params = new ArrayList();
+
+ private String resolveId;
+
+ private ModuleRevisionId mRevId;
+
+ public File getTodir() {
+ return todir;
+ }
+
+ public void setTodir(File todir) {
+ this.todir = todir;
+ }
+
+ public void setCache(File cache) {
+ cacheAttributeNotSupported();
+ }
+
+ public String getConf() {
+ return conf;
+ }
+
+ public void setConf(String conf) {
+ this.conf = conf;
+ }
+
+ public String getModule() {
+ return module;
+ }
+
+ public void setModule(String module) {
+ this.module = module;
+ }
+
+ public String getOrganisation() {
+ return organisation;
+ }
+
+ public void setOrganisation(String organisation) {
+ this.organisation = organisation;
+ }
+
+ public boolean isGraph() {
+ return graph;
+ }
+
+ public void setGraph(boolean graph) {
+ this.graph = graph;
+ }
+
+ public File getXslfile() {
+ return xslFile;
+ }
+
+ public void setXslfile(File xslFile) {
+ this.xslFile = xslFile;
+ }
+
+ public String getOutputpattern() {
+ return outputpattern;
+ }
+
+ public void setOutputpattern(String outputpattern) {
+ this.outputpattern = outputpattern;
+ }
+
+ public String getResolveId() {
+ return resolveId;
+ }
+
+ public void setResolveId(String resolveId) {
+ this.resolveId = resolveId;
+ }
+
+ public void doExecute() throws BuildException {
+ Ivy ivy = getIvyInstance();
+ IvySettings settings = ivy.getSettings();
+
+ conf = getProperty(conf, settings, "ivy.resolved.configurations", resolveId);
+ if ("*".equals(conf)) {
+ conf = getProperty(settings, "ivy.resolved.configurations", resolveId);
+ }
+ if (conf == null) {
+ throw new BuildException("no conf provided for ivy report task: "
+ + "It can either be set explicitely via the attribute 'conf' or "
+ + "via 'ivy.resolved.configurations' property or a prior call to <resolve/>");
+ }
+ if (todir == null) {
+ String t = getProperty(settings, "ivy.report.todir");
+ if (t != null) {
+ todir = getProject().resolveFile(t);
+ }
+ }
+ if (todir != null && todir.exists()) {
+ todir.mkdirs();
+ }
+ outputpattern = getProperty(outputpattern, settings, "ivy.report.output.pattern");
+ if (outputpattern == null) {
+ outputpattern = "[organisation]-[module]-[conf].[ext]";
+ }
+
+ if (todir != null && todir.exists() && !todir.isDirectory()) {
+ throw new BuildException("destination directory should be a directory !");
+ }
+
+ if (resolveId == null) {
+ organisation = getProperty(organisation, settings, "ivy.organisation", resolveId);
+ module = getProperty(module, settings, "ivy.module", resolveId);
+
+ if (organisation == null) {
+ throw new BuildException("no organisation provided for ivy report task: "
+ + "It can either be set explicitely via the attribute 'organisation' or "
+ + "via 'ivy.organisation' property or a prior call to <resolve/>");
+ }
+ if (module == null) {
+ throw new BuildException("no module name provided for ivy report task: "
+ + "It can either be set explicitely via the attribute 'module' or "
+ + "via 'ivy.module' property or a prior call to <resolve/>");
+ }
+
+ resolveId = ResolveOptions.getDefaultResolveId(new ModuleId(organisation, module));
+ }
+
+ try {
+ String[] confs = splitConfs(conf);
+ if (xsl) {
+ genreport(confs);
+ }
+ if (xml) {
+ genxml(confs);
+ }
+ if (graph) {
+ genStyled(confs, getStylePath("ivy-report-graph.xsl"), "graphml");
+ }
+ if (dot) {
+ genStyled(confs, getStylePath("ivy-report-dot.xsl"), "dot");
+ }
+ } catch (IOException e) {
+ throw new BuildException("impossible to generate report: " + e, e);
+ }
+ }
+
+ private void genxml(String[] confs) throws IOException {
+ ResolutionCacheManager cacheMgr = getIvyInstance().getResolutionCacheManager();
+ for (int i = 0; i < confs.length; i++) {
+ File xml = cacheMgr.getConfigurationResolveReportInCache(resolveId, confs[i]);
+
+ File out;
+ if (todir != null) {
+ out = new File(todir, getOutputPattern(confs[i], "xml"));
+ } else {
+ out = getProject().resolveFile(getOutputPattern(confs[i], "xml"));
+ }
+
+ FileUtil.copy(xml, out, null);
+ }
+ }
+
+ private void genreport(String[] confs) throws IOException {
+ genStyled(confs, getReportStylePath(), xslext);
+
+ // copy the css if required
+ if (xslFile == null) {
+ File css;
+ if (todir != null) {
+ css = new File(todir, "ivy-report.css");
+ } else {
+ css = getProject().resolveFile("ivy-report.css");
+ }
+
+ if (!css.exists()) {
+ Message.debug("copying report css to " + css.getAbsolutePath());
+ FileUtil.copy(XmlReportOutputter.class.getResourceAsStream("ivy-report.css"), css,
+ null);
+ }
+ }
+ }
+
+ private File getReportStylePath() throws IOException {
+ if (xslFile != null) {
+ return xslFile;
+ }
+ // style should be a file (and not an url)
+ // so we have to copy it from classpath to cache
+ ResolutionCacheManager cacheMgr = getIvyInstance().getResolutionCacheManager();
+ File style = new File(cacheMgr.getResolutionCacheRoot(), "ivy-report.xsl");
+ if (!style.exists()) {
+ Message.debug("copying ivy-report.xsl to " + style.getAbsolutePath());
+ FileUtil.copy(XmlReportOutputter.class.getResourceAsStream("ivy-report.xsl"), style,
+ null);
+ }
+ return style;
+ }
+
+ private String getOutputPattern(String conf, String ext) {
+ if (mRevId == null) {
+ ResolutionCacheManager cacheMgr = getIvyInstance().getResolutionCacheManager();
+
+ XmlReportParser parser = new XmlReportParser();
+ File reportFile = cacheMgr.getConfigurationResolveReportInCache(resolveId, conf);
+
+ try {
+ parser.parse(reportFile);
+ } catch (ParseException e) {
+ throw new BuildException("Error occurred while parsing reportfile '"
+ + reportFile.getAbsolutePath() + "'", e);
+ }
+
+ // get the resolve module
+ mRevId = parser.getResolvedModule();
+ }
+
+ return IvyPatternHelper.substitute(outputpattern, mRevId.getOrganisation(),
+ mRevId.getName(), mRevId.getRevision(), "", "", ext, conf,
+ mRevId.getQualifiedExtraAttributes(), null);
+ }
+
+ private void genStyled(String[] confs, File style, String ext) throws IOException {
+ ResolutionCacheManager cacheMgr = getIvyInstance().getResolutionCacheManager();
+
+ // process the report with xslt to generate dot file
+ File out;
+ if (todir != null) {
+ out = todir;
+ } else {
+ out = getProject().getBaseDir();
+ }
+
+ InputStream xsltStream = null;
+ try {
+ // create stream to stylesheet
+ xsltStream = new BufferedInputStream(new FileInputStream(style));
+ Source xsltSource = new StreamSource(xsltStream, JAXPUtils.getSystemId(style));
+
+ // create transformer
+ TransformerFactory tFactory = TransformerFactory.newInstance();
+ Transformer transformer = tFactory.newTransformer(xsltSource);
+
+ // add standard parameters
+ transformer.setParameter("confs", conf);
+ transformer.setParameter("extension", xslext);
+
+ // add the provided XSLT parameters
+ for (Iterator it = params.iterator(); it.hasNext();) {
+ XSLTProcess.Param param = (XSLTProcess.Param) it.next();
+ transformer.setParameter(param.getName(), param.getExpression());
+ }
+
+ // create the report
+ for (int i = 0; i < confs.length; i++) {
+ File reportFile = cacheMgr
+ .getConfigurationResolveReportInCache(resolveId, confs[i]);
+ File outFile = new File(out, getOutputPattern(confs[i], ext));
+
+ log("Processing " + reportFile + " to " + outFile);
+
+ // make sure the output directory exist
+ File outFileDir = outFile.getParentFile();
+ if (!outFileDir.exists()) {
+ if (!outFileDir.mkdirs()) {
+ throw new BuildException("Unable to create directory: "
+ + outFileDir.getAbsolutePath());
+ }
+ }
+
+ InputStream inStream = null;
+ OutputStream outStream = null;
+ try {
+ inStream = new BufferedInputStream(new FileInputStream(reportFile));
+ outStream = new BufferedOutputStream(new FileOutputStream(outFile));
+ StreamResult res = new StreamResult(outStream);
+ Source src = new StreamSource(inStream, JAXPUtils.getSystemId(style));
+ transformer.transform(src, res);
+ } catch (TransformerException e) {
+ throw new BuildException(e);
+ } finally {
+ if (inStream != null) {
+ try {
+ inStream.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ if (outStream != null) {
+ try {
+ outStream.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ }
+ }
+ } catch (TransformerConfigurationException e) {
+ throw new BuildException(e);
+ } finally {
+ if (xsltStream != null) {
+ try {
+ xsltStream.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ }
+ }
+
+ private File getStylePath(String styleResourceName) throws IOException {
+ // style should be a file (and not an url)
+ // so we have to copy it from classpath to cache
+ ResolutionCacheManager cacheMgr = getIvyInstance().getResolutionCacheManager();
+ File style = new File(cacheMgr.getResolutionCacheRoot(), styleResourceName);
+ FileUtil.copy(XmlReportOutputter.class.getResourceAsStream(styleResourceName), style, null);
+ return style;
+ }
+
+ public boolean isXml() {
+ return xml;
+ }
+
+ public void setXml(boolean xml) {
+ this.xml = xml;
+ }
+
+ public boolean isXsl() {
+ return xsl;
+ }
+
+ public void setXsl(boolean xsl) {
+ this.xsl = xsl;
+ }
+
+ public String getXslext() {
+ return xslext;
+ }
+
+ public void setXslext(String xslext) {
+ this.xslext = xslext;
+ }
+
+ public XSLTProcess.Param createParam() {
+ XSLTProcess.Param result = new XSLTProcess.Param();
+ params.add(result);
+ return result;
+ }
+
+ public boolean isDot() {
+ return dot;
+ }
+
+ public void setDot(boolean dot) {
+ this.dot = dot;
+ }
+
+}
diff --git a/src/java/org/apache/ivy/ant/IvyRepositoryReport.java b/src/java/org/apache/ivy/ant/IvyRepositoryReport.java
new file mode 100644
index 0000000..0b725fc
--- /dev/null
+++ b/src/java/org/apache/ivy/ant/IvyRepositoryReport.java
@@ -0,0 +1,328 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.ant;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.ivy.Ivy;
+import org.apache.ivy.core.cache.ResolutionCacheManager;
+import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleId;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.report.ResolveReport;
+import org.apache.ivy.core.resolve.ResolveOptions;
+import org.apache.ivy.core.settings.IvySettings;
+import org.apache.ivy.plugins.matcher.PatternMatcher;
+import org.apache.ivy.plugins.report.XmlReportOutputter;
+import org.apache.ivy.util.FileUtil;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.taskdefs.XSLTProcess;
+
+/**
+ * Generates a report of dependencies of a set of modules in the repository. The set of modules is
+ * specified using organisation/module and matcher.
+ */
+public class IvyRepositoryReport extends IvyTask {
+ private String organisation = "*";
+
+ private String module;
+
+ private String branch;
+
+ private String revision = "latest.integration";
+
+ private String matcher = PatternMatcher.EXACT_OR_REGEXP;
+
+ private File todir;
+
+ private boolean graph = false;
+
+ private boolean dot = false;
+
+ private boolean xml = true;
+
+ private boolean xsl = false;
+
+ private String xslFile;
+
+ private String outputname = "ivy-repository-report";
+
+ private String xslext = "html";
+
+ private List params = new ArrayList();
+
+ public void doExecute() throws BuildException {
+ Ivy ivy = getIvyInstance();
+ IvySettings settings = ivy.getSettings();
+ if (xsl && xslFile == null) {
+ throw new BuildException("xsl file is mandatory when using xsl generation");
+ }
+ if (module == null && PatternMatcher.EXACT.equals(matcher)) {
+ throw new BuildException("no module name provided for ivy repository graph task: "
+ + "It can either be set explicitely via the attribute 'module' or "
+ + "via 'ivy.module' property or a prior call to <resolve/>");
+ } else if (module == null && !PatternMatcher.EXACT.equals(matcher)) {
+ module = PatternMatcher.ANY_EXPRESSION;
+ }
+ ModuleRevisionId mrid = ModuleRevisionId.newInstance(organisation, module, revision);
+
+ try {
+ ModuleRevisionId criteria = null;
+
+ if ((revision == null) || settings.getVersionMatcher().isDynamic(mrid)) {
+ criteria = new ModuleRevisionId(new ModuleId(organisation, module), branch, "*");
+ } else {
+ criteria = new ModuleRevisionId(new ModuleId(organisation, module), branch,
+ revision);
+ }
+
+ ModuleRevisionId[] mrids = ivy.listModules(criteria, settings.getMatcher(matcher));
+
+ // replace all found revisions with the original requested revision
+ Set modules = new HashSet();
+ for (int i = 0; i < mrids.length; i++) {
+ modules.add(ModuleRevisionId.newInstance(mrids[i], revision));
+ }
+
+ mrids = (ModuleRevisionId[]) modules.toArray(new ModuleRevisionId[modules.size()]);
+ ModuleDescriptor md = DefaultModuleDescriptor.newCallerInstance(mrids, true, false);
+ String resolveId = ResolveOptions.getDefaultResolveId(md);
+ ResolveReport report = ivy.resolve(md, new ResolveOptions().setResolveId(resolveId)
+ .setValidate(doValidate(settings)));
+
+ ResolutionCacheManager cacheMgr = getIvyInstance().getResolutionCacheManager();
+ new XmlReportOutputter().output(report, cacheMgr, new ResolveOptions());
+ if (graph) {
+ gengraph(cacheMgr, md.getModuleRevisionId().getOrganisation(), md
+ .getModuleRevisionId().getName());
+ }
+ if (dot) {
+ gendot(cacheMgr, md.getModuleRevisionId().getOrganisation(), md
+ .getModuleRevisionId().getName());
+ }
+ if (xml) {
+
+ FileUtil.copy(cacheMgr.getConfigurationResolveReportInCache(resolveId, "default"),
+ new File(getTodir(), outputname + ".xml"), null);
+ }
+ if (xsl) {
+ genreport(cacheMgr, md.getModuleRevisionId().getOrganisation(), md
+ .getModuleRevisionId().getName());
+ }
+ } catch (Exception e) {
+ throw new BuildException("impossible to generate graph for " + mrid + ": " + e, e);
+ }
+ }
+
+ private void genreport(ResolutionCacheManager cache, String organisation, String module)
+ throws IOException {
+ // first process the report with xslt
+ XSLTProcess xslt = new XSLTProcess();
+ xslt.setTaskName(getTaskName());
+ xslt.setProject(getProject());
+ xslt.init();
+
+ String resolveId = ResolveOptions.getDefaultResolveId(new ModuleId(organisation, module));
+ xslt.setIn(cache.getConfigurationResolveReportInCache(resolveId, "default"));
+ xslt.setOut(new File(getTodir(), outputname + "." + xslext));
+
+ xslt.setStyle(xslFile);
+
+ XSLTProcess.Param param = xslt.createParam();
+ param.setName("extension");
+ param.setExpression(xslext);
+
+ // add the provided XSLT parameters
+ for (Iterator it = params.iterator(); it.hasNext();) {
+ param = (XSLTProcess.Param) it.next();
+ XSLTProcess.Param realParam = xslt.createParam();
+ realParam.setName(param.getName());
+ realParam.setExpression(param.getExpression());
+ }
+
+ xslt.execute();
+ }
+
+ private void gengraph(ResolutionCacheManager cache, String organisation, String module)
+ throws IOException {
+ gen(cache, organisation, module, getGraphStylePath(cache.getResolutionCacheRoot()),
+ "graphml");
+ }
+
+ private String getGraphStylePath(File cache) throws IOException {
+ // style should be a file (and not an url)
+ // so we have to copy it from classpath to cache
+ File style = new File(cache, "ivy-report-graph-all.xsl");
+ FileUtil.copy(XmlReportOutputter.class.getResourceAsStream("ivy-report-graph-all.xsl"),
+ style, null);
+ return style.getAbsolutePath();
+ }
+
+ private void gendot(ResolutionCacheManager cache, String organisation, String module)
+ throws IOException {
+ gen(cache, organisation, module, getDotStylePath(cache.getResolutionCacheRoot()), "dot");
+ }
+
+ private String getDotStylePath(File cache) throws IOException {
+ // style should be a file (and not an url)
+ // so we have to copy it from classpath to cache
+ File style = new File(cache, "ivy-report-dot-all.xsl");
+ FileUtil.copy(XmlReportOutputter.class.getResourceAsStream("ivy-report-dot-all.xsl"),
+ style, null);
+ return style.getAbsolutePath();
+ }
+
+ private void gen(ResolutionCacheManager cache, String organisation, String module,
+ String style, String ext) throws IOException {
+ XSLTProcess xslt = new XSLTProcess();
+ xslt.setTaskName(getTaskName());
+ xslt.setProject(getProject());
+ xslt.init();
+
+ String resolveId = ResolveOptions.getDefaultResolveId(new ModuleId(organisation, module));
+ xslt.setIn(cache.getConfigurationResolveReportInCache(resolveId, "default"));
+ xslt.setOut(new File(getTodir(), outputname + "." + ext));
+ xslt.setBasedir(cache.getResolutionCacheRoot());
+ xslt.setStyle(style);
+ xslt.execute();
+ }
+
+ public File getTodir() {
+ if (todir == null && getProject() != null) {
+ return getProject().getBaseDir();
+ }
+ return todir;
+ }
+
+ public void setTodir(File todir) {
+ this.todir = todir;
+ }
+
+ public boolean isGraph() {
+ return graph;
+ }
+
+ public void setGraph(boolean graph) {
+ this.graph = graph;
+ }
+
+ public String getXslfile() {
+ return xslFile;
+ }
+
+ public void setXslfile(String xslFile) {
+ this.xslFile = xslFile;
+ }
+
+ public boolean isXml() {
+ return xml;
+ }
+
+ public void setXml(boolean xml) {
+ this.xml = xml;
+ }
+
+ public boolean isXsl() {
+ return xsl;
+ }
+
+ public void setXsl(boolean xsl) {
+ this.xsl = xsl;
+ }
+
+ public String getXslext() {
+ return xslext;
+ }
+
+ public void setXslext(String xslext) {
+ this.xslext = xslext;
+ }
+
+ public XSLTProcess.Param createParam() {
+ XSLTProcess.Param result = new XSLTProcess.Param();
+ params.add(result);
+ return result;
+ }
+
+ public String getOutputname() {
+ return outputname;
+ }
+
+ public void setOutputname(String outputpattern) {
+ outputname = outputpattern;
+ }
+
+ public void setCache(File cache) {
+ cacheAttributeNotSupported();
+ }
+
+ public String getMatcher() {
+ return matcher;
+ }
+
+ public void setMatcher(String matcher) {
+ this.matcher = matcher;
+ }
+
+ public String getModule() {
+ return module;
+ }
+
+ public void setModule(String module) {
+ this.module = module;
+ }
+
+ public String getOrganisation() {
+ return organisation;
+ }
+
+ public void setOrganisation(String organisation) {
+ this.organisation = organisation;
+ }
+
+ public String getRevision() {
+ return revision;
+ }
+
+ public void setRevision(String revision) {
+ this.revision = revision;
+ }
+
+ public String getBranch() {
+ return branch;
+ }
+
+ public void setBranch(String branch) {
+ this.branch = branch;
+ }
+
+ public boolean isDot() {
+ return dot;
+ }
+
+ public void setDot(boolean dot) {
+ this.dot = dot;
+ }
+}
diff --git a/src/java/org/apache/ivy/ant/IvyResolve.java b/src/java/org/apache/ivy/ant/IvyResolve.java
new file mode 100644
index 0000000..5dd7757
--- /dev/null
+++ b/src/java/org/apache/ivy/ant/IvyResolve.java
@@ -0,0 +1,517 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.ant;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.ivy.Ivy;
+import org.apache.ivy.core.LogOptions;
+import org.apache.ivy.core.module.descriptor.DefaultExcludeRule;
+import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.ExtendsDescriptor;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.report.ResolveReport;
+import org.apache.ivy.core.resolve.ResolveOptions;
+import org.apache.ivy.core.resolve.ResolveProcessException;
+import org.apache.ivy.core.settings.IvySettings;
+import org.apache.ivy.util.Message;
+import org.apache.ivy.util.filter.FilterHelper;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+
+/**
+ * This task allow to call the Ivy dependency resolution from ant.
+ */
+public class IvyResolve extends IvyTask {
+ private File file = null;
+
+ private String conf = null;
+
+ private String organisation = null;
+
+ private String module = null;
+
+ private String branch = null;
+
+ private String revision = null;
+
+ private String pubdate = null;
+
+ private boolean inline = false;
+
+ private boolean haltOnFailure = true;
+
+ private boolean showProgress = true;
+
+ private boolean useCacheOnly = false;
+
+ private String type = null;
+
+ private boolean transitive = true;
+
+ private boolean refresh = false;
+
+ private boolean changing = false;
+
+ private Boolean keep = null;
+
+ private String failureProperty = null;
+
+ private boolean useOrigin = false;
+
+ private String resolveMode = null;
+
+ private String resolveId = null;
+
+ private String log = ResolveOptions.LOG_DEFAULT;
+
+ private boolean checkIfChanged = true; // for backward compatibility
+
+ private List/* <IvyDependency> */dependencies = new ArrayList();
+
+ private List/* <IvyExclude> */excludes = new ArrayList();
+
+ private List/* <IvyConflict> */conflicts = new ArrayList();
+
+ public boolean isUseOrigin() {
+ return useOrigin;
+ }
+
+ public void setUseOrigin(boolean useOrigin) {
+ this.useOrigin = useOrigin;
+ }
+
+ public String getDate() {
+ return pubdate;
+ }
+
+ public void setDate(String pubdate) {
+ this.pubdate = pubdate;
+ }
+
+ public String getRevision() {
+ return revision;
+ }
+
+ public void setRevision(String revision) {
+ this.revision = revision;
+ }
+
+ public String getBranch() {
+ return branch;
+ }
+
+ public void setBranch(String branch) {
+ this.branch = branch;
+ }
+
+ public void setCache(File cache) {
+ cacheAttributeNotSupported();
+ }
+
+ public String getConf() {
+ return conf;
+ }
+
+ public void setConf(String conf) {
+ this.conf = conf;
+ }
+
+ public File getFile() {
+ return file;
+ }
+
+ public void setFile(File file) {
+ this.file = file;
+ }
+
+ public boolean isHaltonfailure() {
+ return haltOnFailure;
+ }
+
+ public void setHaltonfailure(boolean haltOnFailure) {
+ this.haltOnFailure = haltOnFailure;
+ }
+
+ public void setShowprogress(boolean show) {
+ this.showProgress = show;
+ }
+
+ public boolean isUseCacheOnly() {
+ return useCacheOnly;
+ }
+
+ public void setUseCacheOnly(boolean useCacheOnly) {
+ this.useCacheOnly = useCacheOnly;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public boolean isRefresh() {
+ return refresh;
+ }
+
+ public void setRefresh(boolean refresh) {
+ this.refresh = refresh;
+ }
+
+ public String getLog() {
+ return log;
+ }
+
+ public void setLog(String log) {
+ this.log = log;
+ }
+
+ /**
+ * @deprecated Use {@link #setFailureProperty(String)} instead
+ */
+ @Deprecated
+ public void setFailurePropery(String failureProperty) {
+ log("The 'failurepropery' attribute is deprecated. "
+ + "Please use the 'failureproperty' attribute instead", Project.MSG_WARN);
+ setFailureProperty(failureProperty);
+ }
+
+ public void setFailureProperty(String failureProperty) {
+ this.failureProperty = failureProperty;
+ }
+
+ public String getFailureProperty() {
+ return failureProperty;
+ }
+
+ public IvyDependency createDependency() {
+ IvyDependency dep = new IvyDependency();
+ dependencies.add(dep);
+ return dep;
+ }
+
+ public IvyExclude createExclude() {
+ IvyExclude ex = new IvyExclude();
+ excludes.add(ex);
+ return ex;
+ }
+
+ public IvyConflict createConflict() {
+ IvyConflict c = new IvyConflict();
+ conflicts.add(c);
+ return c;
+ }
+
+ protected void prepareTask() {
+ super.prepareTask();
+ Message.setShowProgress(showProgress);
+ }
+
+ public void doExecute() throws BuildException {
+ Ivy ivy = getIvyInstance();
+ IvySettings settings = ivy.getSettings();
+ try {
+ conf = getProperty(conf, settings, "ivy.configurations");
+ type = getProperty(type, settings, "ivy.resolve.default.type.filter");
+ String[] confs = splitConfs(conf);
+
+ boolean childs = !dependencies.isEmpty() || !excludes.isEmpty() || !conflicts.isEmpty();
+
+ ResolveReport report;
+ if (childs) {
+ if (isInline()) {
+ throw new BuildException("the inline mode is incompatible with child elements");
+ }
+ if (organisation != null) {
+ throw new BuildException("'organisation' is not allowed with child elements");
+ }
+ if (module != null) {
+ throw new BuildException("'module' is not allowed with child elements");
+ }
+ if (file != null) {
+ throw new BuildException("'file' not allowed with child elements");
+ }
+ if (!getAllowedLogOptions().contains(log)) {
+ throw new BuildException("invalid option for 'log': " + log
+ + ". Available options are " + getAllowedLogOptions());
+ }
+
+ ModuleRevisionId mrid = ModuleRevisionId.newInstance("", "",
+ Ivy.getWorkingRevision());
+ DefaultModuleDescriptor md = DefaultModuleDescriptor.newBasicInstance(mrid, null);
+
+ Iterator itDeps = dependencies.iterator();
+ while (itDeps.hasNext()) {
+ IvyDependency dep = (IvyDependency) itDeps.next();
+ DependencyDescriptor dd = dep.asDependencyDescriptor(md, "default", settings);
+ md.addDependency(dd);
+ }
+
+ Iterator itExcludes = excludes.iterator();
+ while (itExcludes.hasNext()) {
+ IvyExclude exclude = (IvyExclude) itExcludes.next();
+ DefaultExcludeRule rule = exclude.asRule(settings);
+ rule.addConfiguration("default");
+ md.addExcludeRule(rule);
+ }
+
+ Iterator itConflicts = conflicts.iterator();
+ while (itConflicts.hasNext()) {
+ IvyConflict conflict = (IvyConflict) itConflicts.next();
+ conflict.addConflict(md, settings);
+ }
+
+ report = ivy
+ .resolve(md, getResolveOptions(ivy, new String[] {"default"}, settings));
+ } else if (isInline()) {
+ if (organisation == null) {
+ throw new BuildException("'organisation' is required when using inline mode");
+ }
+ if (module == null) {
+ throw new BuildException("'module' is required when using inline mode");
+ }
+ if (file != null) {
+ throw new BuildException("'file' not allowed when using inline mode");
+ }
+ if (!getAllowedLogOptions().contains(log)) {
+ throw new BuildException("invalid option for 'log': " + log
+ + ". Available options are " + getAllowedLogOptions());
+ }
+ for (int i = 0; i < confs.length; i++) {
+ if ("*".equals(confs[i])) {
+ confs[i] = "*(public)";
+ }
+ }
+ if (revision == null) {
+ revision = "latest.integration";
+ }
+ report = ivy.resolve(
+ ModuleRevisionId.newInstance(organisation, module, branch, revision),
+ getResolveOptions(ivy, confs, settings), changing);
+
+ } else {
+ if (organisation != null) {
+ throw new BuildException(
+ "'organisation' not allowed when not using 'org' attribute");
+ }
+ if (module != null) {
+ throw new BuildException("'module' not allowed when not using 'org' attribute");
+ }
+ if (file == null) {
+ file = getProject().resolveFile(getProperty(settings, "ivy.dep.file"));
+ }
+ report = ivy.resolve(file.toURI().toURL(), getResolveOptions(ivy, confs, settings));
+ }
+ if (report.hasError()) {
+ if (failureProperty != null) {
+ getProject().setProperty(failureProperty, "true");
+ }
+ if (isHaltonfailure()) {
+ throw new BuildException("resolve failed - see output for details");
+ }
+ }
+ setResolved(report, resolveId, isKeep());
+ confs = report.getConfigurations();
+
+ if (isKeep()) {
+ ModuleDescriptor md = report.getModuleDescriptor();
+ // put resolved infos in ant properties and ivy variables
+ // putting them in ivy variables is important to be able to change from one resolve
+ // call to the other
+ String mdOrg = md.getModuleRevisionId().getOrganisation();
+ String mdName = md.getModuleRevisionId().getName();
+ String mdRev = md.getResolvedModuleRevisionId().getRevision();
+ getProject().setProperty("ivy.organisation", mdOrg);
+ settings.setVariable("ivy.organisation", mdOrg);
+ getProject().setProperty("ivy.module", mdName);
+ settings.setVariable("ivy.module", mdName);
+ getProject().setProperty("ivy.revision", mdRev);
+ settings.setVariable("ivy.revision", mdRev);
+ for (int i = 0; i < md.getInheritedDescriptors().length; i++) {
+ ExtendsDescriptor parent = md.getInheritedDescriptors()[i];
+ String parentOrg = parent.getResolvedParentRevisionId().getOrganisation();
+ String parentModule = parent.getResolvedParentRevisionId().getName();
+ String parentRevision = parent.getResolvedParentRevisionId().getRevision();
+ String parentBranch = parent.getResolvedParentRevisionId().getBranch();
+ getProject().setProperty("ivy.parent[" + i + "].organisation", parentOrg);
+ settings.setVariable("ivy.parent[" + i + "].organisation", parentOrg);
+ getProject().setProperty("ivy.parent[" + i + "].module", parentModule);
+ settings.setVariable("ivy.parent[" + i + "].module", parentModule);
+ getProject().setProperty("ivy.parent[" + i + "].revision", parentRevision);
+ settings.setVariable("ivy.parent[" + i + "].revision", parentRevision);
+ if (parentBranch != null) {
+ getProject().setProperty("ivy.parent[" + i + "].branch", parentBranch);
+ settings.setVariable("ivy.parent[" + i + "].branch", parentBranch);
+ }
+ }
+ getProject().setProperty("ivy.parents.count",
+ String.valueOf(md.getInheritedDescriptors().length));
+ settings.setVariable("ivy.parents.count",
+ String.valueOf(md.getInheritedDescriptors().length));
+
+ Boolean hasChanged = null;
+ if (getCheckIfChanged()) {
+ hasChanged = Boolean.valueOf(report.hasChanged());
+ getProject().setProperty("ivy.deps.changed", hasChanged.toString());
+ settings.setVariable("ivy.deps.changed", hasChanged.toString());
+ }
+ getProject().setProperty("ivy.resolved.configurations", mergeConfs(confs));
+ settings.setVariable("ivy.resolved.configurations", mergeConfs(confs));
+ if (file != null) {
+ getProject().setProperty("ivy.resolved.file", file.getAbsolutePath());
+ settings.setVariable("ivy.resolved.file", file.getAbsolutePath());
+ }
+ if (resolveId != null) {
+ getProject().setProperty("ivy.organisation." + resolveId, mdOrg);
+ settings.setVariable("ivy.organisation." + resolveId, mdOrg);
+ getProject().setProperty("ivy.module." + resolveId, mdName);
+ settings.setVariable("ivy.module." + resolveId, mdName);
+ getProject().setProperty("ivy.revision." + resolveId, mdRev);
+ settings.setVariable("ivy.revision." + resolveId, mdRev);
+ if (getCheckIfChanged()) {
+ // hasChanged has already been set earlier
+ getProject().setProperty("ivy.deps.changed." + resolveId,
+ hasChanged.toString());
+ settings.setVariable("ivy.deps.changed." + resolveId, hasChanged.toString());
+ }
+ getProject().setProperty("ivy.resolved.configurations." + resolveId,
+ mergeConfs(confs));
+ settings.setVariable("ivy.resolved.configurations." + resolveId,
+ mergeConfs(confs));
+ if (file != null) {
+ getProject().setProperty("ivy.resolved.file." + resolveId,
+ file.getAbsolutePath());
+ settings.setVariable("ivy.resolved.file." + resolveId,
+ file.getAbsolutePath());
+ }
+ }
+ }
+ } catch (MalformedURLException e) {
+ throw new BuildException("unable to convert given ivy file to url: " + file + ": " + e,
+ e);
+ } catch (ParseException e) {
+ log(e.getMessage(), Project.MSG_ERR);
+ throw new BuildException("syntax errors in ivy file: " + e, e);
+ } catch (ResolveProcessException e) {
+ throw new BuildException("impossible to resolve dependencies:\n\t" + e.getMessage(), e);
+ } catch (Exception e) {
+ throw new BuildException("impossible to resolve dependencies:\n\t" + e, e);
+ }
+ }
+
+ protected Collection/* <String> */getAllowedLogOptions() {
+ return Arrays.asList(new String[] {LogOptions.LOG_DEFAULT, LogOptions.LOG_DOWNLOAD_ONLY,
+ LogOptions.LOG_QUIET});
+ }
+
+ private ResolveOptions getResolveOptions(Ivy ivy, String[] confs, IvySettings settings) {
+ if (useOrigin) {
+ settings.useDeprecatedUseOrigin();
+ }
+ return ((ResolveOptions) new ResolveOptions().setLog(log)).setConfs(confs)
+ .setValidate(doValidate(settings))
+ .setArtifactFilter(FilterHelper.getArtifactTypeFilter(type)).setRevision(revision)
+ .setDate(getPubDate(pubdate, null)).setUseCacheOnly(useCacheOnly)
+ .setRefresh(refresh).setTransitive(transitive).setResolveMode(resolveMode)
+ .setResolveId(resolveId).setCheckIfChanged(checkIfChanged);
+ }
+
+ public String getModule() {
+ return module;
+ }
+
+ public void setModule(String module) {
+ this.module = module;
+ }
+
+ public String getOrganisation() {
+ return organisation;
+ }
+
+ public void setOrganisation(String organisation) {
+ this.organisation = organisation;
+ }
+
+ public boolean isTransitive() {
+ return transitive;
+ }
+
+ public void setTransitive(boolean transitive) {
+ this.transitive = transitive;
+ }
+
+ public boolean isChanging() {
+ return changing;
+ }
+
+ public void setChanging(boolean changing) {
+ this.changing = changing;
+ }
+
+ public boolean isKeep() {
+ return keep == null ? organisation == null : keep.booleanValue();
+ }
+
+ public void setKeep(boolean keep) {
+ this.keep = Boolean.valueOf(keep);
+ }
+
+ public boolean isInline() {
+ return inline;
+ }
+
+ public void setInline(boolean inline) {
+ this.inline = inline;
+ }
+
+ public String getResolveId() {
+ return resolveId;
+ }
+
+ public void setResolveId(String resolveId) {
+ this.resolveId = resolveId;
+ }
+
+ public String getResolveMode() {
+ return resolveMode;
+ }
+
+ public void setResolveMode(String resolveMode) {
+ this.resolveMode = resolveMode;
+ }
+
+ public boolean getCheckIfChanged() {
+ return checkIfChanged;
+ }
+
+ public void setCheckIfChanged(boolean checkIfChanged) {
+ this.checkIfChanged = checkIfChanged;
+ }
+}
diff --git a/src/java/org/apache/ivy/ant/IvyResources.java b/src/java/org/apache/ivy/ant/IvyResources.java
new file mode 100644
index 0000000..41ec043
--- /dev/null
+++ b/src/java/org/apache/ivy/ant/IvyResources.java
@@ -0,0 +1,118 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.ant;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.ivy.core.report.ArtifactDownloadReport;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Location;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.types.resources.BaseResourceCollectionWrapper;
+import org.apache.tools.ant.types.resources.FileResource;
+
+public class IvyResources extends IvyCacheTask implements ResourceCollection {
+
+ /**
+ * Delegate for the implementation of the resource collection
+ */
+ private class IvyBaseResourceCollectionWrapper extends BaseResourceCollectionWrapper {
+
+ protected Collection getCollection() {
+ return resolveResources(null);
+ }
+
+ }
+
+ private IvyBaseResourceCollectionWrapper wrapper = new IvyBaseResourceCollectionWrapper();
+
+ // delegate the ProjectComponent API on the wrapper
+
+ public void setLocation(Location location) {
+ super.setLocation(location);
+ wrapper.setLocation(location);
+ }
+
+ public void setProject(Project project) {
+ super.setProject(project);
+ wrapper.setProject(project);
+ }
+
+ public void setDescription(String desc) {
+ super.setDescription(desc);
+ wrapper.setDescription(desc);
+ }
+
+ // delegate the DataType API on the wrapper
+
+ public void setRefid(Reference ref) {
+ wrapper.setRefid(ref);
+ }
+
+ // delegate the AbstractResourceCollectionWrapper API on the wrapper
+
+ public void setCache(boolean b) {
+ wrapper.setCache(b);
+ }
+
+ // implementation of the Resource Collection API
+
+ public boolean isFilesystemOnly() {
+ return true;
+ }
+
+ public Iterator iterator() {
+ return wrapper.iterator();
+ }
+
+ public int size() {
+ return wrapper.size();
+ }
+
+ // convert the ivy reports into an Ant Resource collection
+
+ private Collection resolveResources(String id) throws BuildException {
+ prepareAndCheck();
+ try {
+ List/* <FileResource> */resources = new ArrayList();
+ if (id != null) {
+ getProject().addReference(id, this);
+ }
+ for (Iterator iter = getArtifactReports().iterator(); iter.hasNext();) {
+ ArtifactDownloadReport a = (ArtifactDownloadReport) iter.next();
+ resources.add(new FileResource(a.getLocalFile()));
+ }
+ return resources;
+ } catch (Exception ex) {
+ throw new BuildException("impossible to build ivy resources: " + ex, ex);
+ }
+ }
+
+ // implementation of the IvyPostResolveTask API
+
+ public void doExecute() throws BuildException {
+ // TODO : maybe there is a way to implement it ?
+ throw new BuildException("ivy:resources should not be used as a Ant Task");
+ }
+
+}
diff --git a/src/java/org/apache/ivy/ant/IvyRetrieve.java b/src/java/org/apache/ivy/ant/IvyRetrieve.java
new file mode 100644
index 0000000..d333cd8
--- /dev/null
+++ b/src/java/org/apache/ivy/ant/IvyRetrieve.java
@@ -0,0 +1,234 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.ant;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.apache.ivy.core.LogOptions;
+import org.apache.ivy.core.retrieve.RetrieveOptions;
+import org.apache.ivy.core.retrieve.RetrieveReport;
+import org.apache.ivy.util.filter.Filter;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.Mapper;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.PatternSet;
+import org.apache.tools.ant.util.FileNameMapper;
+
+/**
+ * This task allow to retrieve dependencies from the cache to a local directory like a lib dir.
+ */
+public class IvyRetrieve extends IvyPostResolveTask {
+
+ private static final Collection OVERWRITEMODE_VALUES = Arrays.asList(new String[] {
+ RetrieveOptions.OVERWRITEMODE_ALWAYS, RetrieveOptions.OVERWRITEMODE_NEVER,
+ RetrieveOptions.OVERWRITEMODE_NEWER, RetrieveOptions.OVERWRITEMODE_DIFFERENT});
+
+ private String pattern;
+
+ private String ivypattern = null;
+
+ private boolean sync = false;
+
+ private boolean symlink = false;
+
+ private boolean symlinkmass = false;
+
+ private String overwriteMode = RetrieveOptions.OVERWRITEMODE_NEWER;
+
+ private String pathId = null;
+
+ private String setId = null;
+
+ private Mapper mapper = null;
+
+ public String getPattern() {
+ return pattern;
+ }
+
+ public void setPattern(String pattern) {
+ this.pattern = pattern;
+ }
+
+ public String getPathId() {
+ return pathId;
+ }
+
+ public void setPathId(String pathId) {
+ this.pathId = pathId;
+ }
+
+ public String getSetId() {
+ return setId;
+ }
+
+ public void setSetId(String setId) {
+ this.setId = setId;
+ }
+
+ public void doExecute() throws BuildException {
+ prepareAndCheck();
+
+ if (!getAllowedLogOptions().contains(getLog())) {
+ throw new BuildException("invalid option for 'log': " + getLog()
+ + ". Available options are " + getAllowedLogOptions());
+ }
+
+ pattern = getProperty(pattern, getSettings(), "ivy.retrieve.pattern");
+ try {
+ Filter artifactFilter = getArtifactFilter();
+ RetrieveReport report = getIvyInstance().retrieve(
+ getResolvedMrid(),
+ ((RetrieveOptions) new RetrieveOptions().setLog(getLog()))
+ .setConfs(splitConfs(getConf())).setDestArtifactPattern(pattern)
+ .setDestIvyPattern(ivypattern).setArtifactFilter(artifactFilter)
+ .setSync(sync).setOverwriteMode(getOverwriteMode())
+ .setUseOrigin(isUseOrigin()).setMakeSymlinks(symlink)
+ .setMakeSymlinksInMass(symlinkmass).setResolveId(getResolveId())
+ .setMapper(mapper == null ? null : new MapperAdapter(mapper)));
+
+ int targetsCopied = report.getNbrArtifactsCopied();
+ boolean haveTargetsBeenCopied = targetsCopied > 0;
+ getProject().setProperty("ivy.nb.targets.copied", String.valueOf(targetsCopied));
+ getProject().setProperty("ivy.targets.copied", String.valueOf(haveTargetsBeenCopied));
+
+ if (getPathId() != null) {
+ Path path = new Path(getProject());
+ getProject().addReference(getPathId(), path);
+
+ for (Iterator iter = report.getRetrievedFiles().iterator(); iter.hasNext();) {
+ path.createPathElement().setLocation((File) iter.next());
+ }
+ }
+
+ if (getSetId() != null) {
+ FileSet fileset = new FileSet();
+ fileset.setProject(getProject());
+ getProject().addReference(getSetId(), fileset);
+
+ fileset.setDir(report.getRetrieveRoot());
+
+ for (Iterator iter = report.getRetrievedFiles().iterator(); iter.hasNext();) {
+ PatternSet.NameEntry ne = fileset.createInclude();
+ ne.setName(getPath(report.getRetrieveRoot(), (File) iter.next()));
+ }
+ }
+ } catch (Exception ex) {
+ throw new BuildException("impossible to ivy retrieve: " + ex, ex);
+ }
+ }
+
+ protected Collection/* <String> */getAllowedLogOptions() {
+ return Arrays.asList(new String[] {LogOptions.LOG_DEFAULT, LogOptions.LOG_DOWNLOAD_ONLY,
+ LogOptions.LOG_QUIET});
+ }
+
+ public String getIvypattern() {
+ return ivypattern;
+ }
+
+ public void setIvypattern(String ivypattern) {
+ this.ivypattern = ivypattern;
+ }
+
+ public boolean isSync() {
+ return sync;
+ }
+
+ public void setSync(boolean sync) {
+ this.sync = sync;
+ }
+
+ /**
+ * Option to create symlinks instead of copying.
+ */
+ public void setSymlink(boolean symlink) {
+ this.symlink = symlink;
+ }
+
+ /**
+ * Option to create symlinks in one mass action, instead of separately.
+ */
+ public void setSymlinkmass(boolean symlinkmass) {
+ this.symlinkmass = symlinkmass;
+ }
+
+ public void setOverwriteMode(String overwriteMode) {
+ if (!OVERWRITEMODE_VALUES.contains(overwriteMode)) {
+ throw new IllegalArgumentException("invalid overwriteMode value '" + overwriteMode
+ + "'. " + "Valid values are " + OVERWRITEMODE_VALUES);
+ }
+ this.overwriteMode = overwriteMode;
+ }
+
+ public String getOverwriteMode() {
+ return overwriteMode;
+ }
+
+ /**
+ * Add a mapper to convert the file names.
+ *
+ * @param mapper
+ * a <code>Mapper</code> value.
+ */
+ public void addMapper(Mapper mapper) {
+ if (this.mapper != null) {
+ throw new IllegalArgumentException("Cannot define more than one mapper");
+ }
+ this.mapper = mapper;
+ }
+
+ /**
+ * Add a nested filenamemapper.
+ *
+ * @param fileNameMapper
+ * the mapper to add.
+ */
+ public void add(FileNameMapper fileNameMapper) {
+ Mapper m = new Mapper(getProject());
+ m.add(fileNameMapper);
+ addMapper(m);
+ }
+
+ /**
+ * Returns the path of the file relative to the given base directory.
+ *
+ * @param base
+ * the parent directory to which the file must be evaluated.
+ * @param file
+ * the file for which the path should be returned
+ * @return the path of the file relative to the given base directory.
+ */
+ private String getPath(File base, File file) {
+ String absoluteBasePath = base.getAbsolutePath();
+
+ int beginIndex = absoluteBasePath.length();
+
+ // checks if the basePath ends with the file separator (which can for instance
+ // happen if the basePath is the root on unix)
+ if (!absoluteBasePath.endsWith(File.separator)) {
+ beginIndex++; // skip the seperator char as well
+ }
+
+ return file.getAbsolutePath().substring(beginIndex);
+ }
+
+}
diff --git a/src/java/org/apache/ivy/ant/IvyTask.java b/src/java/org/apache/ivy/ant/IvyTask.java
new file mode 100644
index 0000000..3533480
--- /dev/null
+++ b/src/java/org/apache/ivy/ant/IvyTask.java
@@ -0,0 +1,297 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.ant;
+
+import java.util.Date;
+import java.util.Locale;
+
+import org.apache.ivy.Ivy;
+import org.apache.ivy.core.IvyContext;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.report.ResolveReport;
+import org.apache.ivy.core.settings.IvySettings;
+import org.apache.ivy.util.DateUtil;
+import org.apache.ivy.util.Message;
+import org.apache.ivy.util.StringUtils;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.Reference;
+
+/**
+ * Base class for all ivy ant tasks, deal particularly with ivy instance storage in ant project.
+ */
+public abstract class IvyTask extends Task {
+ public static final String ANT_PROJECT_CONTEXT_KEY = "ant-project";
+
+ private Boolean validate = null;
+
+ private Reference antIvyEngineRef = null;
+
+ protected boolean doValidate(IvySettings ivy) {
+ if (validate != null) {
+ return validate.booleanValue();
+ }
+ return ivy.doValidate();
+ }
+
+ public boolean isValidate() {
+ return validate == null ? true : validate.booleanValue();
+ }
+
+ public void setValidate(boolean validate) {
+ this.validate = Boolean.valueOf(validate);
+ }
+
+ public void setSettingsRef(Reference ref) {
+ antIvyEngineRef = ref;
+ }
+
+ public Reference getSettingsRef() {
+ return antIvyEngineRef;
+ }
+
+ protected IvySettings getSettings() {
+ return getIvyInstance().getSettings();
+ }
+
+ protected Ivy getIvyInstance() {
+ Object antIvyEngine;
+ if (antIvyEngineRef != null) {
+ antIvyEngine = antIvyEngineRef.getReferencedObject(getProject());
+ if (!antIvyEngine.getClass().getName().equals(IvyAntSettings.class.getName())) {
+ throw new BuildException(antIvyEngineRef.getRefId()
+ + " doesn't reference an ivy:settings", getLocation());
+ }
+ if (!(antIvyEngine instanceof IvyAntSettings)) {
+ throw new BuildException(antIvyEngineRef.getRefId()
+ + " has been defined in a different classloader. "
+ + "Please use the same loader when defining your task, or "
+ + "redeclare your ivy:settings in this classloader", getLocation());
+ }
+ } else {
+ antIvyEngine = IvyAntSettings.getDefaultInstance(this);
+ }
+ Ivy ivy = ((IvyAntSettings) antIvyEngine).getConfiguredIvyInstance(this);
+ AntMessageLogger.register(this, ivy);
+ return ivy;
+ }
+
+ protected void setResolved(ResolveReport report, boolean keep) {
+ ModuleDescriptor md = report.getModuleDescriptor();
+ String[] confs = report.getConfigurations();
+ if (keep) {
+ getProject().addReference("ivy.resolved.report", report);
+ getProject().addReference("ivy.resolved.configurations.ref", confs);
+ getProject().addReference("ivy.resolved.descriptor", md);
+ }
+ String suffix = md.getModuleRevisionId().getModuleId().getOrganisation() + "."
+ + md.getModuleRevisionId().getModuleId().getName();
+ getProject().addReference("ivy.resolved.report." + suffix, report);
+ getProject().addReference("ivy.resolved.descriptor." + suffix, md);
+ getProject().addReference("ivy.resolved.configurations.ref." + suffix, confs);
+ }
+
+ protected void setResolved(ResolveReport report, String resolveId, boolean keep) {
+ setResolved(report, keep);
+ if (resolveId != null) {
+ ModuleDescriptor md = report.getModuleDescriptor();
+ String[] confs = report.getConfigurations();
+ getProject().addReference("ivy.resolved.report." + resolveId, report);
+ getProject().addReference("ivy.resolved.descriptor." + resolveId, md);
+ getProject().addReference("ivy.resolved.configurations.ref." + resolveId, confs);
+ }
+ }
+
+ protected String[] getResolvedConfigurations(String org, String module, boolean strict) {
+ return (String[]) getReference("ivy.resolved.configurations.ref", org, module, strict);
+ }
+
+ protected Object getResolvedDescriptor(String resolveId) {
+ return getResolvedDescriptor(resolveId, true);
+ }
+
+ protected Object getResolvedDescriptor(String resolveId, boolean strict) {
+ Object result = getProject().getReference("ivy.resolved.descriptor." + resolveId);
+ if (strict && (result == null)) {
+ throw new BuildException("ModuleDescriptor for resolve with id '" + resolveId
+ + "' not found.");
+ }
+ return result;
+ }
+
+ protected Object getResolvedDescriptor(String org, String module) {
+ return getResolvedDescriptor(org, module, false);
+ }
+
+ protected Object getResolvedDescriptor(String org, String module, boolean strict) {
+ return getReference("ivy.resolved.descriptor", org, module, strict);
+ }
+
+ private Object getReference(String prefix, String org, String module, boolean strict) {
+ Object reference = null;
+ if (org != null && module != null) {
+ reference = getProject().getReference(prefix + "." + org + "." + module);
+ }
+ if (!strict && reference == null) {
+ reference = getProject().getReference(prefix);
+ }
+ return reference;
+ }
+
+ protected ResolveReport getResolvedReport(String org, String module, String resolveId) {
+ ResolveReport result = null;
+
+ if (resolveId == null) {
+ result = (ResolveReport) getReference("ivy.resolved.report", org, module, false);
+ } else {
+ result = (ResolveReport) getReference("ivy.resolved.report." + resolveId, null, null,
+ false);
+ }
+
+ return result;
+ }
+
+ protected String[] splitConfs(String conf) {
+ if (conf == null) {
+ return null;
+ }
+ String[] confs = conf.split(",");
+ for (int i = 0; i < confs.length; i++) {
+ confs[i] = confs[i].trim();
+ }
+ return confs;
+ }
+
+ protected String mergeConfs(String[] conf) {
+ return StringUtils.join(conf, ", ");
+ }
+
+ protected static Date getPubDate(String date, Date def) {
+ if (date != null) {
+ if ("now".equals(date.toLowerCase(Locale.US))) {
+ return new Date();
+ }
+ try {
+ return DateUtil.parse(date);
+ } catch (Exception ex) {
+ throw new BuildException("Publication date provided in bad format. Should be '"
+ + DateUtil.DATE_FORMAT_PATTERN + "' and not '" + date + "'!");
+ }
+ } else {
+ return def;
+ }
+ }
+
+ protected String getProperty(String value, IvySettings ivy, String name) {
+ if (value == null) {
+ return getProperty(ivy, name);
+ } else {
+ value = ivy.substitute(value);
+ Message.debug("parameter found as attribute value: " + name + "=" + value);
+ return value;
+ }
+ }
+
+ protected String getProperty(String value, IvySettings ivy, String name, String resolveId) {
+ if (resolveId == null) {
+ return getProperty(value, ivy, name);
+ } else {
+ return getProperty(value, ivy, name + "." + resolveId);
+ }
+ }
+
+ protected String getProperty(IvySettings ivy, String name, String resolveId) {
+ if (resolveId == null) {
+ return getProperty(ivy, name);
+ } else {
+ return getProperty(ivy, name + "." + resolveId);
+ }
+ }
+
+ protected String getProperty(IvySettings ivy, String name) {
+ String val = ivy.getVariable(name);
+ if (val == null) {
+ val = ivy.substitute(getProject().getProperty(name));
+ if (val != null) {
+ Message.debug("parameter found as ant project property: " + name + "=" + val);
+ } else {
+ Message.debug("parameter not found: " + name);
+ }
+ } else {
+ val = ivy.substitute(val);
+ Message.debug("parameter found as ivy variable: " + name + "=" + val);
+ }
+ return val;
+ }
+
+ /**
+ * Called when task starts its execution.
+ */
+ protected void prepareTask() {
+ getProject().setProperty("ivy.version", Ivy.getIvyVersion());
+
+ // push current project and Ivy on the stack in context
+ IvyContext.pushNewCopyContext();
+ IvyContext.getContext().setIvy(getIvyInstance());
+ IvyContext.getContext().push(ANT_PROJECT_CONTEXT_KEY, getProject());
+ }
+
+ /**
+ * Called when task is about to finish Should clean up all state related information (stacks for
+ * example)
+ */
+ protected void finalizeTask() {
+ if (!IvyContext.getContext().pop(ANT_PROJECT_CONTEXT_KEY, getProject())) {
+ Message.error("ANT project poped from stack not equals current !. Ignoring");
+ }
+ IvyContext.popContext();
+ }
+
+ /**
+ * Ant task execute. Calls prepareTask, doExecute, finalzeTask
+ */
+ public final void execute() throws BuildException {
+ try {
+ prepareTask();
+ doExecute();
+ } finally {
+ finalizeTask();
+ }
+ }
+
+ /**
+ * The real logic of task execution after project has been set in the context. MUST be
+ * implemented by subclasses
+ *
+ * @throws BuildException
+ */
+ public abstract void doExecute() throws BuildException;
+
+ public String toString() {
+ return getClass().getName() + ":" + getTaskName();
+ }
+
+ /**
+ * Informs the user that the cache attribute is not supported any more.
+ */
+ protected void cacheAttributeNotSupported() {
+ throw new BuildException(
+ "cache attribute is not supported any more. See IVY-685 for details.");
+ }
+
+}
diff --git a/src/java/org/apache/ivy/ant/IvyVar.java b/src/java/org/apache/ivy/ant/IvyVar.java
new file mode 100644
index 0000000..cd8d670
--- /dev/null
+++ b/src/java/org/apache/ivy/ant/IvyVar.java
@@ -0,0 +1,132 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.ant;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Iterator;
+import java.util.Properties;
+
+import org.apache.ivy.Ivy;
+import org.apache.ivy.core.settings.IvySettings;
+import org.apache.tools.ant.BuildException;
+
+/**
+ * This task let user set ivy variables from ant.
+ */
+public class IvyVar extends IvyTask {
+ private String name;
+
+ private String value;
+
+ private File file;
+
+ private String url;
+
+ private String prefix;
+
+ public File getFile() {
+ return file;
+ }
+
+ public void setFile(File aFile) {
+ file = aFile;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String aName) {
+ name = aName;
+ }
+
+ public String getPrefix() {
+ return prefix;
+ }
+
+ public void setPrefix(String aPrefix) {
+ prefix = aPrefix;
+ }
+
+ public String getUrl() {
+ return url;
+ }
+
+ public void setUrl(String aUrl) {
+ url = aUrl;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public void setValue(String aValue) {
+ value = aValue;
+ }
+
+ public void doExecute() throws BuildException {
+ Ivy ivy = getIvyInstance();
+ IvySettings settings = ivy.getSettings();
+ if (getName() != null) {
+ settings.setVariable(getVarName(getName()), getValue());
+ } else {
+ Properties props = new Properties();
+ InputStream is = null;
+ try {
+ if (getFile() != null) {
+ is = new FileInputStream(getFile());
+ } else if (getUrl() != null) {
+ is = new URL(getUrl()).openStream();
+ } else {
+ throw new BuildException("specify either name or file or url to ivy var task");
+ }
+ props.load(is);
+ } catch (Exception ex) {
+ throw new BuildException("impossible to load variables from file: " + ex, ex);
+ } finally {
+ if (is != null) {
+ try {
+ is.close();
+ } catch (Exception e) {
+ // ignore
+ }
+ }
+ }
+ for (Iterator iter = props.keySet().iterator(); iter.hasNext();) {
+ String name = (String) iter.next();
+ String value = (String) props.get(name);
+ settings.setVariable(getVarName(name), value);
+ }
+ }
+ }
+
+ private String getVarName(String name) {
+ String prefix = getPrefix();
+ if (prefix != null) {
+ if (prefix.endsWith(".")) {
+ return prefix + name;
+ } else {
+ return prefix + "." + name;
+ }
+ }
+ return name;
+ }
+}
diff --git a/src/java/org/apache/ivy/ant/MapperAdapter.java b/src/java/org/apache/ivy/ant/MapperAdapter.java
new file mode 100644
index 0000000..a9ceab7
--- /dev/null
+++ b/src/java/org/apache/ivy/ant/MapperAdapter.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.ant;
+
+import org.apache.ivy.core.retrieve.FileNameMapper;
+import org.apache.tools.ant.types.Mapper;
+
+class MapperAdapter implements FileNameMapper {
+
+ private static final String[] EMPTY = new String[0];
+
+ private Mapper mapper;
+
+ MapperAdapter(Mapper mapper) {
+ this.mapper = mapper;
+ }
+
+ public String[] mapFileName(String fileName) {
+ String[] result = mapper.getImplementation().mapFileName(fileName);
+ if (result == null) {
+ result = EMPTY;
+ }
+
+ return result;
+ }
+
+}
diff --git a/src/java/org/apache/ivy/ant/PackageMapping.java b/src/java/org/apache/ivy/ant/PackageMapping.java
new file mode 100644
index 0000000..c848057
--- /dev/null
+++ b/src/java/org/apache/ivy/ant/PackageMapping.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.ant;
+
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+
+/**
+ * Describes a mapping between a package name and an org name revision uple
+ */
+public class PackageMapping {
+ private String pkg;
+
+ private String organisation;
+
+ private String module;
+
+ private String revision;
+
+ public String getModule() {
+ return module;
+ }
+
+ public void setModule(String module) {
+ this.module = module;
+ }
+
+ public String getOrganisation() {
+ return organisation;
+ }
+
+ public void setOrganisation(String organisation) {
+ this.organisation = organisation;
+ }
+
+ public String getRevision() {
+ return revision;
+ }
+
+ public void setRevision(String revision) {
+ this.revision = revision;
+ }
+
+ public String getPackage() {
+ return pkg;
+ }
+
+ public void setPackage(String package1) {
+ pkg = package1;
+ }
+
+ public ModuleRevisionId getModuleRevisionId() {
+ return ModuleRevisionId.newInstance(organisation, module, revision);
+ }
+}
diff --git a/src/java/org/apache/ivy/ant/antlib.xml b/src/java/org/apache/ivy/ant/antlib.xml
new file mode 100644
index 0000000..ac32740
--- /dev/null
+++ b/src/java/org/apache/ivy/ant/antlib.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<antlib xmlns:current="ant:current">
+ <typedef name="settings" classname="org.apache.ivy.ant.IvyAntSettings"/>
+ <typedef name="resources" classname="org.apache.ivy.ant.IvyResources" onerror="report"/>
+ <taskdef name="configure" classname="org.apache.ivy.ant.IvyConfigure"/>
+ <taskdef name="resolve" classname="org.apache.ivy.ant.IvyResolve"/>
+ <taskdef name="retrieve" classname="org.apache.ivy.ant.IvyRetrieve"/>
+ <taskdef name="deliver" classname="org.apache.ivy.ant.IvyDeliver"/>
+ <taskdef name="publish" classname="org.apache.ivy.ant.IvyPublish"/>
+ <taskdef name="extract" classname="org.apache.ivy.ant.IvyExtractFromSources"/>
+ <taskdef name="cachepath" classname="org.apache.ivy.ant.IvyCachePath"/>
+ <taskdef name="cachefileset" classname="org.apache.ivy.ant.IvyCacheFileset"/>
+ <taskdef name="report" classname="org.apache.ivy.ant.IvyReport"/>
+ <taskdef name="repreport" classname="org.apache.ivy.ant.IvyRepositoryReport"/>
+ <taskdef name="var" classname="org.apache.ivy.ant.IvyVar"/>
+ <taskdef name="check" classname="org.apache.ivy.ant.IvyCheck"/>
+ <taskdef name="artifactproperty" classname="org.apache.ivy.ant.IvyArtifactProperty"/>
+ <taskdef name="buildlist" classname="org.apache.ivy.ant.IvyBuildList"/>
+ <taskdef name="install" classname="org.apache.ivy.ant.IvyInstall"/>
+ <taskdef name="convertpom" classname="org.apache.ivy.ant.IvyConvertPom"/>
+ <taskdef name="makepom" classname="org.apache.ivy.ant.IvyMakePom"/>
+ <taskdef name="artifactreport" classname="org.apache.ivy.ant.IvyArtifactReport"/>
+ <taskdef name="info" classname="org.apache.ivy.ant.IvyInfo"/>
+ <taskdef name="addpath" classname="org.apache.ivy.ant.AddPathTask"/>
+ <taskdef name="listmodules" classname="org.apache.ivy.ant.IvyListModules"/>
+ <taskdef name="findrevision" classname="org.apache.ivy.ant.IvyFindRevision"/>
+ <taskdef name="buildnumber" classname="org.apache.ivy.ant.IvyBuildNumber"/>
+ <taskdef name="cleancache" classname="org.apache.ivy.ant.IvyCleanCache"/>
+ <taskdef name="buildobr" classname="org.apache.ivy.ant.BuildOBRTask" />
+ <taskdef name="convertmanifest" classname="org.apache.ivy.ant.ConvertManifestTask" />
+ <taskdef name="fixdeps" classname="org.apache.ivy.ant.FixDepsTask" />
+ <taskdef name="dependencytree" classname="org.apache.ivy.ant.IvyDependencyTree"/>
+ <taskdef name="checkdepsupdate" classname="org.apache.ivy.ant.IvyDependencyUpdateChecker"/>
+</antlib>
diff --git a/src/java/org/apache/ivy/core/ExecutionRelativeUrlResolver.java b/src/java/org/apache/ivy/core/ExecutionRelativeUrlResolver.java
new file mode 100644
index 0000000..db972cb
--- /dev/null
+++ b/src/java/org/apache/ivy/core/ExecutionRelativeUrlResolver.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+/**
+ * Resolve relative url relatively to the current execution directory instead of relatively to the
+ * context. This is was actually done prior 2.0. This class allow thus to work in a backward
+ * compatible mode.
+ */
+public class ExecutionRelativeUrlResolver extends RelativeUrlResolver {
+
+ public URL getURL(URL context, String url) throws MalformedURLException {
+ return new URL(url);
+ }
+
+}
diff --git a/src/java/org/apache/ivy/core/IvyContext.java b/src/java/org/apache/ivy/core/IvyContext.java
new file mode 100644
index 0000000..1e28a4b
--- /dev/null
+++ b/src/java/org/apache/ivy/core/IvyContext.java
@@ -0,0 +1,386 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core;
+
+import java.lang.ref.WeakReference;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Stack;
+
+import org.apache.ivy.Ivy;
+import org.apache.ivy.core.event.EventManager;
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.resolve.ResolveData;
+import org.apache.ivy.core.settings.IvySettings;
+import org.apache.ivy.plugins.circular.CircularDependencyStrategy;
+import org.apache.ivy.util.Message;
+import org.apache.ivy.util.MessageLogger;
+
+/**
+ * This class represents an execution context of an Ivy action. It contains several getters to
+ * retrieve information, like the used Ivy instance, the cache location...
+ *
+ * @see IvyThread
+ */
+public class IvyContext {
+
+ private static ThreadLocal/* <Stack<IvyContext>> */current = new ThreadLocal();
+
+ private Ivy defaultIvy;
+
+ private WeakReference/* <Ivy> */ivy = new WeakReference(null);
+
+ private Map contextMap = new HashMap();
+
+ private Thread operatingThread;
+
+ private ResolveData resolveData;
+
+ private DependencyDescriptor dd;
+
+ public IvyContext() {
+ }
+
+ public IvyContext(IvyContext ctx) {
+ defaultIvy = ctx.defaultIvy;
+ ivy = ctx.ivy;
+ contextMap = new HashMap(ctx.contextMap);
+ operatingThread = ctx.operatingThread;
+ resolveData = ctx.resolveData;
+ dd = ctx.dd;
+ }
+
+ public static IvyContext getContext() {
+ Stack cur = getCurrentStack();
+ if (cur.isEmpty()) {
+ cur.push(new IvyContext());
+ }
+ return (IvyContext) cur.peek();
+ }
+
+ private static Stack/* <IvyContext> */getCurrentStack() {
+ Stack cur = (Stack) current.get();
+ if (cur == null) {
+ cur = new Stack();
+ current.set(cur);
+ }
+ return cur;
+ }
+
+ /**
+ * Creates a new IvyContext and pushes it as the current context in the current thread.
+ * <p>
+ * {@link #popContext()} should usually be called when the job for which this context has been
+ * pushed is finished.
+ * </p>
+ *
+ * @return the newly pushed context
+ */
+ public static IvyContext pushNewContext() {
+ return pushContext(new IvyContext());
+ }
+
+ /**
+ * Creates a new IvyContext as a copy of the current one and pushes it as the current context in
+ * the current thread.
+ * <p>
+ * {@link #popContext()} should usually be called when the job for which this context has been
+ * pushed is finished.
+ * </p>
+ *
+ * @return the newly pushed context
+ */
+ public static IvyContext pushNewCopyContext() {
+ return pushContext(new IvyContext(getContext()));
+ }
+
+ /**
+ * Changes the context associated with this thread. This is especially useful when launching a
+ * new thread, to associate it with the same context as the initial one. Do not forget to call
+ * {@link #popContext()} when done.
+ *
+ * @param context
+ * the new context to use in this thread.
+ * @return the pushed context
+ */
+ public static IvyContext pushContext(IvyContext context) {
+ getCurrentStack().push(context);
+ return context;
+ }
+
+ /**
+ * Pops one context used with this thread. This is usually called after having finished a task
+ * for which a call to {@link #pushNewContext()} or {@link #pushContext(IvyContext)} was done
+ * prior to beginning the task.
+ *
+ * @return the popped context
+ */
+ public static IvyContext popContext() {
+ return (IvyContext) getCurrentStack().pop();
+ }
+
+ /**
+ * Reads the first object from the list saved under given key in the first context from the
+ * context stack in which this key is defined. If value under key in any of the contexts form
+ * the stack represents non List object then a RuntimeException is thrown.
+ * <p>
+ * This methods does a similar job to {@link #peek(String)}, except that it considers the whole
+ * context stack and not only one instance.
+ * </p>
+ *
+ * @param key
+ * context key for the string
+ * @return top object from the list (index 0) of the first context in the stack containing this
+ * key or null if no key or list empty in all contexts from the context stack
+ * @see #peek(String)
+ */
+ public static Object peekInContextStack(String key) {
+ Object value = null;
+ Stack contextStack = getCurrentStack();
+ for (int i = contextStack.size() - 1; i >= 0 && value == null; i--) {
+ IvyContext ctx = (IvyContext) contextStack.get(i);
+ value = ctx.peek(key);
+ }
+ return value;
+ }
+
+ /**
+ * Returns the current ivy instance.
+ * <p>
+ * When calling any public ivy method on an ivy instance, a reference to this instance is put in
+ * this context, and thus accessible using this method, until no code reference this instance
+ * and the garbage collector collects it.
+ * </p>
+ * <p>
+ * Then, or if no ivy method has been called, a default ivy instance is returned by this method,
+ * so that it never returns <code>null</code>.
+ * </p>
+ *
+ * @return the current ivy instance
+ */
+ public Ivy getIvy() {
+ Ivy ivy = peekIvy();
+ return ivy == null ? getDefaultIvy() : ivy;
+ }
+
+ /**
+ * Returns the Ivy instance associated with this context, or <code>null</code> if no such
+ * instance is currently associated with this context.
+ * <p>
+ * If you want get a default Ivy instance in case no instance if currently associated, use
+ * {@link #getIvy()}.
+ * </p>
+ *
+ * @return the current ivy instance, or <code>null</code> if there is no current ivy instance.
+ */
+ public Ivy peekIvy() {
+ Ivy ivy = (Ivy) this.ivy.get();
+ return ivy;
+ }
+
+ private Ivy getDefaultIvy() {
+ if (defaultIvy == null) {
+ defaultIvy = Ivy.newInstance();
+ try {
+ defaultIvy.configureDefault();
+ } catch (Exception e) {
+ Message.debug(e);
+ // ???
+ }
+ }
+ return defaultIvy;
+ }
+
+ public void setIvy(Ivy ivy) {
+ this.ivy = new WeakReference(ivy);
+ operatingThread = Thread.currentThread();
+ }
+
+ public IvySettings getSettings() {
+ return getIvy().getSettings();
+ }
+
+ public CircularDependencyStrategy getCircularDependencyStrategy() {
+ return getSettings().getCircularDependencyStrategy();
+ }
+
+ public Object get(String key) {
+ WeakReference ref = (WeakReference) contextMap.get(key);
+ return ref == null ? null : ref.get();
+ }
+
+ public void set(String key, Object value) {
+ contextMap.put(key, new WeakReference(value));
+ }
+
+ /**
+ * Reads the first object from the list saved under given key in the context. If value under key
+ * represents non List object then a RuntimeException is thrown.
+ *
+ * @param key
+ * context key for the string
+ * @return top object from the list (index 0) or null if no key or list empty
+ */
+ public Object peek(String key) {
+ synchronized (contextMap) {
+ Object o = contextMap.get(key);
+ if (o == null) {
+ return null;
+ }
+ if (o instanceof List) {
+ if (((List) o).size() == 0) {
+ return null;
+ }
+ Object ret = ((List) o).get(0);
+ return ret;
+ } else {
+ throw new RuntimeException("Cannot top from non List object " + o);
+ }
+ }
+ }
+
+ /**
+ * Removes and returns first object from the list saved under given key in the context. If value
+ * under key represents non List object then a RuntimeException is thrown.
+ *
+ * @param key
+ * context key for the string
+ * @return top object from the list (index 0) or null if no key or list empty
+ */
+ public Object pop(String key) {
+ synchronized (contextMap) {
+ Object o = contextMap.get(key);
+ if (o == null) {
+ return null;
+ }
+ if (o instanceof List) {
+ if (((List) o).size() == 0) {
+ return null;
+ }
+ Object ret = ((List) o).remove(0);
+ return ret;
+ } else {
+ throw new RuntimeException("Cannot pop from non List object " + o);
+ }
+ }
+ }
+
+ /**
+ * Removes and returns first object from the list saved under given key in the context but only
+ * if it equals the given expectedValue - if not a false value is returned. If value under key
+ * represents non List object then a RuntimeException is thrown.
+ *
+ * @param key
+ * context key for the string
+ * @return true if the r
+ */
+ public boolean pop(String key, Object expectedValue) {
+ synchronized (contextMap) {
+ Object o = contextMap.get(key);
+ if (o == null) {
+ return false;
+ }
+ if (o instanceof List) {
+ if (((List) o).size() == 0) {
+ return false;
+ }
+ Object top = ((List) o).get(0);
+ if (!top.equals(expectedValue)) {
+ return false;
+ }
+ ((List) o).remove(0);
+ return true;
+ } else {
+ throw new RuntimeException("Cannot pop from non List object " + o);
+ }
+ }
+ }
+
+ /**
+ * Puts a new object at the start of the list saved under given key in the context. If value
+ * under key represents non List object then a RuntimeException is thrown. If no list exists
+ * under given key a new LinkedList is created. This is kept without WeakReference in opposite
+ * to the put() results.
+ *
+ * @param key
+ * key context key for the string
+ * @param value
+ * value to be saved under the key
+ */
+ public void push(String key, Object value) {
+ synchronized (contextMap) {
+ if (!contextMap.containsKey(key)) {
+ contextMap.put(key, new LinkedList());
+ }
+ Object o = contextMap.get(key);
+ if (o instanceof List) {
+ ((List) o).add(0, value);
+ } else {
+ throw new RuntimeException("Cannot push to non List object " + o);
+ }
+ }
+ }
+
+ public Thread getOperatingThread() {
+ return operatingThread;
+ }
+
+ public MessageLogger getMessageLogger() {
+ // calling getIvy() instead of peekIvy() is not possible here: it will initialize a default
+ // Ivy instance, with default settings, but settings themselves may log messages and lead to
+ // a call to this method. So we use the current Ivy instance if any, or the default Ivy
+ // instance, or the default MessageLogger.
+ Ivy ivy = peekIvy();
+ if (ivy == null) {
+ if (defaultIvy == null) {
+ return Message.getDefaultLogger();
+ } else {
+ return defaultIvy.getLoggerEngine();
+ }
+ } else {
+ return ivy.getLoggerEngine();
+ }
+ }
+
+ public EventManager getEventManager() {
+ return getIvy().getEventManager();
+ }
+
+ public void checkInterrupted() {
+ getIvy().checkInterrupted();
+ }
+
+ public void setResolveData(ResolveData data) {
+ this.resolveData = data;
+ }
+
+ public ResolveData getResolveData() {
+ return resolveData;
+ }
+
+ public void setDependencyDescriptor(DependencyDescriptor dd) {
+ this.dd = dd;
+ }
+
+ public DependencyDescriptor getDependencyDescriptor() {
+ return dd;
+ }
+
+}
diff --git a/src/java/org/apache/ivy/core/IvyPatternHelper.java b/src/java/org/apache/ivy/core/IvyPatternHelper.java
new file mode 100644
index 0000000..3300b36
--- /dev/null
+++ b/src/java/org/apache/ivy/core/IvyPatternHelper.java
@@ -0,0 +1,526 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Stack;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.ivy.core.cache.ArtifactOrigin;
+import org.apache.ivy.core.cache.RepositoryCacheManager;
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.DefaultArtifact;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.settings.IvyVariableContainer;
+import org.apache.ivy.core.settings.IvyVariableContainerImpl;
+import org.apache.ivy.util.Message;
+
+/**
+ */
+public final class IvyPatternHelper {
+
+ private IvyPatternHelper() {
+ // Helper class
+ }
+
+ public static final String CONF_KEY = "conf";
+
+ public static final String TYPE_KEY = "type";
+
+ public static final String EXT_KEY = "ext";
+
+ public static final String ARTIFACT_KEY = "artifact";
+
+ public static final String BRANCH_KEY = "branch";
+
+ public static final String REVISION_KEY = "revision";
+
+ public static final String MODULE_KEY = "module";
+
+ public static final String ORGANISATION_KEY = "organisation";
+
+ public static final String ORGANISATION_KEY2 = "organization";
+
+ public static final String ORGANISATION_PATH_KEY = "orgPath";
+
+ public static final String ORIGINAL_ARTIFACTNAME_KEY = "originalname";
+
+ private static final Pattern PARAM_PATTERN = Pattern.compile("\\@\\{(.*?)\\}");
+
+ private static final Pattern VAR_PATTERN = Pattern.compile("\\$\\{(.*?)\\}");
+
+ public static String substitute(String pattern, ModuleRevisionId moduleRevision) {
+ return substitute(pattern, moduleRevision.getOrganisation(), moduleRevision.getName(),
+ moduleRevision.getBranch(), moduleRevision.getRevision(), "ivy", "ivy", "xml", null,
+ null, moduleRevision.getQualifiedExtraAttributes(), null);
+ }
+
+ public static String substitute(String pattern, ModuleRevisionId moduleRevision,
+ String artifact, String type, String ext) {
+ return substitute(pattern, moduleRevision, new DefaultArtifact(moduleRevision, null,
+ artifact, type, ext));
+ }
+
+ public static String substitute(String pattern, Artifact artifact) {
+ return substitute(pattern, artifact, (String) null);
+ }
+
+ public static String substitute(String pattern, Artifact artifact, ArtifactOrigin origin) {
+ return substitute(pattern, artifact.getModuleRevisionId(), artifact, (String) null, origin);
+ }
+
+ public static String substitute(String pattern, Artifact artifact, String conf) {
+ return substitute(pattern, artifact.getModuleRevisionId(), artifact, conf,
+ (ArtifactOrigin) null);
+ }
+
+ public static String substitute(String pattern, ModuleRevisionId mrid, Artifact artifact) {
+ return substitute(pattern, mrid, artifact, (String) null, (ArtifactOrigin) null);
+ }
+
+ public static String substitute(String pattern, ModuleRevisionId mrid, Artifact artifact,
+ String conf, ArtifactOrigin origin) {
+ return substitute(pattern, mrid.getOrganisation(), mrid.getName(), mrid.getBranch(),
+ mrid.getRevision(), artifact.getName(), artifact.getType(), artifact.getExt(), conf,
+ origin, mrid.getQualifiedExtraAttributes(), artifact.getQualifiedExtraAttributes());
+ }
+
+ public static String substitute(String pattern, String org, String module, String revision,
+ String artifact, String type, String ext) {
+ return substitute(pattern, org, module, (String) null, revision, artifact, type, ext,
+ (String) null, (ArtifactOrigin) null, (Map) null, (Map) null);
+ }
+
+ // CheckStyle:ParameterNumber OFF
+ public static String substitute(String pattern, String org, String module, String revision,
+ String artifact, String type, String ext, String conf) {
+ return substitute(pattern, org, module, (String) null, revision, artifact, type, ext, conf,
+ (ArtifactOrigin) null, (Map) null, (Map) null);
+ }
+
+ public static String substitute(String pattern, String org, String module, String revision,
+ String artifact, String type, String ext, String conf, Map extraModuleAttributes,
+ Map extraArtifactAttributes) {
+ return substitute(pattern, org, module, (String) null, revision, artifact, type, ext, conf,
+ (ArtifactOrigin) null, extraModuleAttributes, extraArtifactAttributes);
+ }
+
+ public static String substitute(String pattern, String org, String module, String branch,
+ String revision, String artifact, String type, String ext, String conf,
+ ArtifactOrigin origin, Map extraModuleAttributes, Map extraArtifactAttributes) {
+ Map tokens = new HashMap();
+ if (extraModuleAttributes != null) {
+ for (Iterator entries = extraModuleAttributes.entrySet().iterator(); entries.hasNext();) {
+ Map.Entry entry = (Map.Entry) entries.next();
+ String token = (String) entry.getKey();
+ if (token.indexOf(':') > 0) {
+ token = token.substring(token.indexOf(':') + 1);
+ }
+ tokens.put(token, entry.getValue());
+ }
+ }
+ if (extraArtifactAttributes != null) {
+ for (Iterator entries = extraArtifactAttributes.entrySet().iterator(); entries
+ .hasNext();) {
+ Map.Entry entry = (Map.Entry) entries.next();
+ String token = (String) entry.getKey();
+ if (token.indexOf(':') > 0) {
+ token = token.substring(token.indexOf(':') + 1);
+ }
+ tokens.put(token, entry.getValue());
+ }
+ }
+ tokens.put(ORGANISATION_KEY, org == null ? "" : org);
+ tokens.put(ORGANISATION_KEY2, org == null ? "" : org);
+ tokens.put(ORGANISATION_PATH_KEY, org == null ? "" : org.replace('.', '/'));
+ tokens.put(MODULE_KEY, module == null ? "" : module);
+ tokens.put(BRANCH_KEY, branch == null ? "" : branch);
+ tokens.put(REVISION_KEY, revision == null ? "" : revision);
+ tokens.put(ARTIFACT_KEY, artifact == null ? module : artifact);
+ tokens.put(TYPE_KEY, type == null ? "jar" : type);
+ tokens.put(EXT_KEY, ext == null ? "jar" : ext);
+ tokens.put(CONF_KEY, conf == null ? "default" : conf);
+ if (origin == null) {
+ tokens.put(ORIGINAL_ARTIFACTNAME_KEY, new OriginalArtifactNameValue(org, module,
+ branch, revision, artifact, type, ext, extraModuleAttributes,
+ extraArtifactAttributes));
+ } else {
+ tokens.put(ORIGINAL_ARTIFACTNAME_KEY, new OriginalArtifactNameValue(origin));
+ }
+
+ return substituteTokens(pattern, tokens);
+ }
+
+ // CheckStyle:ParameterNumber ON
+
+ public static String substituteVariables(String pattern, Map variables) {
+ return substituteVariables(pattern, new IvyVariableContainerImpl(variables), new Stack());
+ }
+
+ public static String substituteVariables(String pattern, IvyVariableContainer variables) {
+ return substituteVariables(pattern, variables, new Stack());
+ }
+
+ private static String substituteVariables(String pattern, IvyVariableContainer variables,
+ Stack substituting) {
+ // if you supply null, null is what you get
+ if (pattern == null) {
+ return null;
+ }
+
+ Matcher m = VAR_PATTERN.matcher(pattern);
+
+ boolean useVariables = false;
+ StringBuffer sb = null;
+ while (m.find()) {
+ if (!useVariables) {
+ useVariables = true;
+ sb = new StringBuffer();
+ }
+ String var = m.group(1);
+ String val = variables.getVariable(var);
+ if (val != null) {
+ int index = substituting.indexOf(var);
+ if (index != -1) {
+ List cycle = new ArrayList(substituting.subList(index, substituting.size()));
+ cycle.add(var);
+ throw new IllegalArgumentException("cyclic variable definition: cycle = "
+ + cycle);
+ }
+ substituting.push(var);
+ val = substituteVariables(val, variables, substituting);
+ substituting.pop();
+ } else {
+ val = m.group();
+ }
+ m.appendReplacement(sb, val.replaceAll("\\\\", "\\\\\\\\").replaceAll("\\$", "\\\\\\$"));
+ }
+ if (useVariables) {
+ m.appendTail(sb);
+ return sb.toString();
+ } else {
+ return pattern;
+ }
+ }
+
+ public static String substituteTokens(String pattern, Map tokens) {
+ Map tokensCopy = new HashMap(tokens);
+ if (tokensCopy.containsKey(ORGANISATION_KEY) && !tokensCopy.containsKey(ORGANISATION_KEY2)) {
+ tokensCopy.put(ORGANISATION_KEY2, tokensCopy.get(ORGANISATION_KEY));
+ }
+ if (tokensCopy.containsKey(ORGANISATION_KEY)
+ && !tokensCopy.containsKey(ORGANISATION_PATH_KEY)) {
+ String org = (String) tokensCopy.get(ORGANISATION_KEY);
+ tokensCopy.put(ORGANISATION_PATH_KEY, org == null ? "" : org.replace('.', '/'));
+ }
+
+ StringBuffer buffer = new StringBuffer();
+
+ char[] chars = pattern.toCharArray();
+
+ StringBuffer optionalPart = null;
+ StringBuffer tokenBuffer = null;
+ boolean insideOptionalPart = false;
+ boolean insideToken = false;
+ boolean tokenSeen = false;
+ boolean tokenHadValue = false;
+
+ for (int i = 0; i < chars.length; i++) {
+ switch (chars[i]) {
+ case '(':
+ if (insideOptionalPart) {
+ throw new IllegalArgumentException(
+ "invalid start of optional part at position " + i + " in pattern "
+ + pattern);
+ }
+
+ optionalPart = new StringBuffer();
+ insideOptionalPart = true;
+ tokenSeen = false;
+ tokenHadValue = false;
+ break;
+
+ case ')':
+ if (!insideOptionalPart || insideToken) {
+ throw new IllegalArgumentException(
+ "invalid end of optional part at position " + i + " in pattern "
+ + pattern);
+ }
+
+ if (tokenHadValue) {
+ buffer.append(optionalPart.toString());
+ } else if (!tokenSeen) {
+ buffer.append('(').append(optionalPart.toString()).append(')');
+ }
+
+ insideOptionalPart = false;
+ break;
+
+ case '[':
+ if (insideToken) {
+ throw new IllegalArgumentException("invalid start of token at position "
+ + i + " in pattern " + pattern);
+ }
+
+ tokenBuffer = new StringBuffer();
+ insideToken = true;
+ break;
+
+ case ']':
+ if (!insideToken) {
+ throw new IllegalArgumentException("invalid end of token at position " + i
+ + " in pattern " + pattern);
+ }
+
+ String token = tokenBuffer.toString();
+ Object tokenValue = tokensCopy.get(token);
+ String value = (tokenValue == null) ? null : tokenValue.toString();
+
+ if (insideOptionalPart) {
+ tokenHadValue = (value != null) && (value.length() > 0);
+ optionalPart.append(value);
+ } else {
+ if (value == null) { // the token wasn't set, it's kept as is
+ value = "[" + token + "]";
+ }
+ buffer.append(value);
+ }
+
+ insideToken = false;
+ tokenSeen = true;
+ break;
+
+ default:
+ if (insideToken) {
+ tokenBuffer.append(chars[i]);
+ } else if (insideOptionalPart) {
+ optionalPart.append(chars[i]);
+ } else {
+ buffer.append(chars[i]);
+ }
+
+ break;
+ }
+ }
+
+ if (insideToken) {
+ throw new IllegalArgumentException("last token hasn't been closed in pattern "
+ + pattern);
+ }
+
+ if (insideOptionalPart) {
+ throw new IllegalArgumentException("optional part hasn't been closed in pattern "
+ + pattern);
+ }
+
+ return buffer.toString();
+ }
+
+ public static String substituteVariable(String pattern, String variable, String value) {
+ StringBuffer buf = new StringBuffer(pattern);
+ substituteVariable(buf, variable, value);
+ return buf.toString();
+ }
+
+ public static void substituteVariable(StringBuffer buf, String variable, String value) {
+ String from = "${" + variable + "}";
+ int fromLength = from.length();
+ for (int index = buf.indexOf(from); index != -1; index = buf.indexOf(from, index)) {
+ buf.replace(index, index + fromLength, value);
+ }
+ }
+
+ public static String substituteToken(String pattern, String token, String value) {
+ StringBuffer buf = new StringBuffer(pattern);
+ substituteToken(buf, token, value);
+ return buf.toString();
+ }
+
+ public static void substituteToken(StringBuffer buf, String token, String value) {
+ String from = getTokenString(token);
+ int fromLength = from.length();
+ for (int index = buf.indexOf(from); index != -1; index = buf.indexOf(from, index)) {
+ buf.replace(index, index + fromLength, value);
+ }
+ }
+
+ public static String getTokenString(String token) {
+ return "[" + token + "]";
+ }
+
+ public static String substituteParams(String pattern, Map params) {
+ return substituteParams(pattern, new IvyVariableContainerImpl(params), new Stack());
+ }
+
+ private static String substituteParams(String pattern, IvyVariableContainer params,
+ Stack substituting) {
+ // TODO : refactor this with substituteVariables
+ // if you supply null, null is what you get
+ if (pattern == null) {
+ return null;
+ }
+
+ Matcher m = PARAM_PATTERN.matcher(pattern);
+
+ StringBuffer sb = new StringBuffer();
+ while (m.find()) {
+ String var = m.group(1);
+ String val = params.getVariable(var);
+ if (val != null) {
+ int index = substituting.indexOf(var);
+ if (index != -1) {
+ List cycle = new ArrayList(substituting.subList(index, substituting.size()));
+ cycle.add(var);
+ throw new IllegalArgumentException("cyclic param definition: cycle = " + cycle);
+ }
+ substituting.push(var);
+ val = substituteVariables(val, params, substituting);
+ substituting.pop();
+ } else {
+ val = m.group();
+ }
+ m.appendReplacement(sb, val.replaceAll("\\\\", "\\\\\\\\").replaceAll("\\@", "\\\\\\@"));
+ }
+ m.appendTail(sb);
+
+ return sb.toString();
+ }
+
+ /**
+ * This class returns the original name of the artifact 'on demand'. This is done to avoid
+ * having to read the cached datafile containing the original location of the artifact if we
+ * don't need it.
+ */
+ private static class OriginalArtifactNameValue {
+ // module properties
+ private String org;
+
+ private String moduleName;
+
+ private String branch;
+
+ private String revision;
+
+ private Map extraModuleAttributes;
+
+ // artifact properties
+ private String artifactName;
+
+ private String artifactType;
+
+ private String artifactExt;
+
+ private Map extraArtifactAttributes;
+
+ // cached origin;
+ private ArtifactOrigin origin;
+
+ public OriginalArtifactNameValue(String org, String moduleName, String branch,
+ String revision, String artifactName, String artifactType, String artifactExt,
+ Map extraModuleAttributes, Map extraArtifactAttributes) {
+ this.org = org;
+ this.moduleName = moduleName;
+ this.branch = branch;
+ this.revision = revision;
+ this.artifactName = artifactName;
+ this.artifactType = artifactType;
+ this.artifactExt = artifactExt;
+ this.extraModuleAttributes = extraModuleAttributes;
+ this.extraArtifactAttributes = extraArtifactAttributes;
+ }
+
+ /**
+ * @param origin
+ */
+ public OriginalArtifactNameValue(ArtifactOrigin origin) {
+ this.origin = origin;
+ }
+
+ // Called by substituteTokens only if the original artifact name is needed
+ public String toString() {
+ if (origin == null) {
+ ModuleRevisionId revId = ModuleRevisionId.newInstance(org, moduleName, branch,
+ revision, extraModuleAttributes);
+ Artifact artifact = new DefaultArtifact(revId, null, artifactName, artifactType,
+ artifactExt, extraArtifactAttributes);
+
+ // TODO cache: see how we could know which actual cache manager to use, since this
+ // will fail when using a resolver in a chain with a specific cache manager
+ RepositoryCacheManager cacheManager = IvyContext.getContext().getSettings()
+ .getResolver(revId).getRepositoryCacheManager();
+
+ origin = cacheManager.getSavedArtifactOrigin(artifact);
+
+ if (ArtifactOrigin.isUnknown(origin)) {
+ Message.debug("no artifact origin found for " + artifact + " in "
+ + cacheManager);
+ return null;
+ }
+ }
+
+ if (ArtifactOrigin.isUnknown(origin)) {
+ return null;
+ }
+
+ // we assume that the original filename is the last part of the original file location
+ String location = origin.getLocation();
+ int lastPathIndex = location.lastIndexOf('/');
+ if (lastPathIndex == -1) {
+ lastPathIndex = location.lastIndexOf('\\');
+ }
+ int lastColonIndex = location.lastIndexOf('.');
+
+ return location.substring(lastPathIndex + 1, lastColonIndex);
+ }
+ }
+
+ public static String getTokenRoot(String pattern) {
+ int index = pattern.indexOf('[');
+ if (index == -1) {
+ return pattern;
+ } else {
+ // it could be that pattern is something like "lib/([optional]/)[module]"
+ // we don't want the '(' in the result
+ int optionalIndex = pattern.indexOf('(');
+ if (optionalIndex >= 0) {
+ index = Math.min(index, optionalIndex);
+ }
+ return pattern.substring(0, index);
+ }
+ }
+
+ public static String getFirstToken(String pattern) {
+ if (pattern == null) {
+ return null;
+ }
+ int startIndex = pattern.indexOf('[');
+ if (startIndex == -1) {
+ return null;
+ }
+ int endIndex = pattern.indexOf(']', startIndex);
+ if (endIndex == -1) {
+ return null;
+ }
+ return pattern.substring(startIndex + 1, endIndex);
+ }
+}
diff --git a/src/java/org/apache/ivy/core/IvyThread.java b/src/java/org/apache/ivy/core/IvyThread.java
new file mode 100644
index 0000000..b0d2a51
--- /dev/null
+++ b/src/java/org/apache/ivy/core/IvyThread.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core;
+
+/**
+ * A simple thread subclass associated the same IvyContext as the thread in which it is
+ * instanciated. If you override the run target, then you will have to call initContext() to do the
+ * association with the original IvyContext.
+ *
+ * @see IvyContext
+ */
+public class IvyThread extends Thread {
+ private IvyContext context = IvyContext.getContext();
+
+ public IvyThread() {
+ super();
+ }
+
+ public IvyThread(Runnable target, String name) {
+ super(target, name);
+ }
+
+ public IvyThread(Runnable target) {
+ super(target);
+ }
+
+ public IvyThread(String name) {
+ super(name);
+ }
+
+ public IvyThread(ThreadGroup group, Runnable target, String name, long stackSize) {
+ super(group, target, name, stackSize);
+ }
+
+ public IvyThread(ThreadGroup group, Runnable target, String name) {
+ super(group, target, name);
+ }
+
+ public IvyThread(ThreadGroup group, Runnable target) {
+ super(group, target);
+ }
+
+ public IvyThread(ThreadGroup group, String name) {
+ super(group, name);
+ }
+
+ public void run() {
+ initContext();
+ super.run();
+ }
+
+ protected void initContext() {
+ IvyContext.pushContext(context);
+ }
+}
diff --git a/src/java/org/apache/ivy/core/LogOptions.java b/src/java/org/apache/ivy/core/LogOptions.java
new file mode 100644
index 0000000..942f481
--- /dev/null
+++ b/src/java/org/apache/ivy/core/LogOptions.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core;
+
+public class LogOptions {
+ /**
+ * Defaults log settings. Output all usual messages during the resolve process.
+ */
+ public static final String LOG_DEFAULT = "default";
+
+ /**
+ * This log setting disable all usual messages but download ones.
+ */
+ public static final String LOG_DOWNLOAD_ONLY = "download-only";
+
+ /**
+ * This log setting disable all usual messages during the resolve process.
+ */
+ public static final String LOG_QUIET = "quiet";
+
+ /**
+ * The log settings to use. One of {@link #LOG_DEFAULT}, {@link #LOG_DOWNLOAD_ONLY},
+ * {@link #LOG_QUIET}
+ */
+ private String log = LOG_DEFAULT;
+
+ public LogOptions() {
+ }
+
+ public LogOptions(LogOptions options) {
+ log = options.log;
+ }
+
+ public String getLog() {
+ return log;
+ }
+
+ public LogOptions setLog(String log) {
+ this.log = log;
+ return this;
+ }
+}
diff --git a/src/java/org/apache/ivy/core/NormalRelativeUrlResolver.java b/src/java/org/apache/ivy/core/NormalRelativeUrlResolver.java
new file mode 100644
index 0000000..691560a
--- /dev/null
+++ b/src/java/org/apache/ivy/core/NormalRelativeUrlResolver.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+/**
+ * Normal implementation of RelativeUrlResolver.
+ */
+public class NormalRelativeUrlResolver extends RelativeUrlResolver {
+
+ public URL getURL(URL context, String url) throws MalformedURLException {
+ return new URL(context, url);
+ }
+
+}
diff --git a/src/java/org/apache/ivy/core/RelativeUrlResolver.java b/src/java/org/apache/ivy/core/RelativeUrlResolver.java
new file mode 100644
index 0000000..2fac8cb
--- /dev/null
+++ b/src/java/org/apache/ivy/core/RelativeUrlResolver.java
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+/**
+ * Resolve an file or url relatively to its context.
+ */
+public abstract class RelativeUrlResolver {
+
+ /**
+ * Resolve the url in the context of context.
+ *
+ * @param context
+ * The URL of the ressource containing the reference url
+ * @param url
+ * a relative or absolution url string
+ * @throws MalformedURLException
+ */
+ public abstract URL getURL(URL context, String url) throws MalformedURLException;
+
+ /**
+ * Relsovle file or url path relatively to a context. file is considered first. If file is not
+ * defined, url will be considered.
+ *
+ * @param context
+ * The URL of the ressource containing the reference file or url
+ * @param file
+ * a relative or absolute path
+ * @param url
+ * a relative or absolution url string
+ * @return the resulting url or null if faile and url are null.
+ * @throws MalformedURLException
+ */
+ public URL getURL(URL context, String file, String url) throws MalformedURLException {
+ if (file != null) {
+ File f = new File(file);
+ if (f.isAbsolute()) {
+ return f.toURI().toURL();
+ } else {
+ return getURL(context, file);
+ }
+ } else if (url != null) {
+ return getURL(context, url);
+ } else {
+ return null;
+ }
+ }
+}
diff --git a/src/java/org/apache/ivy/core/cache/ArtifactOrigin.java b/src/java/org/apache/ivy/core/cache/ArtifactOrigin.java
new file mode 100644
index 0000000..e132b2e
--- /dev/null
+++ b/src/java/org/apache/ivy/core/cache/ArtifactOrigin.java
@@ -0,0 +1,174 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.cache;
+
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.util.Checks;
+
+/**
+ * This class contains information about the origin of an artifact.
+ *
+ * @see org.apache.ivy.plugins.resolver.BasicResolver
+ * @see org.apache.ivy.plugins.resolver.util.ResolvedResource
+ */
+public class ArtifactOrigin {
+ private static final String UNKNOWN = "UNKNOWN";
+
+ /**
+ * ArtifactOrigin instance used when the origin is unknown.
+ */
+ public static final ArtifactOrigin unkwnown(Artifact artifact) {
+ return new ArtifactOrigin(artifact, false, UNKNOWN);
+ }
+
+ public static final boolean isUnknown(ArtifactOrigin artifact) {
+ return artifact == null || UNKNOWN.equals(artifact.getLocation());
+ }
+
+ public static final boolean isUnknown(String location) {
+ return location == null || UNKNOWN.equals(location);
+ }
+
+ private static final int MAGIC_HASH_VALUE = 31;
+
+ private boolean isLocal;
+
+ private String location;
+
+ private Artifact artifact;
+
+ private Long lastChecked;
+
+ private boolean exists = true;
+
+ /**
+ * Create a new instance
+ *
+ * @param artifact
+ * the artifact pointed by this location. Must not be <code>null</code>.
+ * @param isLocal
+ * <code>boolean</code> value indicating if the resource is local (on the
+ * filesystem).
+ * @param location
+ * the location of the resource (normally a url). Must not be <code>null</code>.
+ */
+ public ArtifactOrigin(Artifact artifact, boolean isLocal, String location) {
+ Checks.checkNotNull(artifact, "artifact");
+ Checks.checkNotNull(location, "location");
+ this.artifact = artifact;
+ this.isLocal = isLocal;
+ this.location = location;
+ }
+
+ /**
+ * Is this resource local to this host, i.e. is it on the file system?
+ *
+ * @return <code>boolean</code> value indicating if the resource is local.
+ */
+ public boolean isLocal() {
+ return isLocal;
+ }
+
+ /**
+ * Return the location of the resource (normally a url)
+ *
+ * @return the location of the resource
+ */
+ public String getLocation() {
+ return location;
+ }
+
+ public void setLocation(String location) {
+ this.location = location;
+ }
+
+ /**
+ * Return the artifact that this location is pointing at.
+ *
+ * @return the artifact that this location is pointing at.
+ */
+ public Artifact getArtifact() {
+ return artifact;
+ }
+
+ /**
+ * The last time the resource was checked to be up to date. Maybe <code>null</code> if this
+ * information is not actually used by in some case.
+ *
+ * @return
+ */
+ public Long getLastChecked() {
+ return lastChecked;
+ }
+
+ public void setLastChecked(Long lastChecked) {
+ this.lastChecked = lastChecked;
+ }
+
+ public boolean isExists() {
+ return exists;
+ }
+
+ public void setExist(boolean exists) {
+ this.exists = exists;
+ }
+
+ public String toString() {
+ return "ArtifactOrigin { isLocal=" + isLocal + ", location=" + location + ", lastChecked="
+ + lastChecked + ", exists=" + exists + "}";
+ }
+
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ ArtifactOrigin that = (ArtifactOrigin) o;
+
+ if (isLocal != that.isLocal) {
+ return false;
+ }
+ if (!location.equals(that.location)) {
+ return false;
+ }
+ if (lastChecked == null) {
+ if (that.lastChecked != null) {
+ return false;
+ }
+ } else if (!lastChecked.equals(that.lastChecked)) {
+ return false;
+ }
+ if (exists != that.exists) {
+ return false;
+ }
+
+ return true;
+ }
+
+ public int hashCode() {
+ int result;
+ result = (isLocal ? 1 : 0);
+ result = MAGIC_HASH_VALUE * result + location.hashCode();
+ result = MAGIC_HASH_VALUE * result + ((lastChecked == null) ? 0 : lastChecked.hashCode());
+ result = MAGIC_HASH_VALUE * result + (exists ? 1 : 0);
+ return result;
+ }
+}
diff --git a/src/java/org/apache/ivy/core/cache/CacheDownloadOptions.java b/src/java/org/apache/ivy/core/cache/CacheDownloadOptions.java
new file mode 100644
index 0000000..674abf2
--- /dev/null
+++ b/src/java/org/apache/ivy/core/cache/CacheDownloadOptions.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.cache;
+
+public class CacheDownloadOptions {
+
+ private DownloadListener listener = null;
+
+ private boolean force = false;
+
+ public DownloadListener getListener() {
+ return listener;
+ }
+
+ public CacheDownloadOptions setListener(DownloadListener listener) {
+ this.listener = listener;
+ return this;
+ }
+
+ public boolean isForce() {
+ return force;
+ }
+
+ public CacheDownloadOptions setForce(boolean force) {
+ this.force = force;
+ return this;
+ }
+}
diff --git a/src/java/org/apache/ivy/core/cache/CacheMetadataOptions.java b/src/java/org/apache/ivy/core/cache/CacheMetadataOptions.java
new file mode 100644
index 0000000..a1c77b4
--- /dev/null
+++ b/src/java/org/apache/ivy/core/cache/CacheMetadataOptions.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.cache;
+
+import org.apache.ivy.plugins.namespace.Namespace;
+
+public class CacheMetadataOptions extends CacheDownloadOptions {
+ private boolean validate = false;
+
+ private Namespace namespace = Namespace.SYSTEM_NAMESPACE;
+
+ private Boolean isCheckmodified = null;
+
+ private String changingMatcherName = null;
+
+ private String changingPattern = null;
+
+ private boolean checkTTL = true;
+
+ public Namespace getNamespace() {
+ return namespace;
+ }
+
+ public CacheMetadataOptions setNamespace(Namespace namespace) {
+ this.namespace = namespace;
+ return this;
+ }
+
+ public boolean isValidate() {
+ return validate;
+ }
+
+ public CacheMetadataOptions setValidate(boolean validate) {
+ this.validate = validate;
+ return this;
+ }
+
+ public Boolean isCheckmodified() {
+ return isCheckmodified;
+ }
+
+ public CacheMetadataOptions setCheckmodified(Boolean isCheckmodified) {
+ this.isCheckmodified = isCheckmodified;
+ return this;
+ }
+
+ public String getChangingMatcherName() {
+ return changingMatcherName;
+ }
+
+ public CacheMetadataOptions setChangingMatcherName(String changingMatcherName) {
+ this.changingMatcherName = changingMatcherName;
+ return this;
+ }
+
+ public String getChangingPattern() {
+ return changingPattern;
+ }
+
+ public CacheMetadataOptions setChangingPattern(String changingPattern) {
+ this.changingPattern = changingPattern;
+ return this;
+ }
+
+ public CacheMetadataOptions setCheckTTL(boolean checkTTL) {
+ this.checkTTL = checkTTL;
+ return this;
+ }
+
+ public boolean isCheckTTL() {
+ return checkTTL;
+ }
+}
diff --git a/src/java/org/apache/ivy/core/cache/CacheResourceOptions.java b/src/java/org/apache/ivy/core/cache/CacheResourceOptions.java
new file mode 100644
index 0000000..9e8ef9f
--- /dev/null
+++ b/src/java/org/apache/ivy/core/cache/CacheResourceOptions.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.cache;
+
+public class CacheResourceOptions extends CacheDownloadOptions {
+
+ // by default, a ttl of 1 hour
+ private long ttl = 1000 * 60 * 60;
+
+ public void setTtl(long ttl) {
+ this.ttl = ttl;
+ }
+
+ public long getTtl() {
+ return ttl;
+ }
+}
diff --git a/src/java/org/apache/ivy/core/cache/CacheUtil.java b/src/java/org/apache/ivy/core/cache/CacheUtil.java
new file mode 100644
index 0000000..71b2c14
--- /dev/null
+++ b/src/java/org/apache/ivy/core/cache/CacheUtil.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.cache;
+
+/**
+ * Utility class providing some cache related facilities.
+ *
+ */
+public final class CacheUtil {
+
+ /**
+ * Checks that the given pattern is acceptable as a cache pattern
+ *
+ * @param cachePattern
+ * the pattern to check
+ * @throws IllegalArgumentException
+ * if the pattern isn't acceptable as cache pattern
+ */
+ public static void checkCachePattern(String cachePattern) {
+ if (cachePattern == null) {
+ throw new IllegalArgumentException("null cache pattern not allowed.");
+ }
+ if (cachePattern.startsWith("..")) {
+ throw new IllegalArgumentException("invalid cache pattern: '" + cachePattern
+ + "': cache patterns must not lead outside cache directory");
+ }
+ if (cachePattern.startsWith("/")) {
+ throw new IllegalArgumentException("invalid cache pattern: '" + cachePattern
+ + "': cache patterns must not be absolute");
+ }
+ }
+
+ private CacheUtil() {
+ }
+}
diff --git a/src/java/org/apache/ivy/core/cache/DefaultRepositoryCacheManager.java b/src/java/org/apache/ivy/core/cache/DefaultRepositoryCacheManager.java
new file mode 100644
index 0000000..55f498d
--- /dev/null
+++ b/src/java/org/apache/ivy/core/cache/DefaultRepositoryCacheManager.java
@@ -0,0 +1,1543 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.cache;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.text.ParseException;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+import org.apache.ivy.Ivy;
+import org.apache.ivy.core.IvyPatternHelper;
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.DefaultArtifact;
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ArtifactRevisionId;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.module.id.ModuleRules;
+import org.apache.ivy.core.pack.PackagingManager;
+import org.apache.ivy.core.report.ArtifactDownloadReport;
+import org.apache.ivy.core.report.DownloadStatus;
+import org.apache.ivy.core.report.MetadataArtifactDownloadReport;
+import org.apache.ivy.core.resolve.ResolvedModuleRevision;
+import org.apache.ivy.core.settings.IvySettings;
+import org.apache.ivy.plugins.IvySettingsAware;
+import org.apache.ivy.plugins.lock.LockStrategy;
+import org.apache.ivy.plugins.matcher.ExactPatternMatcher;
+import org.apache.ivy.plugins.matcher.MapMatcher;
+import org.apache.ivy.plugins.matcher.Matcher;
+import org.apache.ivy.plugins.matcher.NoMatcher;
+import org.apache.ivy.plugins.matcher.PatternMatcher;
+import org.apache.ivy.plugins.namespace.NameSpaceHelper;
+import org.apache.ivy.plugins.parser.ModuleDescriptorParser;
+import org.apache.ivy.plugins.parser.ModuleDescriptorParserRegistry;
+import org.apache.ivy.plugins.parser.ParserSettings;
+import org.apache.ivy.plugins.parser.xml.XmlModuleDescriptorParser;
+import org.apache.ivy.plugins.repository.ArtifactResourceResolver;
+import org.apache.ivy.plugins.repository.LocalizableResource;
+import org.apache.ivy.plugins.repository.Repository;
+import org.apache.ivy.plugins.repository.Resource;
+import org.apache.ivy.plugins.repository.ResourceDownloader;
+import org.apache.ivy.plugins.repository.ResourceHelper;
+import org.apache.ivy.plugins.resolver.AbstractResolver;
+import org.apache.ivy.plugins.resolver.DependencyResolver;
+import org.apache.ivy.plugins.resolver.util.ResolvedResource;
+import org.apache.ivy.util.Checks;
+import org.apache.ivy.util.FileUtil;
+import org.apache.ivy.util.HexEncoder;
+import org.apache.ivy.util.Message;
+import org.apache.ivy.util.PropertiesFile;
+
+public class DefaultRepositoryCacheManager implements RepositoryCacheManager, IvySettingsAware {
+ private static final String DEFAULT_ARTIFACT_PATTERN = "[organisation]/[module](/[branch])/[type]s/[artifact]-[revision](-[classifier])(.[ext])";
+
+ private static final String DEFAULT_DATA_FILE_PATTERN = "[organisation]/[module](/[branch])/ivydata-[revision].properties";
+
+ private static final String DEFAULT_IVY_PATTERN = "[organisation]/[module](/[branch])/ivy-[revision].xml";
+
+ private static final int DEFAULT_MEMORY_CACHE_SIZE = 150;
+
+ private static MessageDigest SHA_DIGEST;
+ static {
+ try {
+ SHA_DIGEST = MessageDigest.getInstance("SHA1");
+ } catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException("The SHA1 algorithm is not available in your classpath", e);
+ }
+ }
+
+ private IvySettings settings;
+
+ private File basedir;
+
+ private LockStrategy lockStrategy;
+
+ private String name;
+
+ private String ivyPattern;
+
+ private String dataFilePattern = DEFAULT_DATA_FILE_PATTERN;
+
+ private String artifactPattern;
+
+ private String lockStrategyName;
+
+ private String changingPattern;
+
+ private String changingMatcherName = PatternMatcher.EXACT_OR_REGEXP;
+
+ private Boolean checkmodified;
+
+ private Boolean useOrigin;
+
+ private ModuleRules/* <Long> */ttlRules = new ModuleRules();
+
+ private Long defaultTTL = null;
+
+ private ModuleDescriptorMemoryCache memoryModuleDescrCache;
+
+ private PackagingManager packagingManager = new PackagingManager();
+
+ public DefaultRepositoryCacheManager() {
+ }
+
+ public DefaultRepositoryCacheManager(String name, IvySettings settings, File basedir) {
+ setName(name);
+ setSettings(settings);
+ setBasedir(basedir);
+ }
+
+ public IvySettings getSettings() {
+ return settings;
+ }
+
+ public void setSettings(IvySettings settings) {
+ this.settings = settings;
+ packagingManager.setSettings(settings);
+ }
+
+ public File getIvyFileInCache(ModuleRevisionId mrid) {
+ String file = IvyPatternHelper.substitute(getIvyPattern(),
+ DefaultArtifact.newIvyArtifact(mrid, null));
+ return new File(getRepositoryCacheRoot(), file);
+ }
+
+ public String getIvyPattern() {
+ if (ivyPattern == null) {
+ if (settings != null) {
+ ivyPattern = settings.getDefaultCacheIvyPattern();
+ }
+ if (ivyPattern == null) {
+ ivyPattern = DEFAULT_IVY_PATTERN;
+ }
+ }
+ return ivyPattern;
+ }
+
+ public String getArtifactPattern() {
+ if (artifactPattern == null) {
+ if (settings != null) {
+ artifactPattern = settings.getDefaultCacheArtifactPattern();
+ }
+ if (artifactPattern == null) {
+ artifactPattern = DEFAULT_ARTIFACT_PATTERN;
+ }
+ }
+ return artifactPattern;
+ }
+
+ public void setArtifactPattern(String artifactPattern) {
+ CacheUtil.checkCachePattern(artifactPattern);
+ this.artifactPattern = artifactPattern;
+ }
+
+ public File getBasedir() {
+ if (basedir == null) {
+ basedir = settings.getDefaultRepositoryCacheBasedir();
+ }
+ return basedir;
+ }
+
+ public void setBasedir(File cache) {
+ this.basedir = cache;
+ }
+
+ public long getDefaultTTL() {
+ if (defaultTTL == null) {
+ defaultTTL = new Long(parseDuration(settings.getVariable("ivy.cache.ttl.default")));
+ }
+ return defaultTTL.longValue();
+ }
+
+ public void setDefaultTTL(long defaultTTL) {
+ this.defaultTTL = new Long(defaultTTL);
+ }
+
+ public void setDefaultTTL(String defaultTTL) {
+ this.defaultTTL = new Long(parseDuration(defaultTTL));
+ }
+
+ public String getDataFilePattern() {
+ return dataFilePattern;
+ }
+
+ public void setDataFilePattern(String dataFilePattern) {
+ CacheUtil.checkCachePattern(dataFilePattern);
+ this.dataFilePattern = dataFilePattern;
+ }
+
+ public void setIvyPattern(String ivyPattern) {
+ CacheUtil.checkCachePattern(ivyPattern);
+ this.ivyPattern = ivyPattern;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getChangingMatcherName() {
+ return changingMatcherName;
+ }
+
+ public void setChangingMatcher(String changingMatcherName) {
+ this.changingMatcherName = changingMatcherName;
+ }
+
+ public String getChangingPattern() {
+ return changingPattern;
+ }
+
+ public void setChangingPattern(String changingPattern) {
+ this.changingPattern = changingPattern;
+ }
+
+ public void addTTL(Map attributes, PatternMatcher matcher, long duration) {
+ ttlRules.defineRule(new MapMatcher(attributes, matcher), new Long(duration));
+ }
+
+ public void addConfiguredTtl(Map/* <String,String> */attributes) {
+ String duration = (String) attributes.remove("duration");
+ if (duration == null) {
+ throw new IllegalArgumentException("'duration' attribute is mandatory for ttl");
+ }
+ String matcher = (String) attributes.remove("matcher");
+ addTTL(attributes,
+ matcher == null ? ExactPatternMatcher.INSTANCE : settings.getMatcher(matcher),
+ parseDuration(duration));
+ }
+
+ public void setMemorySize(int size) {
+ memoryModuleDescrCache = new ModuleDescriptorMemoryCache(size);
+ }
+
+ public ModuleDescriptorMemoryCache getMemoryCache() {
+ if (memoryModuleDescrCache == null) {
+ memoryModuleDescrCache = new ModuleDescriptorMemoryCache(DEFAULT_MEMORY_CACHE_SIZE);
+ }
+ return memoryModuleDescrCache;
+ }
+
+ private static final Pattern DURATION_PATTERN = Pattern
+ .compile("(?:(\\d+)d)? ?(?:(\\d+)h)? ?(?:(\\d+)m)? ?(?:(\\d+)s)? ?(?:(\\d+)ms)?");
+
+ private static final int MILLIS_IN_SECONDS = 1000;
+
+ private static final int MILLIS_IN_MINUTES = 60 * MILLIS_IN_SECONDS;
+
+ private static final int MILLIS_IN_HOUR = 60 * MILLIS_IN_MINUTES;
+
+ private static final int MILLIS_IN_DAY = 24 * MILLIS_IN_HOUR;
+
+ private long parseDuration(String duration) {
+ if (duration == null) {
+ return 0;
+ }
+ if ("eternal".equals(duration)) {
+ return Long.MAX_VALUE;
+ }
+ java.util.regex.Matcher m = DURATION_PATTERN.matcher(duration);
+ if (m.matches()) {
+ // CheckStyle:MagicNumber| OFF
+ int days = getGroupIntValue(m, 1);
+ int hours = getGroupIntValue(m, 2);
+ int minutes = getGroupIntValue(m, 3);
+ int seconds = getGroupIntValue(m, 4);
+ int millis = getGroupIntValue(m, 5);
+ // CheckStyle:MagicNumber| ON
+
+ return days * MILLIS_IN_DAY + hours * MILLIS_IN_HOUR + minutes * MILLIS_IN_MINUTES
+ + seconds * MILLIS_IN_SECONDS + millis;
+ } else {
+ throw new IllegalArgumentException("invalid duration '" + duration
+ + "': it must match " + DURATION_PATTERN.pattern() + " or 'eternal'");
+ }
+ }
+
+ private int getGroupIntValue(java.util.regex.Matcher m, int groupNumber) {
+ String g = m.group(groupNumber);
+ return g == null || g.length() == 0 ? 0 : Integer.parseInt(g);
+ }
+
+ /**
+ * True if this cache should check lastmodified date to know if ivy files are up to date.
+ *
+ * @return
+ */
+ public boolean isCheckmodified() {
+ if (checkmodified == null) {
+ if (getSettings() != null) {
+ String check = getSettings().getVariable("ivy.resolver.default.check.modified");
+ return check != null ? Boolean.valueOf(check).booleanValue() : false;
+ } else {
+ return false;
+ }
+ } else {
+ return checkmodified.booleanValue();
+ }
+ }
+
+ public void setCheckmodified(boolean check) {
+ checkmodified = Boolean.valueOf(check);
+ }
+
+ /**
+ * True if this cache should use artifacts original location when possible, false if they should
+ * be copied to cache.
+ */
+ public boolean isUseOrigin() {
+ if (useOrigin == null) {
+ if (getSettings() != null) {
+ return getSettings().isDefaultUseOrigin();
+ } else {
+ return false;
+ }
+ } else {
+ return useOrigin.booleanValue();
+ }
+ }
+
+ public void setUseOrigin(boolean b) {
+ useOrigin = Boolean.valueOf(b);
+ }
+
+ /**
+ * Returns a File object pointing to where the artifact can be found on the local file system.
+ * This is usually in the cache, but it can be directly in the repository if it is local and if
+ * the resolve has been done with useOrigin = true
+ */
+ public File getArchiveFileInCache(Artifact artifact) {
+ ArtifactOrigin origin = getSavedArtifactOrigin(artifact);
+ return getArchiveFileInCache(artifact, origin);
+ }
+
+ /**
+ * Returns a File object pointing to where the artifact can be found on the local file system.
+ * This is usually in the cache, but it can be directly in the repository if it is local and if
+ * the resolve has been done with useOrigin = true
+ */
+ public File getArchiveFileInCache(Artifact artifact, ArtifactOrigin origin) {
+ File archive = new File(getRepositoryCacheRoot(), getArchivePathInCache(artifact, origin));
+ if (!archive.exists() && !ArtifactOrigin.isUnknown(origin) && origin.isLocal()) {
+ File original = Checks.checkAbsolute(origin.getLocation(), artifact
+ + " origin location");
+ if (original.exists()) {
+ return original;
+ }
+ }
+ return archive;
+ }
+
+ /**
+ * Returns a File object pointing to where the artifact can be found on the local file system,
+ * using or not the original location depending on the availability of origin information
+ * provided as parameter and the setting of useOrigin. If useOrigin is false, this method will
+ * always return the file in the cache.
+ */
+ private File getArchiveFileInCache(Artifact artifact, ArtifactOrigin origin, boolean useOrigin) {
+ if (useOrigin && !ArtifactOrigin.isUnknown(origin) && origin.isLocal()) {
+ return Checks.checkAbsolute(origin.getLocation(), artifact + " origin location");
+ } else {
+ return new File(getRepositoryCacheRoot(), getArchivePathInCache(artifact, origin));
+ }
+ }
+
+ public String getArchivePathInCache(Artifact artifact) {
+ return IvyPatternHelper.substitute(getArtifactPattern(), artifact);
+ }
+
+ public String getArchivePathInCache(Artifact artifact, ArtifactOrigin origin) {
+ if (isOriginalMetadataArtifact(artifact)) {
+ return IvyPatternHelper.substitute(getIvyPattern() + ".original", artifact, origin);
+ } else {
+ return IvyPatternHelper.substitute(getArtifactPattern(), artifact, origin);
+ }
+ }
+
+ /**
+ * Saves the information of which resolver was used to resolve a md, so that this info can be
+ * retrieve later (even after a jvm restart) by getSavedResolverName(ModuleDescriptor md)
+ *
+ * @param md
+ * the module descriptor resolved
+ * @param name
+ * resolver name
+ */
+ private void saveResolver(ModuleDescriptor md, String name) {
+ // should always be called with a lock on module metadata artifact
+ PropertiesFile cdf = getCachedDataFile(md);
+ cdf.setProperty("resolver", name);
+ cdf.save();
+ }
+
+ /**
+ * Saves the information of which resolver was used to resolve a md, so that this info can be
+ * retrieve later (even after a jvm restart) by getSavedArtResolverName(ModuleDescriptor md)
+ *
+ * @param md
+ * the module descriptor resolved
+ * @param name
+ * artifact resolver name
+ */
+ public void saveResolvers(ModuleDescriptor md, String metadataResolverName,
+ String artifactResolverName) {
+ ModuleRevisionId mrid = md.getResolvedModuleRevisionId();
+ if (!lockMetadataArtifact(mrid)) {
+ Message.error("impossible to acquire lock for " + mrid);
+ return;
+ }
+ try {
+ PropertiesFile cdf = getCachedDataFile(md);
+ cdf.setProperty("resolver", metadataResolverName);
+ cdf.setProperty("artifact.resolver", artifactResolverName);
+ cdf.save();
+ } finally {
+ unlockMetadataArtifact(mrid);
+ }
+ }
+
+ private String getSavedResolverName(ModuleDescriptor md) {
+ // should always be called with a lock on module metadata artifact
+ PropertiesFile cdf = getCachedDataFile(md);
+ return cdf.getProperty("resolver");
+ }
+
+ private String getSavedArtResolverName(ModuleDescriptor md) {
+ // should always be called with a lock on module metadata artifact
+ PropertiesFile cdf = getCachedDataFile(md);
+ return cdf.getProperty("artifact.resolver");
+ }
+
+ void saveArtifactOrigin(Artifact artifact, ArtifactOrigin origin) {
+ // should always be called with a lock on module metadata artifact
+ PropertiesFile cdf = getCachedDataFile(artifact.getModuleRevisionId());
+ cdf.setProperty(getIsLocalKey(artifact), String.valueOf(origin.isLocal()));
+ cdf.setProperty(getLocationKey(artifact), origin.getLocation());
+ cdf.setProperty(getOriginalKey(artifact), getPrefixKey(origin.getArtifact()));
+ if (origin.getLastChecked() != null) {
+ cdf.setProperty(getLastCheckedKey(artifact), origin.getLastChecked().toString());
+ }
+ cdf.setProperty(getExistsKey(artifact), Boolean.toString(origin.isExists()));
+ cdf.save();
+ }
+
+ private void removeSavedArtifactOrigin(Artifact artifact) {
+ // should always be called with a lock on module metadata artifact
+ PropertiesFile cdf = getCachedDataFile(artifact.getModuleRevisionId());
+ cdf.remove(getLocationKey(artifact));
+ cdf.remove(getIsLocalKey(artifact));
+ cdf.remove(getLastCheckedKey(artifact));
+ cdf.remove(getOriginalKey(artifact));
+ cdf.save();
+ }
+
+ private static final Pattern ARTIFACT_KEY_PATTERN = Pattern
+ .compile(".*:(.*)#(.*)#(.*)#(.*)(\\.location)?");
+
+ public ArtifactOrigin getSavedArtifactOrigin(Artifact artifact) {
+ ModuleRevisionId mrid = artifact.getModuleRevisionId();
+ if (!lockMetadataArtifact(mrid)) {
+ Message.error("impossible to acquire lock for " + mrid);
+ return ArtifactOrigin.unkwnown(artifact);
+ }
+ try {
+ PropertiesFile cdf = getCachedDataFile(artifact.getModuleRevisionId());
+ String location = cdf.getProperty(getLocationKey(artifact));
+ String local = cdf.getProperty(getIsLocalKey(artifact));
+ String lastChecked = cdf.getProperty(getLastCheckedKey(artifact));
+ String exists = cdf.getProperty(getExistsKey(artifact));
+ String original = cdf.getProperty(getOriginalKey(artifact));
+
+ boolean isLocal = Boolean.valueOf(local).booleanValue();
+
+ if (location == null) {
+ // origin has not been specified, return null
+ return ArtifactOrigin.unkwnown(artifact);
+ }
+
+ if (original != null) {
+ // original artifact key artifact:[name]#[type]#[ext]#[hashcode]
+ java.util.regex.Matcher m = ARTIFACT_KEY_PATTERN.matcher(original);
+ if (m.matches()) {
+ String origName = m.group(1);
+ String origType = m.group(2);
+ String origExt = m.group(3);
+
+ ArtifactRevisionId originArtifactId = ArtifactRevisionId.newInstance(
+ artifact.getModuleRevisionId(), origName, origType, origExt);
+ // second check: verify the hashcode of the cached artifact
+ if (m.group(4).equals("" + originArtifactId.hashCode())) {
+ try {
+ artifact = new DefaultArtifact(originArtifactId,
+ artifact.getPublicationDate(), new URL(location), true);
+ } catch (MalformedURLException e) {
+ Message.debug(e);
+ }
+ }
+ }
+ } else {
+ // Fallback if cached with old version:
+
+ // if the origin artifact has another extension (e.g. .pom) then make a synthetic
+ // origin artifact for it
+ if (!location.endsWith("." + artifact.getExt())) {
+ // try to find other cached artifact info with same location. This must be the
+ // origin. We must parse the key as we do not know for sure what the original
+ // artifact is named.
+ Iterator it = cdf.entrySet().iterator();
+ String ownLocationKey = getLocationKey(artifact);
+ while (it.hasNext()) {
+ Map.Entry entry = (Map.Entry) it.next();
+ if (entry.getValue().equals(location)
+ && !ownLocationKey.equals(entry.getKey())) {
+ // found a match, key is
+ // artifact:[name]#[type]#[ext]#[hashcode].location
+ java.util.regex.Matcher m = ARTIFACT_KEY_PATTERN.matcher((String) entry
+ .getKey());
+ if (m.matches()) {
+ String origName = m.group(1);
+ String origType = m.group(2);
+ String origExt = m.group(3);
+
+ // first check: the type should end in .original
+ if (!origType.endsWith(".original")) {
+ continue;
+ }
+
+ ArtifactRevisionId originArtifactId = ArtifactRevisionId
+ .newInstance(artifact.getModuleRevisionId(), origName,
+ origType, origExt);
+ // second check: verify the hashcode of the cached artifact
+ if (m.group(4).equals("" + originArtifactId.hashCode())) {
+ try {
+ artifact = new DefaultArtifact(originArtifactId,
+ artifact.getPublicationDate(), new URL(location),
+ true);
+ } catch (MalformedURLException e) {
+ Message.debug(e);
+ }
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ ArtifactOrigin origin = new ArtifactOrigin(artifact, isLocal, location);
+ if (lastChecked != null) {
+ origin.setLastChecked(Long.valueOf(lastChecked));
+ }
+ if (exists != null) {
+ origin.setExist(Boolean.valueOf(exists).booleanValue());
+ }
+
+ return origin;
+ } finally {
+ unlockMetadataArtifact(mrid);
+ }
+ }
+
+ /**
+ * Creates the unique prefix key that will reference the artifact within the properties.
+ *
+ * @param artifact
+ * the artifact to create the unique key from. Cannot be null.
+ * @return the unique prefix key as a string.
+ */
+ private String getPrefixKey(Artifact artifact) {
+ // use the hashcode as a uuid for the artifact (fingers crossed)
+ int hashCode = artifact.getId().hashCode();
+ // use just some visual cue
+ return "artifact:" + artifact.getName() + "#" + artifact.getType() + "#"
+ + artifact.getExt() + "#" + hashCode;
+ }
+
+ /**
+ * Returns the key used to identify the location of the artifact.
+ *
+ * @param artifact
+ * the artifact to generate the key from. Cannot be null.
+ * @return the key to be used to reference the artifact location.
+ */
+ private String getLocationKey(Artifact artifact) {
+ String prefix = getPrefixKey(artifact);
+ return prefix + ".location";
+ }
+
+ /**
+ * Returns the key used to identify if the artifact is local.
+ *
+ * @param artifact
+ * the artifact to generate the key from. Cannot be null.
+ * @return the key to be used to reference the artifact locality.
+ */
+ private String getIsLocalKey(Artifact artifact) {
+ String prefix = getPrefixKey(artifact);
+ return prefix + ".is-local";
+ }
+
+ /**
+ * Returns the key used to identify the last time the artifact was checked to be up to date.
+ *
+ * @param artifact
+ * the artifact to generate the key from. Cannot be null.
+ * @return the key to be used to reference the artifact's last check date.
+ */
+ private String getLastCheckedKey(Artifact artifact) {
+ String prefix = getPrefixKey(artifact);
+ return prefix + ".lastchecked";
+ }
+
+ /**
+ * Returns the key used to identify the existence of the remote artifact.
+ *
+ * @param artifact
+ * the artifact to generate the key from. Cannot be null.
+ * @return the key to be used to reference the existence of the artifact.
+ */
+ private String getExistsKey(Artifact artifact) {
+ String prefix = getPrefixKey(artifact);
+ return prefix + ".exists";
+ }
+
+ /**
+ * Returns the key used to identify the original artifact.
+ *
+ * @param artifact
+ * the artifact to generate the key from. Cannot be null.
+ * @return the key to be used to reference the original artifact.
+ */
+ private String getOriginalKey(Artifact artifact) {
+ String prefix = getPrefixKey(artifact);
+ return prefix + ".original";
+ }
+
+ private PropertiesFile getCachedDataFile(ModuleDescriptor md) {
+ return getCachedDataFile(md.getResolvedModuleRevisionId());
+ }
+
+ private PropertiesFile getCachedDataFile(ModuleRevisionId mRevId) {
+ return new PropertiesFile(new File(getRepositoryCacheRoot(), IvyPatternHelper.substitute(
+ getDataFilePattern(), mRevId)), "ivy cached data file for " + mRevId);
+ }
+
+ public ResolvedModuleRevision findModuleInCache(DependencyDescriptor dd,
+ ModuleRevisionId requestedRevisionId, CacheMetadataOptions options,
+ String expectedResolver) {
+ ModuleRevisionId mrid = requestedRevisionId;
+ if (isCheckmodified(dd, requestedRevisionId, options)) {
+ Message.verbose("don't use cache for " + mrid + ": checkModified=true");
+ return null;
+ }
+ if (isChanging(dd, requestedRevisionId, options)) {
+ Message.verbose("don't use cache for " + mrid + ": changing=true");
+ return null;
+ }
+ return doFindModuleInCache(mrid, options, expectedResolver);
+ }
+
+ private ResolvedModuleRevision doFindModuleInCache(ModuleRevisionId mrid,
+ CacheMetadataOptions options, String expectedResolver) {
+ if (!lockMetadataArtifact(mrid)) {
+ Message.error("impossible to acquire lock for " + mrid);
+ return null;
+ }
+
+ boolean unlock = true;
+
+ try {
+ if (settings.getVersionMatcher().isDynamic(mrid)) {
+ String resolvedRevision = getResolvedRevision(mrid, options);
+ if (resolvedRevision != null) {
+ Message.verbose("found resolved revision in cache: " + mrid + " => "
+ + resolvedRevision);
+
+ // we have found another module in the cache, make sure we unlock
+ // the original module
+ unlockMetadataArtifact(mrid);
+ mrid = ModuleRevisionId.newInstance(mrid, resolvedRevision);
+
+ // don't forget to request a lock on the new module!
+ if (!lockMetadataArtifact(mrid)) {
+ Message.error("impossible to acquire lock for " + mrid);
+
+ // we couldn't lock the new module, so no need to unlock it
+ unlock = false;
+ return null;
+ }
+ } else {
+ return null;
+ }
+ }
+
+ File ivyFile = getIvyFileInCache(mrid);
+ if (ivyFile.exists()) {
+ // found in cache !
+ try {
+ ModuleDescriptorParser parser = getModuleDescriptorParser(ivyFile);
+ ModuleDescriptor depMD = getMdFromCache(parser, options, ivyFile);
+ String resolverName = getSavedResolverName(depMD);
+ String artResolverName = getSavedArtResolverName(depMD);
+ DependencyResolver resolver = settings.getResolver(resolverName);
+ if (resolver == null) {
+ Message.debug("\tresolver not found: " + resolverName
+ + " => trying to use the one configured for " + mrid);
+ resolver = settings.getResolver(depMD.getResolvedModuleRevisionId());
+ if (resolver != null) {
+ Message.debug("\tconfigured resolver found for "
+ + depMD.getResolvedModuleRevisionId() + ": "
+ + resolver.getName() + ": saving this data");
+ saveResolver(depMD, resolver.getName());
+ }
+ }
+ DependencyResolver artResolver = settings.getResolver(artResolverName);
+ if (artResolver == null) {
+ artResolver = resolver;
+ }
+ if (resolver != null) {
+ Message.debug("\tfound ivy file in cache for " + mrid + " (resolved by "
+ + resolver.getName() + "): " + ivyFile);
+ if (expectedResolver == null || expectedResolver.equals(resolver.getName())) {
+ MetadataArtifactDownloadReport madr = new MetadataArtifactDownloadReport(
+ depMD.getMetadataArtifact());
+ madr.setDownloadStatus(DownloadStatus.NO);
+ madr.setSearched(false);
+ madr.setLocalFile(ivyFile);
+ madr.setSize(ivyFile.length());
+ madr.setArtifactOrigin(getSavedArtifactOrigin(depMD
+ .getMetadataArtifact()));
+ if (madr.getArtifactOrigin().isExists()) {
+ if (madr.getArtifactOrigin().isLocal()
+ && madr.getArtifactOrigin().getArtifact().getUrl() != null) {
+ madr.setOriginalLocalFile(new File(madr.getArtifactOrigin()
+ .getArtifact().getUrl().toURI()));
+ } else {
+ // find locally cached file
+ madr.setOriginalLocalFile(getArchiveFileInCache(madr
+ .getArtifactOrigin().getArtifact()));
+ }
+ }
+ return new ResolvedModuleRevision(resolver, artResolver, depMD, madr);
+ } else {
+ Message.debug("found module in cache but with a different resolver: "
+ + "discarding: " + mrid + "; expected resolver="
+ + expectedResolver + "; resolver=" + resolver.getName());
+ }
+ } else {
+ Message.debug("\tresolver not found: " + resolverName
+ + " => cannot use cached ivy file for " + mrid);
+ }
+ } catch (Exception e) {
+ // will try with resolver
+ Message.debug("\tproblem while parsing cached ivy file for: " + mrid, e);
+ }
+ } else {
+ Message.debug("\tno ivy file in cache for " + mrid + ": tried " + ivyFile);
+ }
+ } finally {
+ if (unlock) {
+ unlockMetadataArtifact(mrid);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Choose write module descriptor parser for a given moduleDescriptor
+ *
+ * @param moduleDescriptorFile
+ * a given module descriptor
+ * @return
+ */
+ protected ModuleDescriptorParser getModuleDescriptorParser(File moduleDescriptorFile) {
+ return XmlModuleDescriptorParser.getInstance();
+ }
+
+ private class MyModuleDescriptorProvider implements ModuleDescriptorProvider {
+
+ private final ModuleDescriptorParser mdParser;
+
+ private final ParserSettings settings;
+
+ public MyModuleDescriptorProvider(ModuleDescriptorParser mdParser, ParserSettings settings) {
+ this.mdParser = mdParser;
+ this.settings = settings;
+ }
+
+ public ModuleDescriptor provideModule(ParserSettings ivySettings, File descriptorURL,
+ boolean validate) throws ParseException, IOException {
+ return mdParser.parseDescriptor(settings, descriptorURL.toURI().toURL(), validate);
+ }
+ }
+
+ private ModuleDescriptor getMdFromCache(ModuleDescriptorParser mdParser,
+ CacheMetadataOptions options, File ivyFile) throws ParseException, IOException {
+ ModuleDescriptorMemoryCache cache = getMemoryCache();
+ ModuleDescriptorProvider mdProvider = new MyModuleDescriptorProvider(mdParser, settings);
+ return cache.get(ivyFile, settings, options.isValidate(), mdProvider);
+ }
+
+ private ModuleDescriptor getStaledMd(ModuleDescriptorParser mdParser,
+ CacheMetadataOptions options, File ivyFile, ParserSettings parserSettings)
+ throws ParseException, IOException {
+ ModuleDescriptorMemoryCache cache = getMemoryCache();
+ ModuleDescriptorProvider mdProvider = new MyModuleDescriptorProvider(mdParser,
+ parserSettings);
+ return cache.getStale(ivyFile, settings, options.isValidate(), mdProvider);
+ }
+
+ private String getResolvedRevision(ModuleRevisionId mrid, CacheMetadataOptions options) {
+ if (!lockMetadataArtifact(mrid)) {
+ Message.error("impossible to acquire lock for " + mrid);
+ return null;
+ }
+ try {
+ String resolvedRevision = null;
+ if (options.isForce()) {
+ Message.verbose("refresh mode: no check for cached resolved revision for " + mrid);
+ return null;
+ }
+ PropertiesFile cachedResolvedRevision = getCachedDataFile(mrid);
+ resolvedRevision = cachedResolvedRevision.getProperty("resolved.revision");
+ if (resolvedRevision == null) {
+ Message.verbose(getName() + ": no cached resolved revision for " + mrid);
+ return null;
+ }
+
+ String resolvedTime = cachedResolvedRevision.getProperty("resolved.time");
+ if (resolvedTime == null) {
+ Message.verbose(getName()
+ + ": inconsistent or old cache: no cached resolved time for " + mrid);
+ saveResolvedRevision(mrid, resolvedRevision);
+ return resolvedRevision;
+ }
+ if (options.isCheckTTL()) {
+ long expiration = Long.parseLong(resolvedTime) + getTTL(mrid);
+ if (expiration > 0 // negative expiration means that Long.MAX_VALUE has been
+ // exceeded
+ && System.currentTimeMillis() > expiration) {
+ Message.verbose(getName() + ": cached resolved revision expired for " + mrid);
+ return null;
+ }
+ }
+ return resolvedRevision;
+ } finally {
+ unlockMetadataArtifact(mrid);
+ }
+ }
+
+ public void saveResolvedRevision(ModuleRevisionId mrid, String revision) {
+ if (!lockMetadataArtifact(mrid)) {
+ Message.error("impossible to acquire lock for " + mrid);
+ return;
+ }
+ try {
+ PropertiesFile cachedResolvedRevision = getCachedDataFile(mrid);
+ cachedResolvedRevision.setProperty("resolved.time",
+ String.valueOf(System.currentTimeMillis()));
+ cachedResolvedRevision.setProperty("resolved.revision", revision);
+ cachedResolvedRevision.save();
+ } finally {
+ unlockMetadataArtifact(mrid);
+ }
+ }
+
+ public long getTTL(ModuleRevisionId mrid) {
+ Long ttl = (Long) ttlRules.getRule(mrid);
+ return ttl == null ? getDefaultTTL() : ttl.longValue();
+ }
+
+ public String toString() {
+ return name;
+ }
+
+ public File getRepositoryCacheRoot() {
+ return getBasedir();
+ }
+
+ public LockStrategy getLockStrategy() {
+ if (lockStrategy == null) {
+ if (lockStrategyName != null) {
+ lockStrategy = settings.getLockStrategy(lockStrategyName);
+ } else {
+ lockStrategy = settings.getDefaultLockStrategy();
+ }
+ }
+ return lockStrategy;
+ }
+
+ public void setLockStrategy(LockStrategy lockStrategy) {
+ this.lockStrategy = lockStrategy;
+ }
+
+ public void setLockStrategy(String lockStrategyName) {
+ this.lockStrategyName = lockStrategyName;
+ }
+
+ public ArtifactDownloadReport download(Artifact artifact,
+ ArtifactResourceResolver resourceResolver, ResourceDownloader resourceDownloader,
+ CacheDownloadOptions options) {
+ final ArtifactDownloadReport adr = new ArtifactDownloadReport(artifact);
+ boolean useOrigin = isUseOrigin();
+
+ // TODO: see if we could lock on the artifact to download only, instead of the module
+ // metadata artifact. We'd need to store artifact origin and is local in artifact specific
+ // file to do so, or lock the metadata artifact only to update artifact origin, which would
+ // mean acquiring nested locks, which can be a dangerous thing
+ ModuleRevisionId mrid = artifact.getModuleRevisionId();
+ if (!lockMetadataArtifact(mrid)) {
+ adr.setDownloadStatus(DownloadStatus.FAILED);
+ adr.setDownloadDetails("impossible to get lock for " + mrid);
+ return adr;
+ }
+ try {
+ DownloadListener listener = options.getListener();
+ if (listener != null) {
+ listener.needArtifact(this, artifact);
+ }
+ ArtifactOrigin origin = getSavedArtifactOrigin(artifact);
+ // if we can use origin file, we just ask ivy for the file in cache, and it will
+ // return the original one if possible. If we are not in useOrigin mode, we use the
+ // getArchivePath method which always return a path in the actual cache
+ File archiveFile = getArchiveFileInCache(artifact, origin, useOrigin);
+
+ if (archiveFile.exists() && !options.isForce()) {
+ adr.setDownloadStatus(DownloadStatus.NO);
+ adr.setSize(archiveFile.length());
+ adr.setArtifactOrigin(origin);
+ adr.setLocalFile(archiveFile);
+ } else {
+ long start = System.currentTimeMillis();
+ try {
+ ResolvedResource artifactRef = resourceResolver.resolve(artifact);
+ if (artifactRef != null) {
+ Resource artifactRes = artifactRef.getResource();
+ origin = new ArtifactOrigin(artifact, artifactRes.isLocal(),
+ artifactRes.getName());
+ if (useOrigin && artifactRes.isLocal()) {
+ if (artifactRes instanceof LocalizableResource) {
+ origin.setLocation(((LocalizableResource) artifactRes).getFile()
+ .getAbsolutePath());
+ }
+ saveArtifactOrigin(artifact, origin);
+ archiveFile = getArchiveFileInCache(artifact, origin);
+ adr.setDownloadStatus(DownloadStatus.NO);
+ adr.setSize(archiveFile.length());
+ adr.setArtifactOrigin(origin);
+ adr.setLocalFile(archiveFile);
+ } else {
+ // refresh archive file now that we better now its origin
+ archiveFile = getArchiveFileInCache(artifact, origin, useOrigin);
+ if (ResourceHelper.equals(artifactRes, archiveFile)) {
+ throw new IllegalStateException("invalid settings for '"
+ + resourceResolver
+ + "': pointing repository to ivy cache is forbidden !");
+ }
+ if (listener != null) {
+ listener.startArtifactDownload(this, artifactRef, artifact, origin);
+ }
+
+ resourceDownloader.download(artifact, artifactRes, archiveFile);
+ adr.setSize(archiveFile.length());
+ saveArtifactOrigin(artifact, origin);
+ adr.setDownloadTimeMillis(System.currentTimeMillis() - start);
+ adr.setDownloadStatus(DownloadStatus.SUCCESSFUL);
+ adr.setArtifactOrigin(origin);
+ adr.setLocalFile(archiveFile);
+ }
+ } else {
+ adr.setDownloadStatus(DownloadStatus.FAILED);
+ adr.setDownloadDetails(ArtifactDownloadReport.MISSING_ARTIFACT);
+ adr.setDownloadTimeMillis(System.currentTimeMillis() - start);
+ }
+ } catch (Exception ex) {
+ Message.debug(ex);
+ adr.setDownloadStatus(DownloadStatus.FAILED);
+ adr.setDownloadDetails(ex.getMessage());
+ adr.setDownloadTimeMillis(System.currentTimeMillis() - start);
+ }
+ }
+ if (adr.getDownloadStatus() != DownloadStatus.FAILED) {
+ unpackArtifact(artifact, adr, options);
+ }
+ if (listener != null) {
+ listener.endArtifactDownload(this, artifact, adr, archiveFile);
+ }
+ return adr;
+ } finally {
+ unlockMetadataArtifact(mrid);
+ }
+ }
+
+ private void unpackArtifact(Artifact artifact, ArtifactDownloadReport adr,
+ CacheDownloadOptions options) {
+ Artifact unpacked = packagingManager.getUnpackedArtifact(artifact);
+ if (unpacked == null) {
+ // nothing to unpack
+ return;
+ }
+
+ File archiveFile = getArchiveFileInCache(unpacked, null, false);
+ if (archiveFile.exists() && !options.isForce()) {
+ adr.setUnpackedLocalFile(archiveFile);
+ } else {
+ Message.info("\tUnpacking " + artifact.getId());
+ try {
+ packagingManager.unpackArtifact(artifact, adr.getLocalFile(), archiveFile);
+ adr.setUnpackedLocalFile(archiveFile);
+ } catch (Exception e) {
+ Message.debug(e);
+ adr.setDownloadStatus(DownloadStatus.FAILED);
+ adr.setDownloadDetails("The packed artifact " + artifact.getId()
+ + " could not be unpacked (" + e.getMessage() + ")");
+ }
+ }
+ }
+
+ public ArtifactDownloadReport downloadRepositoryResource(final Resource resource, String name,
+ String type, String extension, CacheResourceOptions options, Repository repository) {
+
+ String hash = computeResourceNameHash(resource);
+ ModuleRevisionId mrid = ModuleRevisionId.newInstance("_repository_metadata_", hash,
+ Ivy.getWorkingRevision());
+ Artifact artifact = new DefaultArtifact(mrid, null, name, type, extension);
+ final ArtifactDownloadReport adr = new ArtifactDownloadReport(artifact);
+ boolean useOrigin = isUseOrigin();
+
+ try {
+ DownloadListener listener = options.getListener();
+ if (listener != null) {
+ listener.needArtifact(this, artifact);
+ }
+ ArtifactOrigin savedOrigin = getSavedArtifactOrigin(artifact);
+ File archiveFile = getArchiveFileInCache(artifact, savedOrigin, useOrigin);
+
+ ArtifactOrigin origin = new ArtifactOrigin(artifact, resource.isLocal(),
+ resource.getName());
+
+ if (!options.isForce()
+ // if the local file has been checked to be up to date enough recently, don't download
+ && checkCacheUptodate(archiveFile, resource, savedOrigin, origin,
+ options.getTtl())) {
+ if (archiveFile.exists()) {
+ saveArtifactOrigin(artifact, origin);
+ adr.setDownloadStatus(DownloadStatus.NO);
+ adr.setSize(archiveFile.length());
+ adr.setArtifactOrigin(savedOrigin);
+ adr.setLocalFile(archiveFile);
+ } else {
+ // we trust the cache to says that the resource doesn't exist
+ adr.setDownloadStatus(DownloadStatus.FAILED);
+ adr.setDownloadDetails("Remote resource is known to not exist");
+ }
+ } else {
+ long start = System.currentTimeMillis();
+ origin.setLastChecked(new Long(start));
+ try {
+ ResolvedResource artifactRef = new ResolvedResource(resource,
+ Ivy.getWorkingRevision());
+ if (useOrigin && resource.isLocal()) {
+ saveArtifactOrigin(artifact, origin);
+ archiveFile = getArchiveFileInCache(artifact, origin);
+ adr.setDownloadStatus(DownloadStatus.NO);
+ adr.setSize(archiveFile.length());
+ adr.setArtifactOrigin(origin);
+ adr.setLocalFile(archiveFile);
+ } else {
+ if (listener != null) {
+ listener.startArtifactDownload(this, artifactRef, artifact, origin);
+ }
+
+ // actual download
+ if (archiveFile.exists()) {
+ archiveFile.delete();
+ }
+ File part = new File(archiveFile.getAbsolutePath() + ".part");
+ repository.get(resource.getName(), part);
+ if (!part.renameTo(archiveFile)) {
+ throw new IOException(
+ "impossible to move part file to definitive one: " + part
+ + " -> " + archiveFile);
+ }
+
+ adr.setSize(archiveFile.length());
+ saveArtifactOrigin(artifact, origin);
+ adr.setDownloadTimeMillis(System.currentTimeMillis() - start);
+ adr.setDownloadStatus(DownloadStatus.SUCCESSFUL);
+ adr.setArtifactOrigin(origin);
+ adr.setLocalFile(archiveFile);
+ }
+ } catch (Exception ex) {
+ Message.debug(ex);
+ origin.setExist(false);
+ saveArtifactOrigin(artifact, origin);
+ adr.setDownloadStatus(DownloadStatus.FAILED);
+ adr.setDownloadDetails(ex.getMessage());
+ adr.setDownloadTimeMillis(System.currentTimeMillis() - start);
+ }
+ }
+ if (listener != null) {
+ listener.endArtifactDownload(this, artifact, adr, archiveFile);
+ }
+ return adr;
+ } finally {
+ unlockMetadataArtifact(mrid);
+ }
+ }
+
+ /**
+ * Compute a SHA1 of the resource name, encoded in base64, so we can use it as a file name.
+ *
+ * @param resource
+ * the resource which name will be hashed
+ * @return the hash
+ */
+ private String computeResourceNameHash(Resource resource) {
+ byte[] shaDigest;
+ try {
+ shaDigest = SHA_DIGEST.digest(resource.getName().getBytes("UTF-8"));
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException("UTF-8 not supported", e);
+ }
+ return HexEncoder.encode(shaDigest);
+ }
+
+ /**
+ * Check that a cached file can be considered up to date and thus not downloaded
+ *
+ * @param archiveFile
+ * the file in the cache
+ * @param resource
+ * the remote resource to check
+ * @param savedOrigin
+ * the saved origin which contains that last checked date
+ * @param origin
+ * the origin in which to store the new last checked date
+ * @param ttl
+ * the time to live to consider the cache up to date
+ * @return <code>true</code> if the cache is considered up to date
+ */
+ private boolean checkCacheUptodate(File archiveFile, Resource resource,
+ ArtifactOrigin savedOrigin, ArtifactOrigin origin, long ttl) {
+ long time = System.currentTimeMillis();
+ if (savedOrigin.getLastChecked() != null
+ && (time - savedOrigin.getLastChecked().longValue()) < ttl) {
+ // still in the ttl period, no need to check, trust the cache
+ if (!archiveFile.exists()) {
+ // but if the local archive doesn't exist, trust the cache only if the cached origin
+ // says that the remote resource doesn't exist either
+ return !savedOrigin.isExists();
+ }
+ return true;
+ }
+ if (!archiveFile.exists()) {
+ // the the file doesn't exist in the cache, obviously not up to date
+ return false;
+ }
+ origin.setLastChecked(new Long(time));
+ // check if the local resource is up to date regarding the remote one
+ return archiveFile.lastModified() >= resource.getLastModified();
+ }
+
+ public void originalToCachedModuleDescriptor(DependencyResolver resolver,
+ ResolvedResource orginalMetadataRef, Artifact requestedMetadataArtifact,
+ ResolvedModuleRevision rmr, ModuleDescriptorWriter writer) {
+ ModuleDescriptor md = rmr.getDescriptor();
+ Artifact originalMetadataArtifact = getOriginalMetadataArtifact(requestedMetadataArtifact);
+ File mdFileInCache = getIvyFileInCache(md.getResolvedModuleRevisionId());
+
+ ModuleRevisionId mrid = requestedMetadataArtifact.getModuleRevisionId();
+ if (!lockMetadataArtifact(mrid)) {
+ Message.warn("impossible to acquire lock for: " + mrid);
+ return;
+ }
+ try {
+ File originalFileInCache = getArchiveFileInCache(originalMetadataArtifact);
+ writer.write(orginalMetadataRef, md, originalFileInCache, mdFileInCache);
+
+ getMemoryCache().putInCache(mdFileInCache, new ParserSettingsMonitor(settings), true,
+ md);
+ saveResolvers(md, resolver.getName(), resolver.getName());
+
+ if (!md.isDefault()) {
+ rmr.getReport().setOriginalLocalFile(originalFileInCache);
+ }
+ rmr.getReport().setLocalFile(mdFileInCache);
+ } catch (RuntimeException e) {
+ throw e;
+ } catch (Exception e) {
+ String metadataRef;
+ if (orginalMetadataRef == null) {
+ metadataRef = String.valueOf(md.getResolvedModuleRevisionId());
+ } else {
+ metadataRef = String.valueOf(orginalMetadataRef);
+ }
+ Message.warn("impossible to put metadata file in cache: " + metadataRef, e);
+ } finally {
+ unlockMetadataArtifact(mrid);
+ }
+ }
+
+ public ResolvedModuleRevision cacheModuleDescriptor(DependencyResolver resolver,
+ final ResolvedResource mdRef, DependencyDescriptor dd, Artifact moduleArtifact,
+ ResourceDownloader downloader, CacheMetadataOptions options) throws ParseException {
+ Date cachedPublicationDate = null;
+ ArtifactDownloadReport report;
+ ModuleRevisionId mrid = moduleArtifact.getModuleRevisionId();
+ if (!lockMetadataArtifact(mrid)) {
+ Message.error("impossible to acquire lock for " + mrid);
+ return null;
+ }
+
+ BackupResourceDownloader backupDownloader = new BackupResourceDownloader(downloader);
+
+ try {
+ if (!moduleArtifact.isMetadata()) {
+ // the descriptor we are trying to cache is a default one, not much to do
+ // just make sure the old artifacts are deleted...
+ if (isChanging(dd, mrid, options)) {
+ long repoLastModified = mdRef.getLastModified();
+
+ Artifact transformedArtifact = NameSpaceHelper.transform(moduleArtifact,
+ options.getNamespace().getToSystemTransformer());
+ ArtifactOrigin origin = getSavedArtifactOrigin(transformedArtifact);
+ File artFile = getArchiveFileInCache(transformedArtifact, origin, false);
+ if (artFile.exists() && repoLastModified > artFile.lastModified()) {
+ // artifacts have changed, they should be downloaded again
+ Message.verbose(mrid + " has changed: deleting old artifacts");
+ Message.debug("deleting " + artFile);
+ if (!artFile.delete()) {
+ Message.error("Couldn't delete outdated artifact from cache: "
+ + artFile);
+ return null;
+ }
+ removeSavedArtifactOrigin(transformedArtifact);
+ }
+ }
+ return null;
+ }
+
+ // now let's see if we can find it in cache and if it is up to date
+ ResolvedModuleRevision rmr = doFindModuleInCache(mrid, options, null);
+ if (rmr != null) {
+ if (rmr.getDescriptor().isDefault() && rmr.getResolver() != resolver) {
+ Message.verbose("\t" + getName() + ": found revision in cache: " + mrid
+ + " (resolved by " + rmr.getResolver().getName()
+ + "): but it's a default one, maybe we can find a better one");
+ } else {
+ if (!isCheckmodified(dd, mrid, options) && !isChanging(dd, mrid, options)) {
+ Message.verbose("\t" + getName() + ": revision in cache: " + mrid);
+ rmr.getReport().setSearched(true);
+ return rmr;
+ }
+ long repLastModified = mdRef.getLastModified();
+ long cacheLastModified = rmr.getDescriptor().getLastModified();
+ if (!rmr.getDescriptor().isDefault() && repLastModified <= cacheLastModified) {
+ Message.verbose("\t" + getName() + ": revision in cache (not updated): "
+ + mrid);
+ rmr.getReport().setSearched(true);
+ return rmr;
+ } else {
+ Message.verbose("\t" + getName()
+ + ": revision in cache is not up to date: " + mrid);
+ if (isChanging(dd, mrid, options)) {
+ // ivy file has been updated, we should see if it has a new publication
+ // date to see if a new download is required (in case the dependency is
+ // a changing one)
+ cachedPublicationDate = rmr.getDescriptor()
+ .getResolvedPublicationDate();
+ }
+ }
+ }
+ }
+
+ Artifact originalMetadataArtifact = getOriginalMetadataArtifact(moduleArtifact);
+ // now download module descriptor and parse it
+ report = download(originalMetadataArtifact, new ArtifactResourceResolver() {
+ public ResolvedResource resolve(Artifact artifact) {
+ return mdRef;
+ }
+ }, backupDownloader, new CacheDownloadOptions().setListener(options.getListener())
+ .setForce(true));
+ Message.verbose("\t" + report);
+
+ if (report.getDownloadStatus() == DownloadStatus.FAILED) {
+ Message.warn("problem while downloading module descriptor: " + mdRef.getResource()
+ + ": " + report.getDownloadDetails() + " ("
+ + report.getDownloadTimeMillis() + "ms)");
+ return null;
+ }
+
+ try {
+ ModuleDescriptorParser parser = ModuleDescriptorParserRegistry.getInstance()
+ .getParser(mdRef.getResource());
+ ParserSettings parserSettings = settings;
+ if (resolver instanceof AbstractResolver) {
+ parserSettings = ((AbstractResolver) resolver).getParserSettings();
+ }
+ ModuleDescriptor md = getStaledMd(parser, options, report.getLocalFile(),
+ parserSettings);
+ if (md == null) {
+ throw new IllegalStateException(
+ "module descriptor parser returned a null module descriptor, "
+ + "which is not allowed. " + "parser=" + parser
+ + "; parser class=" + parser.getClass().getName()
+ + "; module descriptor resource=" + mdRef.getResource());
+ }
+ Message.debug("\t" + getName() + ": parsed downloaded md file for " + mrid
+ + "; parsed=" + md.getModuleRevisionId());
+
+ // check if we should delete old artifacts
+ boolean deleteOldArtifacts = false;
+ if (cachedPublicationDate != null
+ && !cachedPublicationDate.equals(md.getResolvedPublicationDate())) {
+ // artifacts have changed, they should be downloaded again
+ Message.verbose(mrid + " has changed: deleting old artifacts");
+ deleteOldArtifacts = true;
+ }
+ if (deleteOldArtifacts) {
+ String[] confs = md.getConfigurationsNames();
+ for (int i = 0; i < confs.length; i++) {
+ Artifact[] arts = md.getArtifacts(confs[i]);
+ for (int j = 0; j < arts.length; j++) {
+ Artifact transformedArtifact = NameSpaceHelper.transform(arts[j],
+ options.getNamespace().getToSystemTransformer());
+ ArtifactOrigin origin = getSavedArtifactOrigin(transformedArtifact);
+ File artFile = getArchiveFileInCache(transformedArtifact, origin, false);
+ if (artFile.exists()) {
+ Message.debug("deleting " + artFile);
+ if (!artFile.delete()) {
+ // Old artifacts couldn't get deleted!
+ // Restore the original ivy file so the next time we
+ // resolve the old artifacts are deleted again
+ backupDownloader.restore();
+ Message.error("Couldn't delete outdated artifact from cache: "
+ + artFile);
+ return null;
+ }
+ }
+ removeSavedArtifactOrigin(transformedArtifact);
+ }
+ }
+ } else if (isChanging(dd, mrid, options)) {
+ Message.verbose(mrid
+ + " is changing, but has not changed: will trust cached artifacts if any");
+ }
+
+ MetadataArtifactDownloadReport madr = new MetadataArtifactDownloadReport(
+ md.getMetadataArtifact());
+ madr.setSearched(true);
+ madr.setDownloadStatus(report.getDownloadStatus());
+ madr.setDownloadDetails(report.getDownloadDetails());
+ madr.setArtifactOrigin(report.getArtifactOrigin());
+ madr.setDownloadTimeMillis(report.getDownloadTimeMillis());
+ madr.setOriginalLocalFile(report.getLocalFile());
+ madr.setSize(report.getSize());
+
+ Artifact transformedMetadataArtifact = NameSpaceHelper.transform(
+ md.getMetadataArtifact(), options.getNamespace().getToSystemTransformer());
+ saveArtifactOrigin(transformedMetadataArtifact, report.getArtifactOrigin());
+
+ return new ResolvedModuleRevision(resolver, resolver, md, madr);
+ } catch (IOException ex) {
+ Message.warn("io problem while parsing ivy file: " + mdRef.getResource(), ex);
+ return null;
+ }
+ } finally {
+ unlockMetadataArtifact(mrid);
+ backupDownloader.cleanUp();
+ }
+
+ }
+
+ // lock used to lock all metadata related information access
+ private boolean lockMetadataArtifact(ModuleRevisionId mrid) {
+ Artifact artifact = getDefaultMetadataArtifact(mrid);
+ try {
+ // we need to provide an artifact origin to be sure we do not end up in a stack overflow
+ // if the cache pattern is using original name, and the substitution thus trying to get
+ // the saved artifact origin value which in turns calls this method
+ return getLockStrategy().lockArtifact(artifact,
+ getArchiveFileInCache(artifact, getDefaultMetadataArtifactOrigin(mrid)));
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt(); // reset interrupt status
+ throw new RuntimeException("operation interrupted");
+ }
+ }
+
+ private void unlockMetadataArtifact(ModuleRevisionId mrid) {
+ Artifact artifact = getDefaultMetadataArtifact(mrid);
+ getLockStrategy().unlockArtifact(artifact,
+ getArchiveFileInCache(artifact, getDefaultMetadataArtifactOrigin(mrid)));
+ }
+
+ private ArtifactOrigin getDefaultMetadataArtifactOrigin(ModuleRevisionId mrid) {
+ // it's important to say the origin is not local to make sure it won't ever be used for
+ // anything else than original token
+ return new ArtifactOrigin(DefaultArtifact.newIvyArtifact(mrid, null), false,
+ getIvyFileInCache(mrid).getPath());
+ }
+
+ private Artifact getDefaultMetadataArtifact(ModuleRevisionId mrid) {
+ return new DefaultArtifact(mrid, new Date(), "metadata", "metadata", "ivy", true);
+ }
+
+ // not used any more, but maybe useful for finer grain locking when downloading artifacts
+ // private boolean lockArtifact(Artifact artifact) {
+ // try {
+ // return getLockStrategy().lockArtifact(artifact,
+ // getArchiveFileInCache(artifact, null));
+ // } catch (InterruptedException e) {
+ // Thread.currentThread().interrupt(); // reset interrupt status
+ // throw new RuntimeException("operation interrupted");
+ // }
+ // }
+ //
+ // private void unlockArtifact(Artifact artifact) {
+ // getLockStrategy().unlockArtifact(artifact, getArchiveFileInCache(artifact, null));
+ // }
+
+ public Artifact getOriginalMetadataArtifact(Artifact moduleArtifact) {
+ return DefaultArtifact.cloneWithAnotherType(moduleArtifact, moduleArtifact.getType()
+ + ".original");
+ }
+
+ private boolean isOriginalMetadataArtifact(Artifact artifact) {
+ return artifact.isMetadata() && artifact.getType().endsWith(".original");
+ }
+
+ private boolean isChanging(DependencyDescriptor dd, ModuleRevisionId requestedRevisionId,
+ CacheMetadataOptions options) {
+ return dd.isChanging()
+ || getChangingMatcher(options).matches(requestedRevisionId.getRevision());
+ }
+
+ private Matcher getChangingMatcher(CacheMetadataOptions options) {
+ String changingPattern = options.getChangingPattern() != null ? options
+ .getChangingPattern() : this.changingPattern;
+ if (changingPattern == null) {
+ return NoMatcher.INSTANCE;
+ }
+ String changingMatcherName = options.getChangingMatcherName() != null ? options
+ .getChangingMatcherName() : this.changingMatcherName;
+ PatternMatcher matcher = settings.getMatcher(changingMatcherName);
+ if (matcher == null) {
+ throw new IllegalStateException("unknown matcher '" + changingMatcherName
+ + "'. It is set as changing matcher in " + this);
+ }
+ return matcher.getMatcher(changingPattern);
+ }
+
+ private boolean isCheckmodified(DependencyDescriptor dd, ModuleRevisionId requestedRevisionId,
+ CacheMetadataOptions options) {
+ if (options.isCheckmodified() != null) {
+ return options.isCheckmodified().booleanValue();
+ }
+ return isCheckmodified();
+ }
+
+ public void clean() {
+ FileUtil.forceDelete(getBasedir());
+ }
+
+ public void dumpSettings() {
+ Message.verbose("\t" + getName());
+ Message.debug("\t\tivyPattern: " + getIvyPattern());
+ Message.debug("\t\tartifactPattern: " + getArtifactPattern());
+ Message.debug("\t\tlockingStrategy: " + getLockStrategy().getName());
+ Message.debug("\t\tchangingPattern: " + getChangingPattern());
+ Message.debug("\t\tchangingMatcher: " + getChangingMatcherName());
+ }
+
+ /**
+ * Resource downloader which makes a copy of the previously existing file before overriding it.
+ * <p>
+ * The backup file can be restored or cleanuped later
+ */
+ private final class BackupResourceDownloader implements ResourceDownloader {
+
+ private ResourceDownloader delegate;
+
+ private File backup;
+
+ private String originalPath;
+
+ private BackupResourceDownloader(ResourceDownloader delegate) {
+ this.delegate = delegate;
+ }
+
+ public void download(Artifact artifact, Resource resource, File dest) throws IOException {
+ // keep a copy of the original file
+ if (dest.exists()) {
+ originalPath = dest.getAbsolutePath();
+ backup = new File(dest.getAbsolutePath() + ".backup");
+ FileUtil.copy(dest, backup, null, true);
+ }
+ delegate.download(artifact, resource, dest);
+ }
+
+ public void restore() throws IOException {
+ if ((backup != null) && backup.exists()) {
+ File original = new File(originalPath);
+ FileUtil.copy(backup, original, null, true);
+ backup.delete();
+ }
+ }
+
+ public void cleanUp() {
+ if ((backup != null) && backup.exists()) {
+ backup.delete();
+ }
+ }
+
+ }
+
+}
diff --git a/src/java/org/apache/ivy/core/cache/DefaultResolutionCacheManager.java b/src/java/org/apache/ivy/core/cache/DefaultResolutionCacheManager.java
new file mode 100644
index 0000000..2683702
--- /dev/null
+++ b/src/java/org/apache/ivy/core/cache/DefaultResolutionCacheManager.java
@@ -0,0 +1,304 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.cache;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.text.ParseException;
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.ivy.core.IvyPatternHelper;
+import org.apache.ivy.core.RelativeUrlResolver;
+import org.apache.ivy.core.module.descriptor.ExtendsDescriptor;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleId;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.module.status.StatusManager;
+import org.apache.ivy.core.settings.IvySettings;
+import org.apache.ivy.plugins.IvySettingsAware;
+import org.apache.ivy.plugins.conflict.ConflictManager;
+import org.apache.ivy.plugins.matcher.PatternMatcher;
+import org.apache.ivy.plugins.namespace.Namespace;
+import org.apache.ivy.plugins.parser.ModuleDescriptorParser;
+import org.apache.ivy.plugins.parser.ParserSettings;
+import org.apache.ivy.plugins.parser.xml.XmlModuleDescriptorParser;
+import org.apache.ivy.plugins.resolver.DependencyResolver;
+import org.apache.ivy.util.FileUtil;
+
+public class DefaultResolutionCacheManager implements ResolutionCacheManager, IvySettingsAware {
+
+ private static final String DEFAULT_CACHE_RESOLVED_IVY_PATTERN = "resolved-[organisation]-[module]-[revision].xml";
+
+ private static final String DEFAULT_CACHE_RESOLVED_IVY_PROPERTIES_PATTERN = "resolved-[organisation]-[module]-[revision].properties";
+
+ private String resolvedIvyPattern = DEFAULT_CACHE_RESOLVED_IVY_PATTERN;
+
+ private String resolvedIvyPropertiesPattern = DEFAULT_CACHE_RESOLVED_IVY_PROPERTIES_PATTERN;
+
+ private File basedir;
+
+ private String name = "resolution-cache";
+
+ private IvySettings settings;
+
+ public DefaultResolutionCacheManager() {
+ }
+
+ public DefaultResolutionCacheManager(File basedir) {
+ setBasedir(basedir);
+ }
+
+ public void setSettings(IvySettings settings) {
+ this.settings = settings;
+ }
+
+ public File getResolutionCacheRoot() {
+ return basedir;
+ }
+
+ public File getBasedir() {
+ return basedir;
+ }
+
+ public void setBasedir(File basedir) {
+ this.basedir = basedir;
+ }
+
+ public String getResolvedIvyPattern() {
+ return resolvedIvyPattern;
+ }
+
+ public void setResolvedIvyPattern(String cacheResolvedIvyPattern) {
+ this.resolvedIvyPattern = cacheResolvedIvyPattern;
+ }
+
+ public String getResolvedIvyPropertiesPattern() {
+ return resolvedIvyPropertiesPattern;
+ }
+
+ public void setResolvedIvyPropertiesPattern(String cacheResolvedIvyPropertiesPattern) {
+ this.resolvedIvyPropertiesPattern = cacheResolvedIvyPropertiesPattern;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public File getResolvedIvyFileInCache(ModuleRevisionId mrid) {
+ String file = IvyPatternHelper.substitute(getResolvedIvyPattern(), mrid.getOrganisation(),
+ mrid.getName(), mrid.getRevision(), "ivy", "ivy", "xml");
+ return new File(getResolutionCacheRoot(), file);
+ }
+
+ public File getResolvedIvyPropertiesInCache(ModuleRevisionId mrid) {
+ String file = IvyPatternHelper.substitute(getResolvedIvyPropertiesPattern(),
+ mrid.getOrganisation(), mrid.getName(), mrid.getRevision(), "ivy", "ivy", "xml");
+ return new File(getResolutionCacheRoot(), file);
+ }
+
+ public File getConfigurationResolveReportInCache(String resolveId, String conf) {
+ return new File(getResolutionCacheRoot(), resolveId + "-" + conf + ".xml");
+ }
+
+ public File[] getConfigurationResolveReportsInCache(final String resolveId) {
+ final String prefix = resolveId + "-";
+ final String suffix = ".xml";
+ return getResolutionCacheRoot().listFiles(new FilenameFilter() {
+ public boolean accept(File dir, String name) {
+ return (name.startsWith(prefix) && name.endsWith(suffix));
+ }
+ });
+ }
+
+ public ModuleDescriptor getResolvedModuleDescriptor(ModuleRevisionId mrid)
+ throws ParseException, IOException {
+ File ivyFile = getResolvedIvyFileInCache(mrid);
+ if (!ivyFile.exists()) {
+ throw new IllegalStateException("Ivy file not found in cache for " + mrid + "!");
+ }
+
+ Properties paths = new Properties();
+
+ File parentsFile = getResolvedIvyPropertiesInCache(ModuleRevisionId.newInstance(mrid,
+ mrid.getRevision() + "-parents"));
+ if (parentsFile.exists()) {
+ FileInputStream in = new FileInputStream(parentsFile);
+ paths.load(in);
+ in.close();
+ }
+
+ ParserSettings pSettings = new CacheParserSettings(settings, paths);
+
+ URL ivyFileURL = ivyFile.toURI().toURL();
+ return getModuleDescriptorParser(ivyFile).parseDescriptor(pSettings, ivyFileURL, false);
+ }
+
+ /**
+ * Choose write module descriptor parser for a given moduleDescriptor
+ *
+ * @param moduleDescriptorFile
+ * a given module descriptor
+ * @return
+ */
+ protected ModuleDescriptorParser getModuleDescriptorParser(File moduleDescriptorFile) {
+ return XmlModuleDescriptorParser.getInstance();
+ }
+
+ public void saveResolvedModuleDescriptor(ModuleDescriptor md) throws ParseException,
+ IOException {
+ ModuleRevisionId mrevId = md.getResolvedModuleRevisionId();
+ File ivyFileInCache = getResolvedIvyFileInCache(mrevId);
+ md.toIvyFile(ivyFileInCache);
+
+ Properties paths = new Properties();
+ saveLocalParents(mrevId, md, ivyFileInCache, paths);
+
+ if (!paths.isEmpty()) {
+ File parentsFile = getResolvedIvyPropertiesInCache(ModuleRevisionId.newInstance(mrevId,
+ mrevId.getRevision() + "-parents"));
+ FileOutputStream out = new FileOutputStream(parentsFile);
+ paths.store(out, null);
+ out.close();
+ }
+ }
+
+ private void saveLocalParents(ModuleRevisionId baseMrevId, ModuleDescriptor md, File mdFile,
+ Properties paths) throws ParseException, IOException {
+ ExtendsDescriptor[] parents = md.getInheritedDescriptors();
+ for (int i = 0; i < parents.length; i++) {
+ if (!parents[i].isLocal()) {
+ // we store only local parents in the cache!
+ continue;
+ }
+
+ ModuleDescriptor parent = parents[i].getParentMd();
+ ModuleRevisionId pRevId = ModuleRevisionId.newInstance(baseMrevId,
+ baseMrevId.getRevision() + "-parent." + paths.size());
+ File parentFile = getResolvedIvyFileInCache(pRevId);
+ parent.toIvyFile(parentFile);
+
+ paths.setProperty(mdFile.getName() + "|" + parents[i].getLocation(),
+ parentFile.getAbsolutePath());
+ saveLocalParents(baseMrevId, parent, parentFile, paths);
+ }
+ }
+
+ public String toString() {
+ return name;
+ }
+
+ public void clean() {
+ FileUtil.forceDelete(getBasedir());
+ }
+
+ private static class CacheParserSettings implements ParserSettings {
+
+ private ParserSettings delegate;
+
+ private Map parentPaths;
+
+ public CacheParserSettings(ParserSettings delegate, Map parentPaths) {
+ this.delegate = delegate;
+ this.parentPaths = parentPaths;
+ }
+
+ public String substitute(String value) {
+ return delegate.substitute(value);
+ }
+
+ public Map substitute(Map strings) {
+ return delegate.substitute(strings);
+ }
+
+ public ResolutionCacheManager getResolutionCacheManager() {
+ return delegate.getResolutionCacheManager();
+ }
+
+ public ConflictManager getConflictManager(String name) {
+ return delegate.getConflictManager(name);
+ }
+
+ public PatternMatcher getMatcher(String matcherName) {
+ return delegate.getMatcher(matcherName);
+ }
+
+ public Namespace getNamespace(String namespace) {
+ return delegate.getNamespace(namespace);
+ }
+
+ public StatusManager getStatusManager() {
+ return delegate.getStatusManager();
+ }
+
+ public RelativeUrlResolver getRelativeUrlResolver() {
+ return new MapURLResolver(parentPaths, delegate.getRelativeUrlResolver());
+ }
+
+ public DependencyResolver getResolver(ModuleRevisionId mRevId) {
+ return delegate.getResolver(mRevId);
+ }
+
+ public File resolveFile(String filename) {
+ return delegate.resolveFile(filename);
+ }
+
+ public String getDefaultBranch(ModuleId moduleId) {
+ return delegate.getDefaultBranch(moduleId);
+ }
+
+ public Namespace getContextNamespace() {
+ return delegate.getContextNamespace();
+ }
+ }
+
+ private static class MapURLResolver extends RelativeUrlResolver {
+
+ private Map paths;
+
+ private RelativeUrlResolver delegate;
+
+ private MapURLResolver(Map paths, RelativeUrlResolver delegate) {
+ this.paths = paths;
+ this.delegate = delegate;
+ }
+
+ public URL getURL(URL context, String url) throws MalformedURLException {
+ String path = context.getPath();
+ if (path.indexOf('/') >= 0) {
+ String file = path.substring(path.lastIndexOf('/') + 1);
+
+ if (paths.containsKey(file + "|" + url)) {
+ File result = new File(paths.get(file + "|" + url).toString());
+ return result.toURI().toURL();
+ }
+ }
+
+ return delegate.getURL(context, url);
+ }
+ }
+}
diff --git a/src/java/org/apache/ivy/core/cache/DownloadListener.java b/src/java/org/apache/ivy/core/cache/DownloadListener.java
new file mode 100644
index 0000000..50f7741
--- /dev/null
+++ b/src/java/org/apache/ivy/core/cache/DownloadListener.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.cache;
+
+import java.io.File;
+
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.report.ArtifactDownloadReport;
+import org.apache.ivy.plugins.resolver.util.ResolvedResource;
+
+public interface DownloadListener {
+ public void needArtifact(RepositoryCacheManager cache, Artifact artifact);
+
+ public void startArtifactDownload(RepositoryCacheManager cache, ResolvedResource rres,
+ Artifact artifact, ArtifactOrigin origin);
+
+ public void endArtifactDownload(RepositoryCacheManager cache, Artifact artifact,
+ ArtifactDownloadReport adr, File archiveFile);
+}
diff --git a/src/java/org/apache/ivy/core/cache/ModuleDescriptorMemoryCache.java b/src/java/org/apache/ivy/core/cache/ModuleDescriptorMemoryCache.java
new file mode 100644
index 0000000..c4207a9
--- /dev/null
+++ b/src/java/org/apache/ivy/core/cache/ModuleDescriptorMemoryCache.java
@@ -0,0 +1,134 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.cache;
+
+import java.io.File;
+import java.io.IOException;
+import java.text.ParseException;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.plugins.parser.ParserSettings;
+import org.apache.ivy.util.Message;
+
+/**
+ * Cache ModuleDescriptors so that when the same module is used twice (in multi-module build for
+ * instance), it is parsed only once. This cache is has a limited size, and keep the most recently
+ * used entries. The entry in the cache are invalidated if there is a change to one variable used in
+ * the module descriptor.
+ */
+class ModuleDescriptorMemoryCache {
+
+ private final int maxSize;
+
+ private final LinkedHashMap/* <File,CacheEntry> */valueMap;
+
+ /**
+ * Create a cache of the given size
+ *
+ * @param size
+ */
+ public ModuleDescriptorMemoryCache(int size) {
+ this.maxSize = size;
+ this.valueMap = new LinkedHashMap(size);
+ }
+
+ public ModuleDescriptor get(File ivyFile, ParserSettings ivySettings, boolean validated,
+ ModuleDescriptorProvider mdProvider) throws ParseException, IOException {
+
+ ModuleDescriptor descriptor = getFromCache(ivyFile, ivySettings, validated);
+ if (descriptor == null) {
+ descriptor = getStale(ivyFile, ivySettings, validated, mdProvider);
+ }
+ return descriptor;
+ }
+
+ /**
+ * Get the module descriptor from the mdProvider and store it into the cache.
+ */
+ public ModuleDescriptor getStale(File ivyFile, ParserSettings ivySettings, boolean validated,
+ ModuleDescriptorProvider mdProvider) throws ParseException, IOException {
+ ParserSettingsMonitor settingsMonitor = new ParserSettingsMonitor(ivySettings);
+ ModuleDescriptor descriptor = mdProvider.provideModule(
+ settingsMonitor.getMonitoredSettings(), ivyFile, validated);
+ putInCache(ivyFile, settingsMonitor, validated, descriptor);
+ return descriptor;
+ }
+
+ ModuleDescriptor getFromCache(File ivyFile, ParserSettings ivySettings, boolean validated) {
+ if (maxSize <= 0) {
+ // cache is disbaled
+ return null;
+ }
+ CacheEntry entry = (CacheEntry) valueMap.get(ivyFile);
+ if (entry != null) {
+ if (entry.isStale(validated, ivySettings)) {
+ Message.debug("Entry is found in the ModuleDescriptorCache but entry should be "
+ + "reevaluated : " + ivyFile);
+ valueMap.remove(ivyFile);
+ return null;
+ } else {
+ // Move the entry at the end of the list
+ valueMap.remove(ivyFile);
+ valueMap.put(ivyFile, entry);
+ Message.debug("Entry is found in the ModuleDescriptorCache : " + ivyFile);
+ return entry.md;
+ }
+ } else {
+ Message.debug("No entry is found in the ModuleDescriptorCache : " + ivyFile);
+ return null;
+ }
+ }
+
+ void putInCache(File url, ParserSettingsMonitor ivySettingsMonitor, boolean validated,
+ ModuleDescriptor descriptor) {
+ if (maxSize <= 0) {
+ // cache is disabled
+ return;
+ }
+ if (valueMap.size() >= maxSize) {
+ Message.debug("ModuleDescriptorCache is full, remove one entry");
+ Iterator it = valueMap.values().iterator();
+ it.next();
+ it.remove();
+ }
+ valueMap.put(url, new CacheEntry(descriptor, validated, ivySettingsMonitor));
+ }
+
+ private static class CacheEntry {
+ private final ModuleDescriptor md;
+
+ private final boolean validated;
+
+ private final ParserSettingsMonitor parserSettingsMonitor;
+
+ CacheEntry(ModuleDescriptor md, boolean validated,
+ ParserSettingsMonitor parserSettingsMonitor) {
+ this.md = md;
+ this.validated = validated;
+ this.parserSettingsMonitor = parserSettingsMonitor;
+ }
+
+ boolean isStale(boolean validated, ParserSettings newParserSettings) {
+ return (validated && !this.validated)
+ || parserSettingsMonitor.hasChanged(newParserSettings);
+ }
+ }
+
+}
diff --git a/src/java/org/apache/ivy/core/cache/ModuleDescriptorProvider.java b/src/java/org/apache/ivy/core/cache/ModuleDescriptorProvider.java
new file mode 100644
index 0000000..96cae4d
--- /dev/null
+++ b/src/java/org/apache/ivy/core/cache/ModuleDescriptorProvider.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.cache;
+
+import java.io.File;
+import java.io.IOException;
+import java.text.ParseException;
+
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.plugins.parser.ParserSettings;
+
+interface ModuleDescriptorProvider {
+
+ public ModuleDescriptor provideModule(ParserSettings ivySettings, File descriptorFile,
+ boolean validate) throws ParseException, IOException;
+}
diff --git a/src/java/org/apache/ivy/core/cache/ModuleDescriptorWriter.java b/src/java/org/apache/ivy/core/cache/ModuleDescriptorWriter.java
new file mode 100644
index 0000000..ee8542d
--- /dev/null
+++ b/src/java/org/apache/ivy/core/cache/ModuleDescriptorWriter.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.cache;
+
+import java.io.File;
+import java.io.IOException;
+import java.text.ParseException;
+
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.plugins.resolver.util.ResolvedResource;
+
+public interface ModuleDescriptorWriter {
+ public void write(ResolvedResource originalMdResource, ModuleDescriptor md, File src, File dest)
+ throws IOException, ParseException;
+}
diff --git a/src/java/org/apache/ivy/core/cache/ParserSettingsMonitor.java b/src/java/org/apache/ivy/core/cache/ParserSettingsMonitor.java
new file mode 100644
index 0000000..58c7f31
--- /dev/null
+++ b/src/java/org/apache/ivy/core/cache/ParserSettingsMonitor.java
@@ -0,0 +1,154 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.cache;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.ivy.core.RelativeUrlResolver;
+import org.apache.ivy.core.module.id.ModuleId;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.module.status.StatusManager;
+import org.apache.ivy.plugins.conflict.ConflictManager;
+import org.apache.ivy.plugins.matcher.PatternMatcher;
+import org.apache.ivy.plugins.namespace.Namespace;
+import org.apache.ivy.plugins.parser.ParserSettings;
+import org.apache.ivy.plugins.resolver.DependencyResolver;
+import org.apache.ivy.util.Message;
+
+/**
+ * Keep traces of the usage of a ParserSettings in order to check afterwards that the relevant
+ * settings didn't changed.
+ * <p>
+ * A ParserSettingsMonitor provide a ParserSettings that must be used in place of the orignal one.
+ * </p>
+ * <p>
+ * The current implementation consider that a settings changed iff one of the used variable has
+ * changed.
+ * </p>
+ */
+class ParserSettingsMonitor {
+
+ private ParserSettings delegatedSettings;
+
+ private final Map/* <String,String> */substitutes;
+
+ public ParserSettingsMonitor(ParserSettings settings) {
+ this.delegatedSettings = settings;
+ this.substitutes = new HashMap();
+ }
+
+ /**
+ * @return The parser settings that must be used in place of the orignal settings The returned
+ * object delegates all the call to the original settings.
+ */
+ public ParserSettings getMonitoredSettings() {
+ return monitoredSettings;
+ }
+
+ /**
+ * Free the ressource used during the monitoring, keeping only the info required to evaluate
+ * hasChanged.
+ */
+ public void endMonitoring() {
+ monitoredSettings = null;
+ delegatedSettings = null;
+ }
+
+ /**
+ * Check if the newSettings is compatible with the original settings that has been monitored.
+ * Only the info that was actually used is compared.
+ */
+ public boolean hasChanged(ParserSettings newSettings) {
+ for (Iterator it = substitutes.entrySet().iterator(); it.hasNext();) {
+ Map.Entry entry = (Entry) it.next();
+ String key = (String) entry.getKey();
+ Object oldValue = entry.getValue();
+ String newValue = newSettings.substitute(key);
+ if (!oldValue.equals(newValue)) {
+ Message.debug("settings variable has changed for : " + entry.getKey());
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private ParserSettings monitoredSettings = new ParserSettings() {
+
+ public ConflictManager getConflictManager(String name) {
+ return delegatedSettings.getConflictManager(name);
+ }
+
+ public PatternMatcher getMatcher(String matcherName) {
+ return delegatedSettings.getMatcher(matcherName);
+ }
+
+ public Namespace getNamespace(String namespace) {
+ return delegatedSettings.getNamespace(namespace);
+ }
+
+ public RelativeUrlResolver getRelativeUrlResolver() {
+ return delegatedSettings.getRelativeUrlResolver();
+ }
+
+ public ResolutionCacheManager getResolutionCacheManager() {
+ return delegatedSettings.getResolutionCacheManager();
+ }
+
+ public DependencyResolver getResolver(ModuleRevisionId mRevId) {
+ return delegatedSettings.getResolver(mRevId);
+ }
+
+ public StatusManager getStatusManager() {
+ return delegatedSettings.getStatusManager();
+ }
+
+ public File resolveFile(String filename) {
+ return delegatedSettings.resolveFile(filename);
+ }
+
+ public String getDefaultBranch(ModuleId moduleId) {
+ return delegatedSettings.getDefaultBranch(moduleId);
+ }
+
+ public Namespace getContextNamespace() {
+ return delegatedSettings.getContextNamespace();
+ }
+
+ public Map substitute(Map strings) {
+ Map substituted = new LinkedHashMap();
+ for (Iterator it = strings.entrySet().iterator(); it.hasNext();) {
+ Map.Entry entry = (Map.Entry) it.next();
+ substituted.put(entry.getKey(), substitute((String) entry.getValue()));
+ }
+ return substituted;
+ }
+
+ public String substitute(String value) {
+ String r = delegatedSettings.substitute(value);
+ if (value != null && value != r) {
+ substitutes.put(value, r);
+ }
+ return r;
+ }
+ };
+}
diff --git a/src/java/org/apache/ivy/core/cache/RepositoryCacheManager.java b/src/java/org/apache/ivy/core/cache/RepositoryCacheManager.java
new file mode 100644
index 0000000..f6c5c24
--- /dev/null
+++ b/src/java/org/apache/ivy/core/cache/RepositoryCacheManager.java
@@ -0,0 +1,194 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.cache;
+
+import java.text.ParseException;
+
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.report.ArtifactDownloadReport;
+import org.apache.ivy.core.resolve.ResolvedModuleRevision;
+import org.apache.ivy.plugins.repository.ArtifactResourceResolver;
+import org.apache.ivy.plugins.repository.Repository;
+import org.apache.ivy.plugins.repository.Resource;
+import org.apache.ivy.plugins.repository.ResourceDownloader;
+import org.apache.ivy.plugins.resolver.DependencyResolver;
+import org.apache.ivy.plugins.resolver.util.ResolvedResource;
+
+public interface RepositoryCacheManager {
+
+ /**
+ * Returns the name of the repository cache manager.
+ *
+ * @return the name of the repository cache manager.
+ */
+ public abstract String getName();
+
+ /**
+ * Saves the information of which resolvers were used to resolve a module (both for metadata and
+ * artifact), so that this info can be loaded later (even after a jvm restart) for the use of
+ * {@link #findModuleInCache(DependencyDescriptor, CacheMetadataOptions, String)}.
+ *
+ * @param md
+ * the module descriptor resolved
+ * @param metadataResolverName
+ * metadata resolver name
+ * @param artifactResolverName
+ * artifact resolver name
+ */
+ public abstract void saveResolvers(ModuleDescriptor descriptor, String metadataResolverName,
+ String artifactResolverName);
+
+ /**
+ * Returns the artifact origin of the given artifact as saved in this cache.
+ * <p>
+ * If the origin is unknown, the returned ArtifactOrigin instance will return true when
+ * {@link ArtifactOrigin#isUnknown(ArtifactOrigin)} is called.
+ *
+ * @param artifact
+ * the artifact for which the saved artifact origin should be returned.
+ * @return the artifact origin of the given artifact as saved in this cache
+ */
+ public abstract ArtifactOrigin getSavedArtifactOrigin(Artifact artifact);
+
+ /**
+ * Search a module descriptor in cache for a mrid
+ *
+ * @param dd
+ * the dependency descriptor identifying the module to search
+ * @param requestedRevisionId
+ * the requested dependency module revision id identifying the module to search
+ * @param options
+ * options on how caching should be handled
+ * @param expectedResolver
+ * the resolver with which the md in cache must have been resolved to be returned,
+ * null if this doesn't matter
+ * @return the ResolvedModuleRevision corresponding to the module found, null if none correct
+ * has been found in cache
+ */
+ public abstract ResolvedModuleRevision findModuleInCache(DependencyDescriptor dd,
+ ModuleRevisionId requestedRevisionId, CacheMetadataOptions options,
+ String expectedResolver);
+
+ /**
+ * Downloads an artifact to this cache.
+ *
+ * @param artifact
+ * the artifact to download
+ * @param resourceResolver
+ * a resource resolver to use if the artifact needs to be resolved to a Resource for
+ * downloading
+ * @param resourceDownloader
+ * a resource downloader to use if actual download of the resource is needed
+ * @param options
+ * a set of options to adjust the download
+ * @return a report indicating how the download was performed
+ */
+ public abstract ArtifactDownloadReport download(Artifact artifact,
+ ArtifactResourceResolver resourceResolver, ResourceDownloader resourceDownloader,
+ CacheDownloadOptions options);
+
+ /**
+ * Download some repository resource and put it in the cache.
+ * <p>
+ * If the cached version is considered enough up to date, no downloading is done.
+ *
+ * @param resource
+ * the resource of the file to put in cache
+ * @param name
+ * the descriptive name of the resource (helps while manually looking into the cache
+ * files)
+ * @param type
+ * the type of the resource (helps while manually looking into the cache files)
+ * @param extension
+ * the extension of the resource (helps while manually looking into the cache files)
+ * @param options
+ * a set of options to adjust the download
+ * @param repository
+ * the repository which resolve the content of the resource
+ * @return a report indicating how the download was performed
+ */
+ public ArtifactDownloadReport downloadRepositoryResource(Resource resource, String name,
+ String type, String extension, CacheResourceOptions options, Repository repository);
+
+ /**
+ * Caches an original module descriptor.
+ * <p>
+ * After this call, the original module descriptor file (with no modification nor conversion)
+ * should be available as a local file.
+ * </p>
+ *
+ * @param resolver
+ * the dependency resolver from which the cache request comes from
+ * @param orginalMetadataRef
+ * a resolved resource pointing to the remote original module descriptor
+ * @param dd
+ * the dependency descriptor for which the module descriptor should be cached
+ * @param requestedMetadataArtifact
+ * the module descriptor artifact as requested originally
+ * @param downloader
+ * a ResourceDownloader able to download the original module descriptor resource if
+ * required by this cache implementation
+ * @param options
+ * options to apply to cache this module descriptor
+ * @return a {@link ResolvedModuleRevision} representing the local cached module descriptor, or
+ * null if it failed
+ * @throws ParseException
+ * if an exception occurred while parsing the module descriptor
+ */
+ public ResolvedModuleRevision cacheModuleDescriptor(DependencyResolver resolver,
+ ResolvedResource orginalMetadataRef, DependencyDescriptor dd,
+ Artifact requestedMetadataArtifact, ResourceDownloader downloader,
+ CacheMetadataOptions options) throws ParseException;
+
+ /**
+ * Stores a standardized version of an original module descriptor in the cache for later use.
+ *
+ * @param resolver
+ * the dependency resolver from which the cache request comes from
+ * @param orginalMetadataRef
+ * a resolved resource pointing to the remote original module descriptor
+ * @param requestedMetadataArtifact
+ * the module descriptor artifact as requested originally
+ * @param rmr
+ * the {@link ResolvedModuleRevision} representing the local cached module descriptor
+ * @param writer
+ * a {@link ModuleDescriptorWriter} able to write the module descriptor to a stream.
+ */
+ public void originalToCachedModuleDescriptor(DependencyResolver resolver,
+ ResolvedResource orginalMetadataRef, Artifact requestedMetadataArtifact,
+ ResolvedModuleRevision rmr, ModuleDescriptorWriter writer);
+
+ /**
+ * Cleans the whole cache.
+ */
+ public void clean();
+
+ /**
+ * Caches a dynamic revision constraint resolution.
+ *
+ * @param dynamicMrid
+ * the dynamic module revision id
+ * @param revision
+ * the resolved revision
+ */
+ public void saveResolvedRevision(ModuleRevisionId dynamicMrid, String revision);
+
+}
diff --git a/src/java/org/apache/ivy/core/cache/ResolutionCacheManager.java b/src/java/org/apache/ivy/core/cache/ResolutionCacheManager.java
new file mode 100644
index 0000000..9ba33d3
--- /dev/null
+++ b/src/java/org/apache/ivy/core/cache/ResolutionCacheManager.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.cache;
+
+import java.io.File;
+import java.io.IOException;
+import java.text.ParseException;
+
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+
+public interface ResolutionCacheManager {
+
+ File getResolutionCacheRoot();
+
+ File getResolvedIvyFileInCache(ModuleRevisionId mrid);
+
+ File getResolvedIvyPropertiesInCache(ModuleRevisionId mrid);
+
+ File getConfigurationResolveReportInCache(String resolveId, String conf);
+
+ File[] getConfigurationResolveReportsInCache(final String resolveId);
+
+ ModuleDescriptor getResolvedModuleDescriptor(ModuleRevisionId mrid) throws ParseException,
+ IOException;
+
+ void saveResolvedModuleDescriptor(ModuleDescriptor md) throws ParseException, IOException;
+
+ /**
+ * Cleans the whole cache.
+ */
+ void clean();
+}
diff --git a/src/java/org/apache/ivy/core/check/CheckEngine.java b/src/java/org/apache/ivy/core/check/CheckEngine.java
new file mode 100644
index 0000000..34d6323
--- /dev/null
+++ b/src/java/org/apache/ivy/core/check/CheckEngine.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.check;
+
+import java.io.IOException;
+import java.net.URL;
+import java.text.ParseException;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.resolve.ResolveData;
+import org.apache.ivy.core.resolve.ResolveEngine;
+import org.apache.ivy.core.resolve.ResolveOptions;
+import org.apache.ivy.core.resolve.ResolvedModuleRevision;
+import org.apache.ivy.plugins.parser.ModuleDescriptorParserRegistry;
+import org.apache.ivy.plugins.resolver.DependencyResolver;
+import org.apache.ivy.util.Message;
+
+public class CheckEngine {
+ private CheckEngineSettings settings;
+
+ private ResolveEngine resolveEngine;
+
+ public CheckEngine(CheckEngineSettings settings, ResolveEngine resolveEngine) {
+ this.settings = settings;
+ this.resolveEngine = resolveEngine;
+ }
+
+ /**
+ * Checks the given ivy file using current settings to see if all dependencies are available,
+ * with good confs. If a resolver name is given, it also checks that the declared publications
+ * are available in the corresponding resolver. Note that the check is not performed
+ * recursively, i.e. if a dependency has itself dependencies badly described or not available,
+ * this check will not discover it.
+ */
+ public boolean check(URL ivyFile, String resolvername) {
+ try {
+ boolean result = true;
+ // parse ivy file
+ ModuleDescriptor md = ModuleDescriptorParserRegistry.getInstance().parseDescriptor(
+ settings, ivyFile, settings.doValidate());
+
+ // check publications if possible
+ if (resolvername != null) {
+ DependencyResolver resolver = settings.getResolver(resolvername);
+ String[] confs = md.getConfigurationsNames();
+ Set artifacts = new HashSet();
+ for (int i = 0; i < confs.length; i++) {
+ artifacts.addAll(Arrays.asList(md.getArtifacts(confs[i])));
+ }
+ for (Iterator iter = artifacts.iterator(); iter.hasNext();) {
+ Artifact art = (Artifact) iter.next();
+ if (!resolver.exists(art)) {
+ Message.info("declared publication not found: " + art);
+ result = false;
+ }
+ }
+ }
+
+ // check dependencies
+ DependencyDescriptor[] dds = md.getDependencies();
+ ResolveData data = new ResolveData(resolveEngine, new ResolveOptions());
+ for (int i = 0; i < dds.length; i++) {
+ // check master confs
+ String[] masterConfs = dds[i].getModuleConfigurations();
+ for (int j = 0; j < masterConfs.length; j++) {
+ if (!"*".equals(masterConfs[j].trim())
+ && md.getConfiguration(masterConfs[j]) == null) {
+ Message.info("dependency required in non existing conf for " + ivyFile
+ + " \n\tin " + dds[i] + ": " + masterConfs[j]);
+ result = false;
+ }
+ }
+ // resolve
+ DependencyResolver resolver = settings
+ .getResolver(dds[i].getDependencyRevisionId());
+ ResolvedModuleRevision rmr = resolver.getDependency(dds[i], data);
+ if (rmr == null) {
+ Message.info("dependency not found in " + ivyFile + ":\n\t" + dds[i]);
+ result = false;
+ } else {
+ String[] depConfs = dds[i].getDependencyConfigurations(md
+ .getConfigurationsNames());
+ for (int j = 0; j < depConfs.length; j++) {
+ if (!Arrays.asList(rmr.getDescriptor().getConfigurationsNames()).contains(
+ depConfs[j])) {
+ Message.info("dependency configuration is missing for " + ivyFile
+ + "\n\tin " + dds[i] + ": " + depConfs[j]);
+ result = false;
+ }
+ Artifact[] arts = rmr.getDescriptor().getArtifacts(depConfs[j]);
+ for (int k = 0; k < arts.length; k++) {
+ if (!resolver.exists(arts[k])) {
+ Message.info("dependency artifact is missing for " + ivyFile
+ + "\n\t in " + dds[i] + ": " + arts[k]);
+ result = false;
+ }
+ }
+ }
+ }
+ }
+ return result;
+ } catch (ParseException e) {
+ Message.info("parse problem on " + ivyFile, e);
+ return false;
+ } catch (IOException e) {
+ Message.info("io problem on " + ivyFile, e);
+ return false;
+ } catch (Exception e) {
+ Message.info("problem on " + ivyFile, e);
+ return false;
+ }
+ }
+
+}
diff --git a/src/java/org/apache/ivy/core/check/CheckEngineSettings.java b/src/java/org/apache/ivy/core/check/CheckEngineSettings.java
new file mode 100644
index 0000000..c700795
--- /dev/null
+++ b/src/java/org/apache/ivy/core/check/CheckEngineSettings.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.check;
+
+import org.apache.ivy.plugins.parser.ParserSettings;
+import org.apache.ivy.plugins.resolver.DependencyResolver;
+
+public interface CheckEngineSettings extends ParserSettings {
+
+ boolean doValidate();
+
+ DependencyResolver getResolver(String resolvername);
+
+}
diff --git a/src/java/org/apache/ivy/core/deliver/DefaultPublishingDRResolver.java b/src/java/org/apache/ivy/core/deliver/DefaultPublishingDRResolver.java
new file mode 100644
index 0000000..50f26e5
--- /dev/null
+++ b/src/java/org/apache/ivy/core/deliver/DefaultPublishingDRResolver.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.deliver;
+
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+
+public class DefaultPublishingDRResolver implements PublishingDependencyRevisionResolver {
+ public String resolve(ModuleDescriptor published, String publishedStatus,
+ ModuleRevisionId depMrid, String status) {
+ return depMrid.getRevision();
+ }
+}
diff --git a/src/java/org/apache/ivy/core/deliver/DeliverEngine.java b/src/java/org/apache/ivy/core/deliver/DeliverEngine.java
new file mode 100644
index 0000000..05ac1c7
--- /dev/null
+++ b/src/java/org/apache/ivy/core/deliver/DeliverEngine.java
@@ -0,0 +1,214 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.deliver;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.text.ParseException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import org.apache.ivy.core.IvyPatternHelper;
+import org.apache.ivy.core.cache.ResolutionCacheManager;
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.plugins.parser.xml.UpdateOptions;
+import org.apache.ivy.plugins.parser.xml.XmlModuleDescriptorUpdater;
+import org.apache.ivy.plugins.report.XmlReportParser;
+import org.apache.ivy.plugins.repository.Resource;
+import org.apache.ivy.util.ConfigurationUtils;
+import org.apache.ivy.util.Message;
+import org.xml.sax.SAXException;
+
+public class DeliverEngine {
+ private DeliverEngineSettings settings;
+
+ public DeliverEngine(DeliverEngineSettings settings) {
+ this.settings = settings;
+ }
+
+ /**
+ * Delivers a resolved ivy file based upon last resolve call status. If resolve report file
+ * cannot be found in cache, then it throws an IllegalStateException (maybe resolve has not been
+ * called before ?).
+ *
+ * @param revision
+ * the revision to which the module should be delivered
+ * @param destIvyPattern
+ * the pattern to which the delivered ivy file should be written
+ * @param options
+ * the options with which deliver should be done
+ */
+ public void deliver(String revision, String destIvyPattern, DeliverOptions options)
+ throws IOException, ParseException {
+ String resolveId = options.getResolveId();
+ if (resolveId == null) {
+ throw new IllegalArgumentException("A resolveId must be specified for delivering.");
+ }
+ File[] files = getCache().getConfigurationResolveReportsInCache(resolveId);
+ if (files.length == 0) {
+ throw new IllegalStateException("No previous resolve found for id '" + resolveId
+ + "' Please resolve dependencies before delivering.");
+ }
+ XmlReportParser parser = new XmlReportParser();
+ parser.parse(files[0]);
+ ModuleRevisionId mrid = parser.getResolvedModule();
+ deliver(mrid, revision, destIvyPattern, options);
+ }
+
+ private ResolutionCacheManager getCache() {
+ return settings.getResolutionCacheManager();
+ }
+
+ /**
+ * Delivers a resolved ivy file based upon last resolve call status. If resolve report file
+ * cannot be found in cache, then it throws an IllegalStateException (maybe resolve has not been
+ * called before ?).
+ *
+ * @param mrid
+ * the module revision id of the module to deliver
+ * @param revision
+ * the revision to which the module should be delivered
+ * @param destIvyPattern
+ * the pattern to which the delivered ivy file should be written
+ * @param options
+ * the options with which deliver should be done
+ */
+ public void deliver(ModuleRevisionId mrid, String revision, String destIvyPattern,
+ DeliverOptions options) throws IOException, ParseException {
+ Message.info(":: delivering :: " + mrid + " :: " + revision + " :: " + options.getStatus()
+ + " :: " + options.getPubdate());
+ Message.verbose("\toptions = " + options);
+ long start = System.currentTimeMillis();
+ destIvyPattern = settings.substitute(destIvyPattern);
+
+ // 1) find the resolved module descriptor in cache
+ ModuleDescriptor md = getCache().getResolvedModuleDescriptor(mrid);
+ md.setResolvedModuleRevisionId(ModuleRevisionId.newInstance(md.getModuleRevisionId(),
+ options.getPubBranch() == null ? mrid.getBranch() : options.getPubBranch(), revision));
+ md.setResolvedPublicationDate(options.getPubdate());
+
+ // 2) parse resolvedRevisions From properties file
+ Map resolvedRevisions = new HashMap(); // Map (ModuleId -> String revision)
+ Map resolvedBranches = new HashMap(); // Map (ModuleId -> String branch)
+ Map dependenciesStatus = new HashMap(); // Map (ModuleId -> String status)
+ File ivyProperties = getCache().getResolvedIvyPropertiesInCache(mrid);
+ if (!ivyProperties.exists()) {
+ throw new IllegalStateException("ivy properties not found in cache for " + mrid
+ + "; please resolve dependencies before delivering!");
+ }
+ Properties props = new Properties();
+ FileInputStream in = new FileInputStream(ivyProperties);
+ props.load(in);
+ in.close();
+
+ for (Iterator iter = props.keySet().iterator(); iter.hasNext();) {
+ String depMridStr = (String) iter.next();
+ String[] parts = props.getProperty(depMridStr).split(" ");
+ ModuleRevisionId decodedMrid = ModuleRevisionId.decode(depMridStr);
+ if (options.isResolveDynamicRevisions()) {
+ resolvedRevisions.put(decodedMrid, parts[0]);
+ if (parts.length >= 4) {
+ if (parts[3] != null && !"null".equals(parts[3])) {
+ resolvedBranches.put(decodedMrid, parts[3]);
+ }
+ }
+ }
+ dependenciesStatus.put(decodedMrid, parts[1]);
+
+ if (options.isReplaceForcedRevisions()) {
+ if (parts.length <= 2) {
+ // maybe the properties file was generated by an older Ivy version
+ // so it is possible that this part doesn't exist.
+ throw new IllegalStateException("ivy properties file generated by an older"
+ + " version of Ivy which doesn't support replacing forced revisions!");
+ }
+
+ resolvedRevisions.put(decodedMrid, parts[2]);
+ }
+ }
+
+ // 3) use pdrResolver to resolve dependencies info
+ Map resolvedDependencies = new HashMap(); // Map (ModuleRevisionId -> String revision)
+ DependencyDescriptor[] dependencies = md.getDependencies();
+ for (int i = 0; i < dependencies.length; i++) {
+ String rev = (String) resolvedRevisions.get(dependencies[i].getDependencyRevisionId());
+ if (rev == null) {
+ rev = dependencies[i].getDependencyRevisionId().getRevision();
+ }
+ String bra = (String) resolvedBranches.get(dependencies[i].getDependencyRevisionId());
+ if (bra == null || "null".equals(bra)) {
+ bra = dependencies[i].getDependencyRevisionId().getBranch();
+ }
+ String depStatus = (String) dependenciesStatus.get(dependencies[i]
+ .getDependencyRevisionId());
+ ModuleRevisionId mrid2 = null;
+ if (bra == null) {
+ mrid2 = ModuleRevisionId
+ .newInstance(dependencies[i].getDependencyRevisionId(), rev);
+ } else {
+ mrid2 = ModuleRevisionId.newInstance(dependencies[i].getDependencyRevisionId(),
+ bra, rev);
+ }
+ resolvedDependencies.put(dependencies[i].getDependencyRevisionId(), options
+ .getPdrResolver().resolve(md, options.getStatus(), mrid2, depStatus));
+ }
+
+ // 4) copy the source resolved ivy to the destination specified,
+ // updating status, revision and dependency revisions obtained by
+ // PublishingDependencyRevisionResolver
+ File publishedIvy = settings.resolveFile(IvyPatternHelper.substitute(destIvyPattern,
+ md.getResolvedModuleRevisionId()));
+ Message.info("\tdelivering ivy file to " + publishedIvy);
+
+ String[] confs = ConfigurationUtils.replaceWildcards(options.getConfs(), md);
+ Set confsToRemove = new HashSet(Arrays.asList(md.getConfigurationsNames()));
+ confsToRemove.removeAll(Arrays.asList(confs));
+
+ try {
+ UpdateOptions opts = new UpdateOptions()
+ .setSettings(settings)
+ .setResolvedRevisions(resolvedDependencies)
+ .setStatus(options.getStatus())
+ .setRevision(revision)
+ .setBranch(options.getPubBranch())
+ .setPubdate(options.getPubdate())
+ .setGenerateRevConstraint(options.isGenerateRevConstraint())
+ .setMerge(options.isMerge())
+ .setMergedDescriptor(md)
+ .setConfsToExclude(
+ (String[]) confsToRemove.toArray(new String[confsToRemove.size()]));
+ if (!resolvedBranches.isEmpty()) {
+ opts = opts.setResolvedBranches(resolvedBranches);
+ }
+ Resource res = md.getResource();
+ XmlModuleDescriptorUpdater.update(res.openStream(), res, publishedIvy, opts);
+ } catch (SAXException ex) {
+ throw new RuntimeException("bad ivy file in cache for " + mrid, ex);
+ }
+
+ Message.verbose("\tdeliver done (" + (System.currentTimeMillis() - start) + "ms)");
+ }
+}
diff --git a/src/java/org/apache/ivy/core/deliver/DeliverEngineSettings.java b/src/java/org/apache/ivy/core/deliver/DeliverEngineSettings.java
new file mode 100644
index 0000000..2589f33
--- /dev/null
+++ b/src/java/org/apache/ivy/core/deliver/DeliverEngineSettings.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.deliver;
+
+import org.apache.ivy.plugins.parser.ParserSettings;
+
+public interface DeliverEngineSettings extends ParserSettings {
+
+ String substitute(String destIvyPattern);
+
+}
diff --git a/src/java/org/apache/ivy/core/deliver/DeliverOptions.java b/src/java/org/apache/ivy/core/deliver/DeliverOptions.java
new file mode 100644
index 0000000..577e3dc
--- /dev/null
+++ b/src/java/org/apache/ivy/core/deliver/DeliverOptions.java
@@ -0,0 +1,270 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.deliver;
+
+import java.util.Date;
+
+import org.apache.ivy.core.settings.IvySettings;
+
+/**
+ * A set of options used to do a deliver.
+ */
+public class DeliverOptions {
+ private String status;
+
+ private Date pubdate;
+
+ private PublishingDependencyRevisionResolver pdrResolver = new DefaultPublishingDRResolver();
+
+ private boolean validate = true;
+
+ private boolean resolveDynamicRevisions = true;
+
+ private boolean replaceForcedRevisions = false;
+
+ private String resolveId;
+
+ private String[] confs;
+
+ private String pubBranch;
+
+ /**
+ * True to indicate that the revConstraint attribute should be generated if applicable, false to
+ * never generate the revConstraint attribute.
+ */
+ private boolean generateRevConstraint = true;
+
+ /** true to merge parent descriptor elements into delivered child descriptor */
+ private boolean merge = true;
+
+ /**
+ * Returns an instance of DeliverOptions with options corresponding to default values taken from
+ * the given settings.
+ *
+ * @param settings
+ * The settings to use to get default option values
+ * @return a DeliverOptions instance ready to be used or customized
+ */
+ public static DeliverOptions newInstance(IvySettings settings) {
+ return new DeliverOptions(null, new Date(), new DefaultPublishingDRResolver(),
+ settings.doValidate(), true, null);
+ }
+
+ /**
+ * Creates an instance of DeliverOptions which require to be configured using the appropriate
+ * setters.
+ */
+ public DeliverOptions() {
+ }
+
+ /**
+ * Creates an instance of DeliverOptions with all options explicitly set.
+ */
+ public DeliverOptions(String status, Date pubDate,
+ PublishingDependencyRevisionResolver pdrResolver, boolean validate,
+ boolean resolveDynamicRevisions, String[] confs) {
+ this.status = status;
+ this.pubdate = pubDate;
+ this.pdrResolver = pdrResolver;
+ this.validate = validate;
+ this.resolveDynamicRevisions = resolveDynamicRevisions;
+ this.confs = confs;
+ }
+
+ /**
+ * Return the pdrResolver that will be used during deliver for each dependency to get its
+ * published information. This can particularly useful when the deliver is made for a release,
+ * and when we wish to deliver each dependency which is still in integration. The
+ * PublishingDependencyRevisionResolver can then do the delivering work for the dependency and
+ * return the new (delivered) dependency info (with the delivered revision). Note that
+ * PublishingDependencyRevisionResolver is only called for each <b>direct</b> dependency.
+ *
+ * @return the pdrResolver that will be used during deliver
+ */
+ public PublishingDependencyRevisionResolver getPdrResolver() {
+ return pdrResolver;
+ }
+
+ /**
+ * Sets the pdrResolver that will be used during deliver for each dependency to get its
+ * published information. This can particularly useful when the deliver is made for a release,
+ * and when we wish to deliver each dependency which is still in integration. The
+ * PublishingDependencyRevisionResolver can then do the delivering work for the dependency and
+ * return the new (delivered) dependency info (with the delivered revision). Note that
+ * PublishingDependencyRevisionResolver is only called for each <b>direct</b> dependency.
+ *
+ * @return the instance of DeliverOptions on which the method has been called, for easy method
+ * chaining
+ */
+ public DeliverOptions setPdrResolver(PublishingDependencyRevisionResolver pdrResolver) {
+ this.pdrResolver = pdrResolver;
+ return this;
+ }
+
+ public boolean isResolveDynamicRevisions() {
+ return resolveDynamicRevisions;
+ }
+
+ public DeliverOptions setResolveDynamicRevisions(boolean resolveDynamicRevisions) {
+ this.resolveDynamicRevisions = resolveDynamicRevisions;
+ return this;
+ }
+
+ public boolean isReplaceForcedRevisions() {
+ return replaceForcedRevisions;
+ }
+
+ public DeliverOptions setReplaceForcedRevisions(boolean replaceForcedRevisions) {
+ this.replaceForcedRevisions = replaceForcedRevisions;
+ return this;
+ }
+
+ public boolean isValidate() {
+ return validate;
+ }
+
+ public DeliverOptions setValidate(boolean validate) {
+ this.validate = validate;
+ return this;
+ }
+
+ public Date getPubdate() {
+ return pubdate;
+ }
+
+ public DeliverOptions setPubdate(Date pubdate) {
+ this.pubdate = pubdate;
+ return this;
+ }
+
+ /**
+ * Returns the status to which the module should be delivered, or null if the current status
+ * should be kept.
+ *
+ * @return the status to which the module should be delivered
+ */
+ public String getStatus() {
+ return status;
+ }
+
+ /**
+ * Sets the status to which the module should be delivered, use null if the current status
+ * should be kept.
+ *
+ * @return the instance of DeliverOptions on which the method has been called, for easy method
+ * chaining
+ */
+ public DeliverOptions setStatus(String status) {
+ this.status = status;
+ return this;
+ }
+
+ /**
+ * Returns the id of a previous resolve to use for delivering.
+ *
+ * @return the id of a previous resolve
+ */
+ public String getResolveId() {
+ return resolveId;
+ }
+
+ /**
+ * Sets the id of a previous resolve to use for delivering.
+ *
+ * @param resolveId
+ * the id of a previous resolve
+ * @return the instance of DeliverOptions on which the method has been called, for easy method
+ * chaining
+ */
+ public DeliverOptions setResolveId(String resolveId) {
+ this.resolveId = resolveId;
+ return this;
+ }
+
+ /**
+ * Return the configurations which must be deliverd. Returns <tt>null</tt> if all configurations
+ * has to be deliverd. Attention: the returned array can contain wildcards!
+ *
+ * @return the configurations to deliver
+ */
+ public String[] getConfs() {
+ return confs;
+ }
+
+ /**
+ * Sets the configurations to deliver.
+ *
+ * @param confs
+ * the configurations to deliver
+ * @return the instance of DeliverOptions on which the method has been called, for easy method
+ * chaining
+ */
+ public DeliverOptions setConfs(String[] confs) {
+ this.confs = confs;
+ return this;
+ }
+
+ /**
+ * Returns the branch with which the Ivy file should be delivered, or <code>null</code> if
+ * branch info shouldn't be changed.
+ *
+ * @return the branch with which the Ivy file should be delivered
+ */
+ public String getPubBranch() {
+ return pubBranch;
+ }
+
+ /**
+ * Sets the branch with which the Ivy file should be delivered.
+ *
+ * @param pubBranch
+ * the branch with which the Ivy file should be delivered
+ * @return the instance of DeliverOptions on which the method has been called, for easy method
+ * chaining
+ */
+ public DeliverOptions setPubBranch(String pubBranch) {
+ this.pubBranch = pubBranch;
+ return this;
+ }
+
+ public boolean isGenerateRevConstraint() {
+ return generateRevConstraint;
+ }
+
+ public DeliverOptions setGenerateRevConstraint(boolean generateRevConstraint) {
+ this.generateRevConstraint = generateRevConstraint;
+ return this;
+ }
+
+ public boolean isMerge() {
+ return merge;
+ }
+
+ public DeliverOptions setMerge(boolean merge) {
+ this.merge = merge;
+ return this;
+ }
+
+ public String toString() {
+ return "status=" + status + " pubdate=" + pubdate + " validate=" + validate
+ + " resolveDynamicRevisions=" + resolveDynamicRevisions + " merge=" + merge
+ + " resolveId=" + resolveId + " pubBranch=" + pubBranch;
+
+ }
+
+}
diff --git a/src/java/org/apache/ivy/core/deliver/PublishingDependencyRevisionResolver.java b/src/java/org/apache/ivy/core/deliver/PublishingDependencyRevisionResolver.java
new file mode 100644
index 0000000..aa632e3
--- /dev/null
+++ b/src/java/org/apache/ivy/core/deliver/PublishingDependencyRevisionResolver.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.deliver;
+
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+
+/**
+ *
+ */
+public interface PublishingDependencyRevisionResolver {
+
+ /**
+ * Returns the revision of the dependency for the publishing of the 'published' module in
+ * 'publishedStatus' status.
+ *
+ * @param published
+ * @param publishedStatus
+ * @param depMrid
+ * @param status
+ * @return the revision of the dependency
+ */
+ String resolve(ModuleDescriptor published, String publishedStatus, ModuleRevisionId depMrid,
+ String status);
+
+}
diff --git a/src/java/org/apache/ivy/core/event/EventManager.java b/src/java/org/apache/ivy/core/event/EventManager.java
new file mode 100644
index 0000000..743b7d4
--- /dev/null
+++ b/src/java/org/apache/ivy/core/event/EventManager.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.event;
+
+import java.util.Arrays;
+
+import javax.swing.event.EventListenerList;
+
+import org.apache.ivy.plugins.repository.TransferEvent;
+import org.apache.ivy.plugins.repository.TransferListener;
+import org.apache.ivy.util.filter.Filter;
+
+public class EventManager implements TransferListener {
+
+ private EventListenerList listeners = new EventListenerList();
+
+ public void addIvyListener(IvyListener listener) {
+ listeners.add(IvyListener.class, listener);
+ }
+
+ public void addIvyListener(IvyListener listener, String eventName) {
+ addIvyListener(listener, new IvyEventFilter(eventName, null, null));
+ }
+
+ public void addIvyListener(IvyListener listener, Filter filter) {
+ listeners.add(IvyListener.class, new FilteredIvyListener(listener, filter));
+ }
+
+ public void removeIvyListener(IvyListener listener) {
+ listeners.remove(IvyListener.class, listener);
+ IvyListener[] listeners = this.listeners.getListeners(IvyListener.class);
+ for (int i = 0; i < listeners.length; i++) {
+ if (listeners[i] instanceof FilteredIvyListener) {
+ if (listener.equals(((FilteredIvyListener) listeners[i]).getIvyListener())) {
+ this.listeners.remove(IvyListener.class, listeners[i]);
+ }
+ }
+ }
+ }
+
+ public boolean hasIvyListener(IvyListener listener) {
+ return Arrays.asList(listeners.getListeners(IvyListener.class)).contains(listener);
+ }
+
+ public void fireIvyEvent(IvyEvent evt) {
+ Object[] listeners = this.listeners.getListenerList();
+ for (int i = listeners.length - 2; i >= 0; i -= 2) {
+ if (listeners[i] == IvyListener.class) {
+ ((IvyListener) listeners[i + 1]).progress(evt);
+ }
+ }
+ }
+
+ public void addTransferListener(TransferListener listener) {
+ listeners.add(TransferListener.class, listener);
+ }
+
+ public void removeTransferListener(TransferListener listener) {
+ listeners.remove(TransferListener.class, listener);
+ }
+
+ public boolean hasTransferListener(TransferListener listener) {
+ return Arrays.asList(listeners.getListeners(TransferListener.class)).contains(listener);
+ }
+
+ protected void fireTransferEvent(TransferEvent evt) {
+ Object[] listeners = this.listeners.getListenerList();
+ for (int i = listeners.length - 2; i >= 0; i -= 2) {
+ if (listeners[i] == TransferListener.class) {
+ ((TransferListener) listeners[i + 1]).transferProgress(evt);
+ }
+ }
+ }
+
+ public void transferProgress(TransferEvent evt) {
+ fireTransferEvent(evt);
+ fireIvyEvent(evt);
+ }
+
+}
diff --git a/src/java/org/apache/ivy/core/event/FilteredIvyListener.java b/src/java/org/apache/ivy/core/event/FilteredIvyListener.java
new file mode 100644
index 0000000..e52512d
--- /dev/null
+++ b/src/java/org/apache/ivy/core/event/FilteredIvyListener.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.event;
+
+import org.apache.ivy.util.filter.Filter;
+
+public class FilteredIvyListener implements IvyListener {
+ private IvyListener listener;
+
+ private Filter filter;
+
+ public FilteredIvyListener(IvyListener listener, Filter filter) {
+ this.listener = listener;
+ this.filter = filter;
+ }
+
+ public IvyListener getIvyListener() {
+ return listener;
+ }
+
+ public Filter getFilter() {
+ return filter;
+ }
+
+ public void progress(IvyEvent event) {
+ if (filter.accept(event)) {
+ listener.progress(event);
+ }
+ }
+
+}
diff --git a/src/java/org/apache/ivy/core/event/IvyEvent.java b/src/java/org/apache/ivy/core/event/IvyEvent.java
new file mode 100644
index 0000000..b6b938f
--- /dev/null
+++ b/src/java/org/apache/ivy/core/event/IvyEvent.java
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.event;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.ivy.core.IvyContext;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleId;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.util.StringUtils;
+
+/**
+ * The root of all ivy events Any ivy event knows which ivy instance triggered the event (the
+ * source) and also has a name and a map of attributes. The name of the event represents the event
+ * type, usually there is a one - one mapping between event names and IvyEvent subclass, even if
+ * this is not mandatory. Example: pre-resolve pre-resolve-dependency post-download The map of
+ * attributes is a Map from String keys to String values. It is especially useful to filter events,
+ * and to get some of their essential data in some context where access to Java types is not easy
+ * (in an ant build file, for example), Example: pre-resolve (organisation=foo, module=bar,
+ * revision=1.0, conf=default) post-download (organisation=foo, module=bar, revision=1.0,
+ * artifact=foo-test, type=jar, ext=jar)
+ */
+public class IvyEvent {
+ private EventManager source;
+
+ private String name;
+
+ private Map attributes = new HashMap();
+
+ protected IvyEvent(String name) {
+ this.source = IvyContext.getContext().getEventManager();
+ this.name = name;
+ }
+
+ /**
+ * Should only be called during event object construction, since events should be immutable
+ *
+ * @param key
+ * @param value
+ */
+ protected void addAttribute(String key, String value) {
+ attributes.put(key, value);
+ }
+
+ protected void addMDAttributes(ModuleDescriptor md) {
+ addMridAttributes(md.getResolvedModuleRevisionId());
+ }
+
+ protected void addMridAttributes(ModuleRevisionId mrid) {
+ addModuleIdAttributes(mrid.getModuleId());
+ addAttribute("revision", mrid.getRevision());
+ addAttribute("branch", mrid.getBranch());
+ addAttributes(mrid.getQualifiedExtraAttributes());
+ addAttributes(mrid.getExtraAttributes());
+ }
+
+ protected void addModuleIdAttributes(ModuleId moduleId) {
+ addAttribute("organisation", moduleId.getOrganisation());
+ addAttribute("module", moduleId.getName());
+ }
+
+ protected void addConfsAttribute(String[] confs) {
+ addAttribute("conf", StringUtils.join(confs, ", "));
+ }
+
+ protected void addAttributes(Map attributes) {
+ this.attributes.putAll(attributes);
+ }
+
+ public EventManager getSource() {
+ return source;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Returns the attributes of this event, as a Map(String->String)
+ *
+ * @return the attributes of this event, as a Map(String->String)
+ */
+ public Map getAttributes() {
+ return new HashMap(attributes);
+ }
+
+ public String toString() {
+ return getName() + " " + getAttributes();
+ }
+
+ public boolean equals(Object obj) {
+ if (!(obj instanceof IvyEvent)) {
+ return false;
+ }
+ IvyEvent e = (IvyEvent) obj;
+
+ return getSource().equals(e.getSource()) && getName().equals(e.getName())
+ && attributes.equals(e.attributes);
+ }
+
+ public int hashCode() {
+ // CheckStyle:MagicNumber| OFF
+ int hash = 37;
+ hash = 13 * hash + getSource().hashCode();
+ hash = 13 * hash + getName().hashCode();
+ hash = 13 * hash + attributes.hashCode();
+ // CheckStyle:MagicNumber| ON
+ return hash;
+ }
+}
diff --git a/src/java/org/apache/ivy/core/event/IvyEventFilter.java b/src/java/org/apache/ivy/core/event/IvyEventFilter.java
new file mode 100644
index 0000000..d47059f
--- /dev/null
+++ b/src/java/org/apache/ivy/core/event/IvyEventFilter.java
@@ -0,0 +1,159 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.event;
+
+import org.apache.ivy.plugins.matcher.ExactPatternMatcher;
+import org.apache.ivy.plugins.matcher.Matcher;
+import org.apache.ivy.plugins.matcher.PatternMatcher;
+import org.apache.ivy.util.filter.AndFilter;
+import org.apache.ivy.util.filter.Filter;
+import org.apache.ivy.util.filter.NoFilter;
+import org.apache.ivy.util.filter.NotFilter;
+import org.apache.ivy.util.filter.OrFilter;
+
+/**
+ * A filter implementation filtering {@link IvyEvent} based upon an event name and a filter
+ * expression. The name will be matched against the event name using the {@link PatternMatcher} used
+ * to construct this object. The filter expression is a string describing how the event should be
+ * filtered according to its attributes values. The matching between the filter values and the event
+ * attribute values is done using the {@link PatternMatcher} used to construct this object. Here are
+ * some examples:
+ * <table>
+ * <tr>
+ * <td>expression</td>
+ * <td>effect</td>
+ * </tr>
+ * <tr>
+ * <td>type=zip</td>
+ * <td>accepts event with a type attribute matching zip</td>
+ * </tr>
+ * <tr>
+ * <td>type=zip,jar</td>
+ * <td>accepts event with a type attribute matching zip or jar</td>
+ * </tr>
+ * <tr>
+ * <td>type=src AND ext=zip</td>
+ * <td>accepts event with a type attribute matching src AND an ext attribute matching zip</td>
+ * </tr>
+ * <tr>
+ * <td>type=src OR ext=zip</td>
+ * <td>accepts event with a type attribute matching src OR an ext attribute matching zip</td>
+ * </tr>
+ * <tr>
+ * <td>NOT type=src</td>
+ * <td>accepts event with a type attribute NOT matching src</td>
+ * </tr>
+ * </table>
+ * Combination of these can be used, but no parentheses are supported right now, so only the default
+ * priority can be used. The priority order is this one: AND OR NOT = This means that artifact=foo
+ * AND ext=zip OR type=src will match event with artifact matching foo AND (ext matching zip OR type
+ * matching src)
+ *
+ * @since 1.4
+ */
+public class IvyEventFilter implements Filter {
+ private static final String NOT = "NOT ";
+
+ private static final String OR = " OR ";
+
+ private static final String AND = " AND ";
+
+ private PatternMatcher matcher;
+
+ private Filter nameFilter;
+
+ private Filter attFilter;
+
+ public IvyEventFilter(String event, String filterExpression, PatternMatcher matcher) {
+ this.matcher = matcher == null ? ExactPatternMatcher.INSTANCE : matcher;
+ if (event == null) {
+ nameFilter = NoFilter.INSTANCE;
+ } else {
+ final Matcher eventNameMatcher = this.matcher.getMatcher(event);
+ nameFilter = new Filter() {
+ public boolean accept(Object o) {
+ IvyEvent e = (IvyEvent) o;
+ return eventNameMatcher.matches(e.getName());
+ }
+ };
+ }
+ attFilter = filterExpression == null || filterExpression.trim().length() == 0 ? NoFilter.INSTANCE
+ : parseExpression(filterExpression);
+ }
+
+ private Filter parseExpression(String filterExpression) {
+ // expressions handled for the moment: (informal grammar)
+ // EXP := SIMPLE_EXP | AND_EXP | OR_EXP | NOT_EXP
+ // AND_EXP := EXP && EXP
+ // OR_EXP := EXP || EXP
+ // NOT_EXP := ! EXP
+ // SIMPLE_EXP := attname = comma, separated, list, of, accepted, values
+ // example: organisation = foo && module = bar, baz
+ filterExpression = filterExpression.trim();
+ int index = filterExpression.indexOf(AND);
+ if (index == -1) {
+ index = filterExpression.indexOf(OR);
+ if (index == -1) {
+ if (filterExpression.startsWith(NOT)) {
+ return new NotFilter(parseExpression(filterExpression.substring(NOT.length())));
+ } else {
+ index = filterExpression.indexOf("=");
+ if (index == -1) {
+ throw new IllegalArgumentException("bad filter expression: "
+ + filterExpression + ": no equal sign found");
+ }
+ final String attname = filterExpression.substring(0, index).trim();
+ String[] values = filterExpression.substring(index + 1).trim().split(",");
+ final Matcher[] matchers = new Matcher[values.length];
+ for (int i = 0; i < values.length; i++) {
+ matchers[i] = matcher.getMatcher(values[i].trim());
+ }
+ return new Filter() {
+ public boolean accept(Object o) {
+ IvyEvent e = (IvyEvent) o;
+ String val = (String) e.getAttributes().get(attname);
+ if (val == null) {
+ return false;
+ }
+ for (int i = 0; i < matchers.length; i++) {
+ if (matchers[i].matches(val)) {
+ return true;
+ }
+ }
+ return false;
+ }
+ };
+ }
+ } else {
+ return new OrFilter(parseExpression(filterExpression.substring(0, index)),
+ parseExpression(filterExpression.substring(index + OR.length())));
+ }
+ } else {
+ return new AndFilter(parseExpression(filterExpression.substring(0, index)),
+ parseExpression(filterExpression.substring(index + AND.length())));
+ }
+ }
+
+ public boolean accept(Object o) {
+ if (!(o instanceof IvyEvent)) {
+ return false;
+ }
+ return nameFilter.accept(o) && attFilter.accept(o);
+ }
+
+}
diff --git a/src/java/org/apache/ivy/core/event/IvyListener.java b/src/java/org/apache/ivy/core/event/IvyListener.java
new file mode 100644
index 0000000..881b3cd
--- /dev/null
+++ b/src/java/org/apache/ivy/core/event/IvyListener.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.event;
+
+import java.util.EventListener;
+
+public interface IvyListener extends EventListener {
+ public void progress(IvyEvent event);
+}
diff --git a/src/java/org/apache/ivy/core/event/download/DownloadEvent.java b/src/java/org/apache/ivy/core/event/download/DownloadEvent.java
new file mode 100644
index 0000000..5b160a3
--- /dev/null
+++ b/src/java/org/apache/ivy/core/event/download/DownloadEvent.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.event.download;
+
+import org.apache.ivy.core.event.IvyEvent;
+import org.apache.ivy.core.module.descriptor.Artifact;
+
+public abstract class DownloadEvent extends IvyEvent {
+ private Artifact artifact;
+
+ public DownloadEvent(String name, Artifact artifact) {
+ super(name);
+ this.artifact = artifact;
+ addArtifactAttributes(this.artifact);
+ }
+
+ protected void addArtifactAttributes(Artifact artifact) {
+ addMridAttributes(artifact.getModuleRevisionId());
+ addAttributes(artifact.getAttributes());
+ addAttribute("metadata", String.valueOf(artifact.isMetadata()));
+ }
+
+ public Artifact getArtifact() {
+ return artifact;
+ }
+
+}
diff --git a/src/java/org/apache/ivy/core/event/download/EndArtifactDownloadEvent.java b/src/java/org/apache/ivy/core/event/download/EndArtifactDownloadEvent.java
new file mode 100644
index 0000000..c22d4b4
--- /dev/null
+++ b/src/java/org/apache/ivy/core/event/download/EndArtifactDownloadEvent.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.event.download;
+
+import java.io.File;
+
+import org.apache.ivy.core.cache.ArtifactOrigin;
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.report.ArtifactDownloadReport;
+import org.apache.ivy.plugins.resolver.DependencyResolver;
+
+public class EndArtifactDownloadEvent extends DownloadEvent {
+ public static final String NAME = "post-download-artifact";
+
+ private DependencyResolver resolver;
+
+ private ArtifactDownloadReport report;
+
+ public EndArtifactDownloadEvent(DependencyResolver resolver, Artifact artifact,
+ ArtifactDownloadReport report, File dest) {
+ super(NAME, artifact);
+ this.resolver = resolver;
+ this.report = report;
+ addAttribute("resolver", this.resolver.getName());
+ addAttribute("status", this.report.getDownloadStatus().toString());
+ addAttribute("details", this.report.getDownloadDetails());
+ addAttribute("size", String.valueOf(this.report.getSize()));
+ addAttribute("file", dest.getAbsolutePath());
+ addAttribute("duration", String.valueOf(this.report.getDownloadTimeMillis()));
+ ArtifactOrigin origin = report.getArtifactOrigin();
+ if (!ArtifactOrigin.isUnknown(origin)) {
+ addAttribute("origin", origin.getLocation());
+ addAttribute("local", String.valueOf(origin.isLocal()));
+ } else {
+ addAttribute("origin", "");
+ addAttribute("local", "");
+ }
+ }
+
+ public ArtifactDownloadReport getReport() {
+ return report;
+ }
+
+ public DependencyResolver getResolver() {
+ return resolver;
+ }
+
+}
diff --git a/src/java/org/apache/ivy/core/event/download/NeedArtifactEvent.java b/src/java/org/apache/ivy/core/event/download/NeedArtifactEvent.java
new file mode 100644
index 0000000..a1a9f2e
--- /dev/null
+++ b/src/java/org/apache/ivy/core/event/download/NeedArtifactEvent.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.event.download;
+
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.plugins.resolver.DependencyResolver;
+
+public class NeedArtifactEvent extends DownloadEvent {
+ public static final String NAME = "need-artifact";
+
+ private DependencyResolver resolver;
+
+ public NeedArtifactEvent(DependencyResolver resolver, Artifact artifact) {
+ super(NAME, artifact);
+ this.resolver = resolver;
+ addAttribute("resolver", this.resolver.getName());
+ }
+
+ public DependencyResolver getResolver() {
+ return resolver;
+ }
+
+}
diff --git a/src/java/org/apache/ivy/core/event/download/PrepareDownloadEvent.java b/src/java/org/apache/ivy/core/event/download/PrepareDownloadEvent.java
new file mode 100644
index 0000000..a5df433
--- /dev/null
+++ b/src/java/org/apache/ivy/core/event/download/PrepareDownloadEvent.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.event.download;
+
+import org.apache.ivy.core.event.IvyEvent;
+import org.apache.ivy.core.module.descriptor.Artifact;
+
+public class PrepareDownloadEvent extends IvyEvent {
+ public static final String NAME = "prepare-download";
+
+ private Artifact[] artifacts;
+
+ public PrepareDownloadEvent(Artifact[] artifacts) {
+ super(NAME);
+ this.artifacts = artifacts;
+ }
+
+ public Artifact[] getArtifacts() {
+ return artifacts;
+ }
+}
diff --git a/src/java/org/apache/ivy/core/event/download/StartArtifactDownloadEvent.java b/src/java/org/apache/ivy/core/event/download/StartArtifactDownloadEvent.java
new file mode 100644
index 0000000..a30d67c
--- /dev/null
+++ b/src/java/org/apache/ivy/core/event/download/StartArtifactDownloadEvent.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.event.download;
+
+import org.apache.ivy.core.cache.ArtifactOrigin;
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.plugins.resolver.DependencyResolver;
+
+public class StartArtifactDownloadEvent extends DownloadEvent {
+ public static final String NAME = "pre-download-artifact";
+
+ private DependencyResolver resolver;
+
+ private ArtifactOrigin origin;
+
+ public StartArtifactDownloadEvent(DependencyResolver resolver, Artifact artifact,
+ ArtifactOrigin origin) {
+ super(NAME, artifact);
+ this.resolver = resolver;
+ this.origin = origin;
+ addAttribute("resolver", this.resolver.getName());
+ addAttribute("origin", origin.getLocation());
+ addAttribute("local", String.valueOf(origin.isLocal()));
+ }
+
+ public DependencyResolver getResolver() {
+ return resolver;
+ }
+
+ public ArtifactOrigin getOrigin() {
+ return origin;
+ }
+
+}
diff --git a/src/java/org/apache/ivy/core/event/publish/EndArtifactPublishEvent.java b/src/java/org/apache/ivy/core/event/publish/EndArtifactPublishEvent.java
new file mode 100644
index 0000000..36c98ce
--- /dev/null
+++ b/src/java/org/apache/ivy/core/event/publish/EndArtifactPublishEvent.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.event.publish;
+
+import java.io.File;
+
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.plugins.resolver.DependencyResolver;
+
+/**
+ * Event fired after artifact publication has finished (possibly in error). Triggers registered on
+ * {@link #NAME} will be notified of these events.
+ *
+ * @see DependencyResolver#publish(Artifact, File, boolean)
+ */
+public class EndArtifactPublishEvent extends PublishEvent {
+
+ private static final long serialVersionUID = -65690169431499422L;
+
+ public static final String NAME = "post-publish-artifact";
+
+ public static final String STATUS_SUCCESSFUL = "successful";
+
+ public static final String STATUS_FAILED = "failed";
+
+ private final boolean successful;
+
+ public EndArtifactPublishEvent(DependencyResolver resolver, Artifact artifact, File data,
+ boolean overwrite, boolean successful) {
+ super(NAME, resolver, artifact, data, overwrite);
+ this.successful = successful;
+ addAttribute("status", isSuccessful() ? STATUS_SUCCESSFUL : STATUS_FAILED);
+ }
+
+ /**
+ * @return true iff no errors were encountered during the publication
+ */
+ public boolean isSuccessful() {
+ return successful;
+ }
+}
diff --git a/src/java/org/apache/ivy/core/event/publish/PublishEvent.java b/src/java/org/apache/ivy/core/event/publish/PublishEvent.java
new file mode 100644
index 0000000..72ec32a
--- /dev/null
+++ b/src/java/org/apache/ivy/core/event/publish/PublishEvent.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.event.publish;
+
+import java.io.File;
+
+import org.apache.ivy.core.event.IvyEvent;
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.plugins.resolver.DependencyResolver;
+
+/**
+ * Base class for events fired during {@link DependencyResolver#publish(Artifact, File, boolean)}.
+ *
+ * @see StartArtifactPublishEvent
+ * @see EndArtifactPublishEvent
+ */
+public abstract class PublishEvent extends IvyEvent {
+
+ private final DependencyResolver resolver;
+
+ private final Artifact artifact;
+
+ private final File data;
+
+ private final boolean overwrite;
+
+ protected PublishEvent(String name, DependencyResolver resolver, Artifact artifact, File data,
+ boolean overwrite) {
+ super(name);
+ this.resolver = resolver;
+ this.artifact = artifact;
+ this.data = data;
+ this.overwrite = overwrite;
+
+ addMridAttributes(artifact.getModuleRevisionId());
+ addAttributes(artifact.getAttributes());
+ addAttribute("resolver", resolver.getName());
+ addAttribute("file", data.getAbsolutePath());
+ addAttribute("overwrite", String.valueOf(overwrite));
+ }
+
+ /** @return the resolver into which the artifact is being published */
+ public DependencyResolver getResolver() {
+ return resolver;
+ }
+
+ /** @return a local file containing the artifact data */
+ public File getData() {
+ return data;
+ }
+
+ /** @return metadata about the artifact being published */
+ public Artifact getArtifact() {
+ return artifact;
+ }
+
+ /** @return true iff this event overwrites existing resolver data for this artifact */
+ public boolean isOverwrite() {
+ return overwrite;
+ }
+
+}
diff --git a/src/java/org/apache/ivy/core/event/publish/StartArtifactPublishEvent.java b/src/java/org/apache/ivy/core/event/publish/StartArtifactPublishEvent.java
new file mode 100644
index 0000000..2682eb5
--- /dev/null
+++ b/src/java/org/apache/ivy/core/event/publish/StartArtifactPublishEvent.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.event.publish;
+
+import java.io.File;
+
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.plugins.resolver.DependencyResolver;
+
+/**
+ * Event fired just before an artifact is published into a resolver. Triggers registered on
+ * {@link #NAME} will be notified of these events.
+ *
+ * @see DependencyResolver#publish(Artifact, File, boolean)
+ */
+public class StartArtifactPublishEvent extends PublishEvent {
+
+ private static final long serialVersionUID = -1134274781039590219L;
+
+ public static final String NAME = "pre-publish-artifact";
+
+ public StartArtifactPublishEvent(DependencyResolver resolver, Artifact artifact, File data,
+ boolean overwrite) {
+ super(NAME, resolver, artifact, data, overwrite);
+ }
+
+}
diff --git a/src/java/org/apache/ivy/core/event/resolve/EndResolveDependencyEvent.java b/src/java/org/apache/ivy/core/event/resolve/EndResolveDependencyEvent.java
new file mode 100644
index 0000000..3f01d10
--- /dev/null
+++ b/src/java/org/apache/ivy/core/event/resolve/EndResolveDependencyEvent.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.event.resolve;
+
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.resolve.ResolvedModuleRevision;
+import org.apache.ivy.plugins.resolver.DependencyResolver;
+
+public class EndResolveDependencyEvent extends ResolveDependencyEvent {
+ public static final String NAME = "post-resolve-dependency";
+
+ private ResolvedModuleRevision module;
+
+ private long duration;
+
+ public EndResolveDependencyEvent(DependencyResolver resolver, DependencyDescriptor dd,
+ ModuleRevisionId requestedRevisionId, ResolvedModuleRevision module, long duration) {
+ super(NAME, resolver, dd, requestedRevisionId);
+ this.module = module;
+ this.duration = duration;
+ addAttribute("duration", String.valueOf(duration));
+ if (this.module != null) {
+ // override revision from the dependency descriptor
+ addAttribute("revision", this.module.getDescriptor().getResolvedModuleRevisionId()
+ .getRevision());
+ // now that we have loaded the dependency descriptor, we can put the extra attributes
+ // contained in the descriptor too
+ addAttributes(this.module.getDescriptor().getResolvedModuleRevisionId()
+ .getQualifiedExtraAttributes());
+ addAttributes(this.module.getDescriptor().getResolvedModuleRevisionId()
+ .getExtraAttributes());
+
+ addAttribute("resolved", "true");
+ } else {
+ addAttribute("resolved", "false");
+ }
+ }
+
+ public ResolvedModuleRevision getModule() {
+ return module;
+ }
+
+ /**
+ * Returns the time elapsed to resolve the dependency.
+ * <p>
+ * The time elapsed to resolve a dependency includes the time required to locate the the actual
+ * revision if the dependency descriptor use a version constraint, and to download the module
+ * metadata if necessary. It doesn't include any conflict management operations nor transitive
+ * dependency management. It's basically the time elapsed since the corresponding
+ * {@link StartResolveDependencyEvent}
+ * </p>
+ *
+ * @return the time elapsed to resolve the dependency.
+ */
+ public long getDuration() {
+ return duration;
+ }
+
+}
diff --git a/src/java/org/apache/ivy/core/event/resolve/EndResolveEvent.java b/src/java/org/apache/ivy/core/event/resolve/EndResolveEvent.java
new file mode 100644
index 0000000..7ea6775
--- /dev/null
+++ b/src/java/org/apache/ivy/core/event/resolve/EndResolveEvent.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.event.resolve;
+
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.report.ResolveReport;
+
+public class EndResolveEvent extends ResolveEvent {
+ public static final String NAME = "post-resolve";
+
+ private ResolveReport report;
+
+ public EndResolveEvent(ModuleDescriptor md, String[] confs, ResolveReport report) {
+ super(NAME, md, confs);
+ this.report = report;
+ addAttribute("resolve-id", String.valueOf(report.getResolveId()));
+ addAttribute("nb-dependencies", String.valueOf(report.getDependencies().size()));
+ addAttribute("nb-artifacts", String.valueOf(report.getArtifacts().size()));
+ addAttribute("resolve-duration", String.valueOf(report.getResolveTime()));
+ addAttribute("download-duration", String.valueOf(report.getDownloadTime()));
+ addAttribute("download-size", String.valueOf(report.getDownloadSize()));
+ }
+
+ public ResolveReport getReport() {
+ return report;
+ }
+
+}
diff --git a/src/java/org/apache/ivy/core/event/resolve/ResolveDependencyEvent.java b/src/java/org/apache/ivy/core/event/resolve/ResolveDependencyEvent.java
new file mode 100644
index 0000000..97af58c
--- /dev/null
+++ b/src/java/org/apache/ivy/core/event/resolve/ResolveDependencyEvent.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.event.resolve;
+
+import org.apache.ivy.core.event.IvyEvent;
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.plugins.resolver.DependencyResolver;
+
+public class ResolveDependencyEvent extends IvyEvent {
+ private DependencyResolver resolver;
+
+ private DependencyDescriptor dd;
+
+ protected ResolveDependencyEvent(String name, DependencyResolver resolver,
+ DependencyDescriptor dd, ModuleRevisionId requestedRevisionId) {
+ super(name);
+ this.resolver = resolver;
+ this.dd = dd;
+ addAttribute("resolver", this.resolver.getName());
+ addMridAttributes(this.dd.getDependencyRevisionId());
+ addAttributes(this.dd.getQualifiedExtraAttributes());
+ addAttributes(this.dd.getExtraAttributes());
+ addAttribute("req-revision", requestedRevisionId.getRevision());
+ addAttribute("req-revision-default", dd.getDependencyRevisionId().getRevision());
+ addAttribute("req-revision-dynamic", dd.getDynamicConstraintDependencyRevisionId()
+ .getRevision());
+ addAttribute("req-branch", requestedRevisionId.getBranch());
+ addAttribute("req-branch-default", dd.getDependencyRevisionId().getBranch());
+ }
+
+ public DependencyDescriptor getDependencyDescriptor() {
+ return dd;
+ }
+
+ public DependencyResolver getResolver() {
+ return resolver;
+ }
+
+}
diff --git a/src/java/org/apache/ivy/core/event/resolve/ResolveEvent.java b/src/java/org/apache/ivy/core/event/resolve/ResolveEvent.java
new file mode 100644
index 0000000..140cf3d
--- /dev/null
+++ b/src/java/org/apache/ivy/core/event/resolve/ResolveEvent.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.event.resolve;
+
+import org.apache.ivy.core.event.IvyEvent;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+
+public class ResolveEvent extends IvyEvent {
+ private ModuleDescriptor md;
+
+ protected ResolveEvent(String name, ModuleDescriptor md, String[] confs) {
+ super(name);
+ this.md = md;
+ addMDAttributes(md);
+ addConfsAttribute(confs);
+ }
+
+ public ModuleDescriptor getModuleDescriptor() {
+ return md;
+ }
+}
diff --git a/src/java/org/apache/ivy/core/event/resolve/StartResolveDependencyEvent.java b/src/java/org/apache/ivy/core/event/resolve/StartResolveDependencyEvent.java
new file mode 100644
index 0000000..db5c825
--- /dev/null
+++ b/src/java/org/apache/ivy/core/event/resolve/StartResolveDependencyEvent.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.event.resolve;
+
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.plugins.resolver.DependencyResolver;
+
+public class StartResolveDependencyEvent extends ResolveDependencyEvent {
+ public static final String NAME = "pre-resolve-dependency";
+
+ public StartResolveDependencyEvent(DependencyResolver resolver, DependencyDescriptor dd,
+ ModuleRevisionId requestedRevisionId) {
+ super(NAME, resolver, dd, requestedRevisionId);
+ }
+
+}
diff --git a/src/java/org/apache/ivy/core/event/resolve/StartResolveEvent.java b/src/java/org/apache/ivy/core/event/resolve/StartResolveEvent.java
new file mode 100644
index 0000000..687ef73
--- /dev/null
+++ b/src/java/org/apache/ivy/core/event/resolve/StartResolveEvent.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.event.resolve;
+
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+
+public class StartResolveEvent extends ResolveEvent {
+ public static final String NAME = "pre-resolve";
+
+ public StartResolveEvent(ModuleDescriptor md, String[] confs) {
+ super(NAME, md, confs);
+ }
+
+}
diff --git a/src/java/org/apache/ivy/core/event/retrieve/EndRetrieveArtifactEvent.java b/src/java/org/apache/ivy/core/event/retrieve/EndRetrieveArtifactEvent.java
new file mode 100644
index 0000000..5054117
--- /dev/null
+++ b/src/java/org/apache/ivy/core/event/retrieve/EndRetrieveArtifactEvent.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.event.retrieve;
+
+import java.io.File;
+
+import org.apache.ivy.core.report.ArtifactDownloadReport;
+
+public class EndRetrieveArtifactEvent extends RetrieveArtifactEvent {
+ public static final String NAME = "post-retrieve-artifact";
+
+ public EndRetrieveArtifactEvent(ArtifactDownloadReport report, File destFile) {
+ super(NAME, report, destFile);
+ }
+}
diff --git a/src/java/org/apache/ivy/core/event/retrieve/EndRetrieveEvent.java b/src/java/org/apache/ivy/core/event/retrieve/EndRetrieveEvent.java
new file mode 100644
index 0000000..ad0514b
--- /dev/null
+++ b/src/java/org/apache/ivy/core/event/retrieve/EndRetrieveEvent.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.event.retrieve;
+
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.retrieve.RetrieveOptions;
+
+public class EndRetrieveEvent extends RetrieveEvent {
+ public static final String NAME = "post-retrieve";
+
+ private long duration;
+
+ private int nbCopied;
+
+ private int nbUpToDate;
+
+ private long totalCopiedSize;
+
+ public EndRetrieveEvent(ModuleRevisionId mrid, String[] confs, long elapsedTime,
+ int targetsCopied, int targetsUpToDate, long totalCopiedSize, RetrieveOptions options) {
+ super(NAME, mrid, confs, options);
+
+ this.duration = elapsedTime;
+ this.nbCopied = targetsCopied;
+ this.nbUpToDate = targetsUpToDate;
+ this.totalCopiedSize = totalCopiedSize;
+ addAttribute("duration", String.valueOf(elapsedTime));
+ addAttribute("size", String.valueOf(totalCopiedSize));
+ addAttribute("nbCopied", String.valueOf(targetsCopied));
+ addAttribute("nbUptodate", String.valueOf(targetsUpToDate));
+ }
+
+ /**
+ * Duration of the retrieve operation, in ms.
+ *
+ * @return Duration of the retrieve operation, in ms.
+ */
+ public long getDuration() {
+ return duration;
+ }
+
+ /**
+ * Number of artifacts which were copied (or symlinked) during the retrieve
+ *
+ * @return Number of artifacts which were copied during the retrieve.
+ */
+ public int getNbCopied() {
+ return nbCopied;
+ }
+
+ /**
+ * Number of artifacts which were not copied since they were already present and up to date.
+ *
+ * @return Number of artifacts which were not copied since they were already present and up to
+ * date.
+ */
+ public int getNbUpToDate() {
+ return nbUpToDate;
+ }
+
+ /**
+ * Total size of all copied (or symlinked) artifacts, in bytes.
+ *
+ * @return Total size of all copied (or symlinked) artifacts, in bytes.
+ */
+ public long getTotalCopiedSize() {
+ return totalCopiedSize;
+ }
+}
diff --git a/src/java/org/apache/ivy/core/event/retrieve/RetrieveArtifactEvent.java b/src/java/org/apache/ivy/core/event/retrieve/RetrieveArtifactEvent.java
new file mode 100644
index 0000000..9b134ca
--- /dev/null
+++ b/src/java/org/apache/ivy/core/event/retrieve/RetrieveArtifactEvent.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.event.retrieve;
+
+import java.io.File;
+
+import org.apache.ivy.core.event.IvyEvent;
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.report.ArtifactDownloadReport;
+
+public class RetrieveArtifactEvent extends IvyEvent {
+ private ArtifactDownloadReport report;
+
+ private File destFile;
+
+ public RetrieveArtifactEvent(String name, ArtifactDownloadReport report, File destFile) {
+ super(name);
+ addArtifactAttributes(report.getArtifact());
+
+ this.report = report;
+ this.destFile = destFile;
+ addAttribute("from", report.getLocalFile().getAbsolutePath());
+ addAttribute("to", destFile.getAbsolutePath());
+ addAttribute("size", String.valueOf(destFile.length()));
+ }
+
+ protected void addArtifactAttributes(Artifact artifact) {
+ addMridAttributes(artifact.getModuleRevisionId());
+ addAttributes(artifact.getAttributes());
+ addAttribute("metadata", String.valueOf(artifact.isMetadata()));
+ }
+
+ public File getDestFile() {
+ return destFile;
+ }
+
+ public ArtifactDownloadReport getReport() {
+ return report;
+ }
+}
diff --git a/src/java/org/apache/ivy/core/event/retrieve/RetrieveEvent.java b/src/java/org/apache/ivy/core/event/retrieve/RetrieveEvent.java
new file mode 100644
index 0000000..a7199cd
--- /dev/null
+++ b/src/java/org/apache/ivy/core/event/retrieve/RetrieveEvent.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.event.retrieve;
+
+import org.apache.ivy.core.event.IvyEvent;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.retrieve.RetrieveOptions;
+
+public class RetrieveEvent extends IvyEvent {
+ private ModuleRevisionId mrid;
+
+ private RetrieveOptions options;
+
+ protected RetrieveEvent(String name, ModuleRevisionId mrid, String[] confs,
+ RetrieveOptions options) {
+ super(name);
+ this.mrid = mrid;
+ addMridAttributes(mrid);
+ addConfsAttribute(confs);
+ addAttribute("symlink", String.valueOf(options.isMakeSymlinks()));
+ addAttribute("sync", String.valueOf(options.isSync()));
+ this.options = options;
+ }
+
+ public ModuleRevisionId getModuleRevisionId() {
+ return mrid;
+ }
+
+ public RetrieveOptions getOptions() {
+ return options;
+ }
+}
diff --git a/src/java/org/apache/ivy/core/event/retrieve/StartRetrieveArtifactEvent.java b/src/java/org/apache/ivy/core/event/retrieve/StartRetrieveArtifactEvent.java
new file mode 100644
index 0000000..eb0ccf1
--- /dev/null
+++ b/src/java/org/apache/ivy/core/event/retrieve/StartRetrieveArtifactEvent.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.event.retrieve;
+
+import java.io.File;
+
+import org.apache.ivy.core.report.ArtifactDownloadReport;
+
+public class StartRetrieveArtifactEvent extends RetrieveArtifactEvent {
+ public static final String NAME = "pre-retrieve-artifact";
+
+ public StartRetrieveArtifactEvent(ArtifactDownloadReport report, File destFile) {
+ super(NAME, report, destFile);
+ }
+}
diff --git a/src/java/org/apache/ivy/core/event/retrieve/StartRetrieveEvent.java b/src/java/org/apache/ivy/core/event/retrieve/StartRetrieveEvent.java
new file mode 100644
index 0000000..66b1678
--- /dev/null
+++ b/src/java/org/apache/ivy/core/event/retrieve/StartRetrieveEvent.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.event.retrieve;
+
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.retrieve.RetrieveOptions;
+
+public class StartRetrieveEvent extends RetrieveEvent {
+ public static final String NAME = "pre-retrieve";
+
+ public StartRetrieveEvent(ModuleRevisionId mrid, String[] confs, RetrieveOptions options) {
+ super(NAME, mrid, confs, options);
+ }
+}
diff --git a/src/java/org/apache/ivy/core/install/InstallEngine.java b/src/java/org/apache/ivy/core/install/InstallEngine.java
new file mode 100644
index 0000000..b213fbb
--- /dev/null
+++ b/src/java/org/apache/ivy/core/install/InstallEngine.java
@@ -0,0 +1,211 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.install;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Date;
+
+import org.apache.ivy.core.cache.ArtifactOrigin;
+import org.apache.ivy.core.module.descriptor.Configuration;
+import org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleId;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.report.ArtifactDownloadReport;
+import org.apache.ivy.core.report.MetadataArtifactDownloadReport;
+import org.apache.ivy.core.report.ResolveReport;
+import org.apache.ivy.core.resolve.DownloadOptions;
+import org.apache.ivy.core.resolve.IvyNode;
+import org.apache.ivy.core.resolve.ResolveEngine;
+import org.apache.ivy.core.resolve.ResolveOptions;
+import org.apache.ivy.core.search.SearchEngine;
+import org.apache.ivy.plugins.conflict.NoConflictManager;
+import org.apache.ivy.plugins.matcher.ExactPatternMatcher;
+import org.apache.ivy.plugins.matcher.MatcherHelper;
+import org.apache.ivy.plugins.matcher.PatternMatcher;
+import org.apache.ivy.plugins.resolver.DependencyResolver;
+import org.apache.ivy.util.Message;
+
+public class InstallEngine {
+ private InstallEngineSettings settings;
+
+ private ResolveEngine resolveEngine;
+
+ private SearchEngine searchEngine;
+
+ public InstallEngine(InstallEngineSettings settings, SearchEngine searchEngine,
+ ResolveEngine resolveEngine) {
+ this.settings = settings;
+ this.searchEngine = searchEngine;
+ this.resolveEngine = resolveEngine;
+ }
+
+ public ResolveReport install(ModuleRevisionId mrid, String from, String to,
+ InstallOptions options) throws IOException {
+ DependencyResolver fromResolver = settings.getResolver(from);
+ DependencyResolver toResolver = settings.getResolver(to);
+ if (fromResolver == null) {
+ throw new IllegalArgumentException("unknown resolver " + from
+ + ". Available resolvers are: " + settings.getResolverNames());
+ }
+ if (toResolver == null) {
+ throw new IllegalArgumentException("unknown resolver " + to
+ + ". Available resolvers are: " + settings.getResolverNames());
+ }
+ PatternMatcher matcher = settings.getMatcher(options.getMatcherName());
+ if (matcher == null) {
+ throw new IllegalArgumentException("unknown matcher " + options.getMatcherName()
+ + ". Available matchers are: " + settings.getMatcherNames());
+ }
+
+ // build module file declaring the dependency
+ Message.info(":: installing " + mrid + " ::");
+ DependencyResolver oldDicator = resolveEngine.getDictatorResolver();
+ boolean log = settings.logNotConvertedExclusionRule();
+ try {
+ settings.setLogNotConvertedExclusionRule(true);
+ resolveEngine.setDictatorResolver(fromResolver);
+
+ DefaultModuleDescriptor md = new DefaultModuleDescriptor(ModuleRevisionId.newInstance(
+ "apache", "ivy-install", "1.0"), settings.getStatusManager().getDefaultStatus(),
+ new Date());
+ String resolveId = ResolveOptions.getDefaultResolveId(md);
+ md.addConfiguration(new Configuration("default"));
+ md.addConflictManager(new ModuleId(ExactPatternMatcher.ANY_EXPRESSION,
+ ExactPatternMatcher.ANY_EXPRESSION), ExactPatternMatcher.INSTANCE,
+ new NoConflictManager());
+
+ for (int c = 0; c < options.getConfs().length; c++) {
+ final String[] depConfs = options.getConfs();
+
+ for (int j = 0; j < depConfs.length; j++) {
+ final String depConf = depConfs[j].trim();
+
+ if (MatcherHelper.isExact(matcher, mrid)) {
+ DefaultDependencyDescriptor dd = new DefaultDependencyDescriptor(md, mrid,
+ false, false, options.isTransitive());
+ dd.addDependencyConfiguration("default", depConf);
+ md.addDependency(dd);
+ } else {
+ ModuleRevisionId[] mrids = searchEngine.listModules(fromResolver, mrid,
+ matcher);
+
+ for (int i = 0; i < mrids.length; i++) {
+ Message.info("\tfound " + mrids[i] + " to install: adding to the list");
+ DefaultDependencyDescriptor dd = new DefaultDependencyDescriptor(md,
+ mrids[i], false, false, options.isTransitive());
+ dd.addDependencyConfiguration("default", depConf);
+ md.addDependency(dd);
+ }
+ }
+ }
+ }
+
+ // resolve using appropriate resolver
+ ResolveReport report = new ResolveReport(md, resolveId);
+
+ Message.info(":: resolving dependencies ::");
+ ResolveOptions resolveOptions = new ResolveOptions().setResolveId(resolveId)
+ .setConfs(new String[] {"default"}).setValidate(options.isValidate());
+ IvyNode[] dependencies = resolveEngine.getDependencies(md, resolveOptions, report);
+ report.setDependencies(Arrays.asList(dependencies), options.getArtifactFilter());
+
+ Message.info(":: downloading artifacts to cache ::");
+ resolveEngine.downloadArtifacts(report, options.getArtifactFilter(),
+ new DownloadOptions());
+
+ // now that everything is in cache, we can publish all these modules
+ Message.info(":: installing in " + to + " ::");
+ for (int i = 0; i < dependencies.length; i++) {
+ ModuleDescriptor depmd = dependencies[i].getDescriptor();
+ if (depmd != null) {
+ ModuleRevisionId depMrid = depmd.getModuleRevisionId();
+ Message.verbose("installing " + depMrid);
+ boolean successfullyPublished = false;
+ try {
+ toResolver.beginPublishTransaction(depMrid, options.isOverwrite());
+
+ // publish artifacts
+ ArtifactDownloadReport[] artifacts = report.getArtifactsReports(depMrid);
+ for (int j = 0; j < artifacts.length; j++) {
+ if (artifacts[j].getLocalFile() != null) {
+ toResolver.publish(artifacts[j].getArtifact(),
+ artifacts[j].getLocalFile(), options.isOverwrite());
+ }
+ }
+
+ // publish metadata
+ MetadataArtifactDownloadReport artifactDownloadReport = dependencies[i]
+ .getModuleRevision().getReport();
+ File localIvyFile = artifactDownloadReport.getLocalFile();
+ toResolver.publish(depmd.getMetadataArtifact(), localIvyFile,
+ options.isOverwrite());
+
+ if (options.isInstallOriginalMetadata()) {
+ if (artifactDownloadReport.getArtifactOrigin() != null
+ && artifactDownloadReport.getArtifactOrigin().isExists()
+ && !ArtifactOrigin.isUnknown(artifactDownloadReport
+ .getArtifactOrigin())
+ && artifactDownloadReport.getArtifactOrigin().getArtifact() != null
+ && artifactDownloadReport.getArtifactOrigin().getArtifact()
+ .getType().endsWith(".original")
+ && !artifactDownloadReport
+ .getArtifactOrigin()
+ .getArtifact()
+ .getType()
+ .equals(
+ depmd.getMetadataArtifact().getType() + ".original")) {
+ // publish original metadata artifact, too, as it has a different
+ // type
+ toResolver.publish(artifactDownloadReport.getArtifactOrigin()
+ .getArtifact(), artifactDownloadReport
+ .getOriginalLocalFile(), options.isOverwrite());
+ }
+ }
+
+ // end module publish
+ toResolver.commitPublishTransaction();
+ successfullyPublished = true;
+ } finally {
+ if (!successfullyPublished) {
+ toResolver.abortPublishTransaction();
+ }
+ }
+ }
+ }
+
+ Message.info(":: install resolution report ::");
+
+ // output report
+ resolveEngine
+ .outputReport(report, settings.getResolutionCacheManager(), resolveOptions);
+
+ return report;
+ } finally {
+ // IVY-834: log the problems if there were any...
+ Message.sumupProblems();
+
+ resolveEngine.setDictatorResolver(oldDicator);
+ settings.setLogNotConvertedExclusionRule(log);
+ }
+ }
+
+}
diff --git a/src/java/org/apache/ivy/core/install/InstallEngineSettings.java b/src/java/org/apache/ivy/core/install/InstallEngineSettings.java
new file mode 100644
index 0000000..9f3426b
--- /dev/null
+++ b/src/java/org/apache/ivy/core/install/InstallEngineSettings.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.install;
+
+import java.util.Collection;
+
+import org.apache.ivy.core.module.status.StatusManager;
+import org.apache.ivy.plugins.matcher.PatternMatcher;
+import org.apache.ivy.plugins.parser.ParserSettings;
+import org.apache.ivy.plugins.report.ReportOutputter;
+import org.apache.ivy.plugins.resolver.DependencyResolver;
+
+public interface InstallEngineSettings extends ParserSettings {
+
+ DependencyResolver getResolver(String from);
+
+ Collection getResolverNames();
+
+ ReportOutputter[] getReportOutputters();
+
+ void setLogNotConvertedExclusionRule(boolean log);
+
+ StatusManager getStatusManager();
+
+ boolean logNotConvertedExclusionRule();
+
+ PatternMatcher getMatcher(String matcherName);
+
+ Collection getMatcherNames();
+
+}
diff --git a/src/java/org/apache/ivy/core/install/InstallOptions.java b/src/java/org/apache/ivy/core/install/InstallOptions.java
new file mode 100644
index 0000000..5bb4182
--- /dev/null
+++ b/src/java/org/apache/ivy/core/install/InstallOptions.java
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.install;
+
+import org.apache.ivy.plugins.matcher.PatternMatcher;
+import org.apache.ivy.util.filter.Filter;
+import org.apache.ivy.util.filter.FilterHelper;
+
+public class InstallOptions {
+ private boolean transitive = true;
+
+ private boolean validate = true;
+
+ private boolean overwrite = false;
+
+ private boolean installOriginalMetadata = false;
+
+ private String[] confs = {"*"};
+
+ private Filter artifactFilter = FilterHelper.NO_FILTER;
+
+ private String matcherName = PatternMatcher.EXACT;
+
+ public boolean isTransitive() {
+ return transitive;
+ }
+
+ public InstallOptions setTransitive(boolean transitive) {
+ this.transitive = transitive;
+ return this;
+ }
+
+ public boolean isValidate() {
+ return validate;
+ }
+
+ public InstallOptions setValidate(boolean validate) {
+ this.validate = validate;
+ return this;
+ }
+
+ public boolean isOverwrite() {
+ return overwrite;
+ }
+
+ public InstallOptions setOverwrite(boolean overwrite) {
+ this.overwrite = overwrite;
+ return this;
+ }
+
+ public Filter getArtifactFilter() {
+ return artifactFilter;
+ }
+
+ public InstallOptions setArtifactFilter(Filter artifactFilter) {
+ this.artifactFilter = artifactFilter == null ? FilterHelper.NO_FILTER : artifactFilter;
+ return this;
+ }
+
+ public String getMatcherName() {
+ return matcherName;
+ }
+
+ public InstallOptions setMatcherName(String matcherName) {
+ this.matcherName = matcherName;
+ return this;
+ }
+
+ public String[] getConfs() {
+ return confs;
+ }
+
+ public InstallOptions setConfs(String[] conf) {
+ this.confs = conf;
+ return this;
+ }
+
+ public boolean isInstallOriginalMetadata() {
+ return installOriginalMetadata;
+ }
+
+ public InstallOptions setInstallOriginalMetadata(boolean installOriginalMetadata) {
+ this.installOriginalMetadata = installOriginalMetadata;
+ return this;
+ }
+}
diff --git a/src/java/org/apache/ivy/core/module/descriptor/AbstractArtifact.java b/src/java/org/apache/ivy/core/module/descriptor/AbstractArtifact.java
new file mode 100644
index 0000000..95b175c
--- /dev/null
+++ b/src/java/org/apache/ivy/core/module/descriptor/AbstractArtifact.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.module.descriptor;
+
+import java.util.Map;
+
+/**
+ *
+ */
+public abstract class AbstractArtifact implements Artifact {
+ public AbstractArtifact() {
+ }
+
+ public boolean equals(Object obj) {
+ if (!(obj instanceof Artifact)) {
+ return false;
+ }
+ Artifact art = (Artifact) obj;
+ return getModuleRevisionId().equals(art.getModuleRevisionId())
+ && getPublicationDate() == null ? (art.getPublicationDate() == null)
+ : getPublicationDate().equals(art.getPublicationDate())
+ && getName().equals(art.getName()) && getExt().equals(art.getExt())
+ && getType().equals(art.getType())
+ && getQualifiedExtraAttributes().equals(art.getQualifiedExtraAttributes());
+ }
+
+ public int hashCode() {
+ // CheckStyle:MagicNumber| OFF
+ int hash = 33;
+ hash = hash * 17 + getModuleRevisionId().hashCode();
+ if (getPublicationDate() != null) {
+ hash = hash * 17 + getPublicationDate().hashCode();
+ }
+ hash = hash * 17 + getName().hashCode();
+ hash = hash * 17 + getExt().hashCode();
+ hash = hash * 17 + getType().hashCode();
+ hash = hash * 17 + getQualifiedExtraAttributes().hashCode();
+ // CheckStyle:MagicNumber| ON
+ return hash;
+ }
+
+ public String toString() {
+ return String.valueOf(getId());
+ }
+
+ public String getAttribute(String attName) {
+ return getId().getAttribute(attName);
+ }
+
+ public Map getAttributes() {
+ return getId().getAttributes();
+ }
+
+ public String getExtraAttribute(String attName) {
+ return getId().getExtraAttribute(attName);
+ }
+
+ public Map getExtraAttributes() {
+ return getId().getExtraAttributes();
+ }
+
+ public Map getQualifiedExtraAttributes() {
+ return getId().getQualifiedExtraAttributes();
+ }
+
+}
diff --git a/src/java/org/apache/ivy/core/module/descriptor/AbstractIncludeExcludeRule.java b/src/java/org/apache/ivy/core/module/descriptor/AbstractIncludeExcludeRule.java
new file mode 100644
index 0000000..ccd262a
--- /dev/null
+++ b/src/java/org/apache/ivy/core/module/descriptor/AbstractIncludeExcludeRule.java
@@ -0,0 +1,94 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.module.descriptor;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Map;
+
+import org.apache.ivy.core.IvyPatternHelper;
+import org.apache.ivy.core.module.id.ArtifactId;
+import org.apache.ivy.plugins.matcher.PatternMatcher;
+import org.apache.ivy.util.extendable.UnmodifiableExtendableItem;
+
+/**
+ * Abstract class used as implementation for both {@link IncludeRule} and {@link ExcludeRule}, since
+ * their contract is almost identical
+ */
+public abstract class AbstractIncludeExcludeRule extends UnmodifiableExtendableItem implements
+ ConfigurationAware {
+
+ private ArtifactId id;
+
+ private Collection confs = new ArrayList();
+
+ private PatternMatcher patternMatcher;
+
+ public AbstractIncludeExcludeRule(ArtifactId aid, PatternMatcher matcher, Map extraAttributes) {
+ super(null, extraAttributes);
+ id = aid;
+ patternMatcher = matcher;
+ initStandardAttributes();
+ }
+
+ private void initStandardAttributes() {
+ setStandardAttribute(IvyPatternHelper.ORGANISATION_KEY, id.getModuleId().getOrganisation());
+ setStandardAttribute(IvyPatternHelper.MODULE_KEY, id.getModuleId().getName());
+ setStandardAttribute(IvyPatternHelper.ARTIFACT_KEY, id.getName());
+ setStandardAttribute(IvyPatternHelper.TYPE_KEY, id.getType());
+ setStandardAttribute(IvyPatternHelper.EXT_KEY, id.getExt());
+ setStandardAttribute("matcher", patternMatcher.getName());
+ }
+
+ public boolean equals(Object obj) {
+ if (!(obj instanceof AbstractIncludeExcludeRule)) {
+ return false;
+ }
+ AbstractIncludeExcludeRule rule = (AbstractIncludeExcludeRule) obj;
+ return getId().equals(rule.getId());
+ }
+
+ public int hashCode() {
+ return getId().hashCode();
+ }
+
+ /**
+ * Add a configuration for this rule
+ *
+ * @param conf
+ */
+ public void addConfiguration(String conf) {
+ confs.add(conf);
+ }
+
+ public ArtifactId getId() {
+ return id;
+ }
+
+ public String[] getConfigurations() {
+ return (String[]) confs.toArray(new String[confs.size()]);
+ }
+
+ public PatternMatcher getMatcher() {
+ return patternMatcher;
+ }
+
+ public String toString() {
+ return id + "(" + confs + ")";
+ }
+}
diff --git a/src/java/org/apache/ivy/core/module/descriptor/Artifact.java b/src/java/org/apache/ivy/core/module/descriptor/Artifact.java
new file mode 100644
index 0000000..3797839
--- /dev/null
+++ b/src/java/org/apache/ivy/core/module/descriptor/Artifact.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.module.descriptor;
+
+import java.net.URL;
+import java.util.Date;
+
+import org.apache.ivy.core.module.id.ArtifactRevisionId;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.util.extendable.ExtendableItem;
+
+/**
+ * Representation of a published 'file' in the development environment. An artifact is generally a
+ * file that is produced by a project build. This is typically a <code>jar</code>, a
+ * <code>war</code>, an <code>ear</code>, a <code>zip</code>, a <code>deb</code>, etc.
+ */
+public interface Artifact extends ExtendableItem {
+
+ /**
+ * Returns the resolved module revision id for this artifact
+ *
+ * @return the resolved module revision id.
+ */
+ ModuleRevisionId getModuleRevisionId();
+
+ /**
+ * Returns the resolved publication date for this artifact
+ *
+ * @return the resolved publication date. Never null.
+ */
+ Date getPublicationDate();
+
+ /**
+ * Return the name of the artifact, generally 'part' of the basename of the file.
+ *
+ * @return the name of the artifact. Never null.
+ */
+ String getName();
+
+ /**
+ * Returns the type of the artifact, typically 'jar', 'source', 'javadoc', 'debian', ...
+ *
+ * @return the type of the artifact. Never null.
+ */
+ String getType();
+
+ /**
+ * Retrieve the extension of the artifact. The extension is without dot (ie. 'jar' and not
+ * '.jar')
+ *
+ * @return the extension of the artifact. Never null.
+ */
+ String getExt();
+
+ /**
+ * Returns the url at which this artifact can be found independently of ivy configuration. This
+ * can be null (and is usually for standard artifacts)
+ *
+ * @return url at which this artifact can be found independently of ivy configuration
+ */
+ URL getUrl();
+
+ /**
+ * Returns the list of configurations where this artifact is associated to.
+ *
+ * @return the list of configuration this artifact is associated to. Never null.
+ */
+ String[] getConfigurations();
+
+ /**
+ * Return the specific identifier of this artifact.
+ *
+ * @return the id of the artifact
+ */
+ ArtifactRevisionId getId();
+
+ /**
+ * Returns true if this artifact represents a module metadata artifact, false if it's a
+ * published artifact
+ *
+ * @return true if this artifact represents a module metadata artifact, false if it's a
+ * published artifact
+ */
+ boolean isMetadata();
+}
diff --git a/src/java/org/apache/ivy/core/module/descriptor/Configuration.java b/src/java/org/apache/ivy/core/module/descriptor/Configuration.java
new file mode 100644
index 0000000..30d111b
--- /dev/null
+++ b/src/java/org/apache/ivy/core/module/descriptor/Configuration.java
@@ -0,0 +1,247 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.module.descriptor;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.util.extendable.DefaultExtendableItem;
+
+/**
+ * Represents a module configuration
+ */
+public class Configuration extends DefaultExtendableItem implements InheritableItem {
+ public static final class Visibility {
+ public static final Visibility PUBLIC = new Visibility("public");
+
+ public static final Visibility PRIVATE = new Visibility("private");
+
+ public static Visibility getVisibility(String name) {
+ if ("private".equals(name)) {
+ return PRIVATE;
+ } else if ("public".equals(name)) {
+ return PUBLIC;
+ } else {
+ throw new IllegalArgumentException("unknwon visibility " + name);
+ }
+ }
+
+ private String name;
+
+ private Visibility(String name) {
+ this.name = name;
+ }
+
+ public String toString() {
+ return name;
+ }
+ }
+
+ public static Collection/* <Configuration> */findConfigurationExtending(String conf,
+ Configuration[] confs) {
+ Collection extendingConfs = new ArrayList();
+ for (int i = 0; i < confs.length; i++) {
+ if (confs[i] != null && Arrays.asList(confs[i].getExtends()).contains(conf)) {
+ extendingConfs.add(confs[i]);
+ extendingConfs.addAll(findConfigurationExtending(confs[i].getName(), confs));
+ }
+ }
+ return extendingConfs;
+ }
+
+ private String name;
+
+ private String description;
+
+ private String[] extendsFrom;
+
+ private Visibility visibility;
+
+ private boolean transitive = true;
+
+ private String deprecated;
+
+ private ModuleRevisionId sourceModule;
+
+ /**
+ * Creates a new configuration.
+ *
+ * @param name
+ * the name of the configuration
+ */
+ public Configuration(String name) {
+ this(name, Visibility.PUBLIC, null, null, true, null);
+ }
+
+ public Configuration(Configuration source, ModuleRevisionId sourceModule) {
+ this(source.getAttributes(), source.getQualifiedExtraAttributes(), source.getName(), source
+ .getVisibility(), source.getDescription(), source.getExtends(), source
+ .isTransitive(), source.getDeprecated(), sourceModule);
+ }
+
+ /**
+ * Creates a new configuration.
+ *
+ * @param name
+ * the name of the configuration
+ * @param visibility
+ * the visibility of the configuration
+ * @param description
+ * a description
+ * @param ext
+ * the configurations to extend from
+ * @param transitive
+ * indicates if the configuration is transitive
+ * @param deprecated
+ * the deprecation message
+ */
+ public Configuration(String name, Visibility visibility, String description, String[] ext,
+ boolean transitive, String deprecated) {
+ this(null, null, name, visibility, description, ext, transitive, deprecated, null);
+ }
+
+ private Configuration(Map attributes, Map extraAttributes, String name, Visibility visibility,
+ String description, String[] ext, boolean transitive, String deprecated,
+ ModuleRevisionId sourceModule) {
+ super(attributes, extraAttributes);
+
+ if (name == null) {
+ throw new NullPointerException("null configuration name not allowed");
+ }
+ if (visibility == null) {
+ throw new NullPointerException("null visibility not allowed");
+ }
+ this.name = name;
+ this.visibility = visibility;
+ this.description = description;
+ if (ext == null) {
+ extendsFrom = new String[0];
+ } else {
+ extendsFrom = new String[ext.length];
+ for (int i = 0; i < ext.length; i++) {
+ extendsFrom[i] = ext[i].trim();
+ }
+ }
+ this.transitive = transitive;
+ this.deprecated = deprecated;
+ this.sourceModule = sourceModule;
+ }
+
+ /**
+ * Returns the deprecation message, or <tt>null</tt> if not specified.
+ *
+ * @return Returns the deprecation message.
+ */
+ public String getDeprecated() {
+ return deprecated;
+ }
+
+ /**
+ * @return Returns the description. It may be null.
+ */
+ public String getDescription() {
+ return description;
+ }
+
+ /**
+ * @return Returns the extends. May be empty, but never null.
+ */
+ public String[] getExtends() {
+ return extendsFrom;
+ }
+
+ /**
+ * @return Returns the name. Never null;
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @return Returns the visibility. Never null.
+ */
+ public Visibility getVisibility() {
+ return visibility;
+ }
+
+ /**
+ * @return Returns the transitive.
+ */
+ public final boolean isTransitive() {
+ return transitive;
+ }
+
+ public ModuleRevisionId getSourceModule() {
+ return sourceModule;
+ }
+
+ public String toString() {
+ return name;
+ }
+
+ public boolean equals(Object obj) {
+ if (!(obj instanceof Configuration)) {
+ return false;
+ }
+ return ((Configuration) obj).getName().equals(getName());
+ }
+
+ public int hashCode() {
+ return getName().hashCode();
+ }
+
+ public void replaceWildcards(ModuleDescriptor md) {
+ if (this != md.getConfiguration(name)) {
+ throw new IllegalArgumentException(
+ "The given ModuleDescriptor doesn't own this configuration!");
+ }
+
+ Configuration[] configs = md.getConfigurations();
+
+ Set newExtends = new LinkedHashSet();
+ for (int j = 0; j < extendsFrom.length; j++) {
+ if ("*".equals(extendsFrom[j])) {
+ addOther(configs, null, newExtends);
+ } else if ("*(public)".equals(extendsFrom[j])) {
+ addOther(configs, Visibility.PUBLIC, newExtends);
+ } else if ("*(private)".equals(extendsFrom[j])) {
+ addOther(configs, Visibility.PRIVATE, newExtends);
+ } else {
+ newExtends.add(extendsFrom[j]);
+ }
+ }
+
+ this.extendsFrom = (String[]) newExtends.toArray(new String[newExtends.size()]);
+ }
+
+ private void addOther(Configuration[] allConfigs, Visibility visibility, Set configs) {
+ for (int i = 0; i < allConfigs.length; i++) {
+ String currentName = allConfigs[i].getName();
+ if (!name.equals(currentName)
+ && ((visibility == null) || visibility.equals(allConfigs[i].getVisibility()))) {
+ configs.add(currentName);
+ }
+ }
+ }
+
+}
diff --git a/src/java/org/apache/ivy/core/module/descriptor/ConfigurationAware.java b/src/java/org/apache/ivy/core/module/descriptor/ConfigurationAware.java
new file mode 100644
index 0000000..2982f7a
--- /dev/null
+++ b/src/java/org/apache/ivy/core/module/descriptor/ConfigurationAware.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.module.descriptor;
+
+/**
+ * Objects implementing this interface are aware of module configurations, and can thus be added to
+ * configurations, and list their configurations.
+ */
+public interface ConfigurationAware {
+ /**
+ * Returns the configurations of the module to which the object is attached
+ *
+ * @return an array of configuration names to which the object is attached
+ */
+ public String[] getConfigurations();
+
+ /**
+ * Tells this object that it will now be part of the given configuration
+ *
+ * @param confName
+ * the name of the configuration to which the object is now attached
+ */
+ public void addConfiguration(String confName);
+}
diff --git a/src/java/org/apache/ivy/core/module/descriptor/ConfigurationGroup.java b/src/java/org/apache/ivy/core/module/descriptor/ConfigurationGroup.java
new file mode 100644
index 0000000..b25bebd
--- /dev/null
+++ b/src/java/org/apache/ivy/core/module/descriptor/ConfigurationGroup.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.module.descriptor;
+
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * A configuration which is actually a group of several configurations.
+ */
+public class ConfigurationGroup extends Configuration {
+
+ private final Map/* <String, Configuration> */members;
+
+ public ConfigurationGroup(String confName, Map /* <String, Configuration> */members) {
+ super(confName);
+ this.members = members;
+ }
+
+ /**
+ * Returns the list of configurations' names this object is a group of.
+ * <p>
+ * This list is built from the configuration name, if some of these configuration names have
+ * actually not been recognized in the module, they will be <code>null</code> when accessed from
+ * {@link #getIntersectedConfiguration(String)}.
+ * </p>
+ *
+ * @return the list of configurations' names this object is an intersection of.
+ */
+ public String[] getMembersConfigurationNames() {
+ return (String[]) members.keySet().toArray(new String[members.size()]);
+ }
+
+ /**
+ * Returns the {@link Configuration} object for the given conf name, or <code>null</code> if the
+ * given conf name is not part of this group or if this conf name isn't defined in the module in
+ * which this group has been built.
+ *
+ * @param confName
+ * the name of the configuration to return.
+ * @return the member {@link Configuration} object for the given conf name
+ */
+ public Configuration getMemberConfiguration(String confName) {
+ return (Configuration) members.get(confName);
+ }
+
+ public Visibility getVisibility() {
+ for (Iterator it = members.values().iterator(); it.hasNext();) {
+ Configuration c = (Configuration) it.next();
+ if (c != null && Visibility.PRIVATE.equals(c.getVisibility())) {
+ return Visibility.PRIVATE;
+ }
+ }
+ return Visibility.PUBLIC;
+ }
+}
diff --git a/src/java/org/apache/ivy/core/module/descriptor/ConfigurationIntersection.java b/src/java/org/apache/ivy/core/module/descriptor/ConfigurationIntersection.java
new file mode 100644
index 0000000..beba0ca
--- /dev/null
+++ b/src/java/org/apache/ivy/core/module/descriptor/ConfigurationIntersection.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.module.descriptor;
+
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * A configuration which is actually the intersection of several configurations.
+ */
+public class ConfigurationIntersection extends Configuration {
+
+ private final Map/* <String, Configuration> */intersectedConfs;
+
+ public ConfigurationIntersection(String confName,
+ Map /* <String, Configuration> */intersectedConfs) {
+ super(confName);
+ this.intersectedConfs = intersectedConfs;
+ }
+
+ /**
+ * Returns the list of configurations' names this object is an intersection of.
+ * <p>
+ * This list is built from the configuration name, if some of these configuration names have
+ * actually not been recognized in the module, they will be <code>null</code> when accessed from
+ * {@link #getIntersectedConfiguration(String)}.
+ * </p>
+ *
+ * @return the list of configurations' names this object is an intersection of.
+ */
+ public String[] getIntersectedConfigurationNames() {
+ return (String[]) intersectedConfs.keySet().toArray(new String[intersectedConfs.size()]);
+ }
+
+ /**
+ * Returns the intersected {@link Configuration} object for the given conf name, or
+ * <code>null</code> if the given conf name is not part of this intersection or if this conf
+ * name isn't defined in the module in which this intersection has been built.
+ *
+ * @param confName
+ * the name of the configuration to return.
+ * @return the intersected {@link Configuration} object for the given conf name
+ */
+ public Configuration getIntersectedConfiguration(String confName) {
+ return (Configuration) intersectedConfs.get(confName);
+ }
+
+ public Visibility getVisibility() {
+ for (Iterator it = intersectedConfs.values().iterator(); it.hasNext();) {
+ Configuration c = (Configuration) it.next();
+ if (c != null && Visibility.PRIVATE.equals(c.getVisibility())) {
+ return Visibility.PRIVATE;
+ }
+ }
+ return Visibility.PUBLIC;
+ }
+}
diff --git a/src/java/org/apache/ivy/core/module/descriptor/DefaultArtifact.java b/src/java/org/apache/ivy/core/module/descriptor/DefaultArtifact.java
new file mode 100644
index 0000000..3ea103d
--- /dev/null
+++ b/src/java/org/apache/ivy/core/module/descriptor/DefaultArtifact.java
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.module.descriptor;
+
+import java.net.URL;
+import java.util.Date;
+import java.util.Map;
+
+import org.apache.ivy.core.module.id.ArtifactRevisionId;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+
+/**
+ *
+ */
+public class DefaultArtifact extends AbstractArtifact {
+
+ public static Artifact newIvyArtifact(ModuleRevisionId mrid, Date pubDate) {
+ return new DefaultArtifact(mrid, pubDate, "ivy", "ivy", "xml", true);
+ }
+
+ public static Artifact newPomArtifact(ModuleRevisionId mrid, Date pubDate) {
+ return new DefaultArtifact(mrid, pubDate, mrid.getName(), "pom", "pom", true);
+ }
+
+ public static Artifact cloneWithAnotherExt(Artifact artifact, String newExt) {
+ return cloneWithAnotherTypeAndExt(artifact, artifact.getType(), newExt);
+ }
+
+ public static Artifact cloneWithAnotherType(Artifact artifact, String newType) {
+ return cloneWithAnotherTypeAndExt(artifact, newType, artifact.getExt());
+ }
+
+ public static Artifact cloneWithAnotherTypeAndExt(Artifact artifact, String newType,
+ String newExt) {
+ return new DefaultArtifact(ArtifactRevisionId.newInstance(artifact.getModuleRevisionId(),
+ artifact.getName(), newType, newExt, artifact.getQualifiedExtraAttributes()),
+ artifact.getPublicationDate(), artifact.getUrl(), artifact.isMetadata());
+ }
+
+ public static Artifact cloneWithAnotherName(Artifact artifact, String name) {
+ return new DefaultArtifact(ArtifactRevisionId.newInstance(artifact.getModuleRevisionId(),
+ name, artifact.getType(), artifact.getExt(), artifact.getQualifiedExtraAttributes()),
+ artifact.getPublicationDate(), artifact.getUrl(), artifact.isMetadata());
+ }
+
+ public static Artifact cloneWithAnotherMrid(Artifact artifact, ModuleRevisionId mrid) {
+ return new DefaultArtifact(ArtifactRevisionId.newInstance(mrid, artifact.getName(),
+ artifact.getType(), artifact.getExt(), artifact.getQualifiedExtraAttributes()),
+ artifact.getPublicationDate(), artifact.getUrl(), artifact.isMetadata());
+ }
+
+ private Date publicationDate;
+
+ private ArtifactRevisionId arid;
+
+ private URL url;
+
+ private boolean isMetadata = false;
+
+ public DefaultArtifact(ModuleRevisionId mrid, Date publicationDate, String name, String type,
+ String ext) {
+ this(mrid, publicationDate, name, type, ext, null, null);
+ }
+
+ public DefaultArtifact(ModuleRevisionId mrid, Date publicationDate, String name, String type,
+ String ext, boolean isMetadata) {
+ this(mrid, publicationDate, name, type, ext, null, null);
+ this.isMetadata = isMetadata;
+ }
+
+ public DefaultArtifact(ModuleRevisionId mrid, Date publicationDate, String name, String type,
+ String ext, Map extraAttributes) {
+ this(mrid, publicationDate, name, type, ext, null, extraAttributes);
+ }
+
+ public DefaultArtifact(ModuleRevisionId mrid, Date publicationDate, String name, String type,
+ String ext, URL url, Map extraAttributes) {
+ this(ArtifactRevisionId.newInstance(mrid, name, type, ext, extraAttributes),
+ publicationDate, url, false);
+ }
+
+ public DefaultArtifact(ArtifactRevisionId arid, Date publicationDate, URL url,
+ boolean isMetadata) {
+ if (arid == null) {
+ throw new NullPointerException("null arid not allowed");
+ }
+ if (publicationDate == null) {
+ publicationDate = new Date();
+ }
+ this.publicationDate = publicationDate;
+ this.arid = arid;
+ this.url = url;
+ this.isMetadata = isMetadata;
+ }
+
+ public ModuleRevisionId getModuleRevisionId() {
+ return arid.getModuleRevisionId();
+ }
+
+ public String getName() {
+ return arid.getName();
+ }
+
+ public Date getPublicationDate() {
+ return publicationDate;
+ }
+
+ public String getType() {
+ return arid.getType();
+ }
+
+ public String getExt() {
+ return arid.getExt();
+ }
+
+ public ArtifactRevisionId getId() {
+ return arid;
+ }
+
+ public String[] getConfigurations() {
+ return new String[0];
+ }
+
+ public URL getUrl() {
+ return url;
+ }
+
+ public boolean isMetadata() {
+ return isMetadata;
+ }
+}
diff --git a/src/java/org/apache/ivy/core/module/descriptor/DefaultDependencyArtifactDescriptor.java b/src/java/org/apache/ivy/core/module/descriptor/DefaultDependencyArtifactDescriptor.java
new file mode 100644
index 0000000..42202f9
--- /dev/null
+++ b/src/java/org/apache/ivy/core/module/descriptor/DefaultDependencyArtifactDescriptor.java
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.module.descriptor;
+
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Map;
+
+import org.apache.ivy.core.IvyPatternHelper;
+import org.apache.ivy.util.Checks;
+import org.apache.ivy.util.extendable.UnmodifiableExtendableItem;
+
+public class DefaultDependencyArtifactDescriptor extends UnmodifiableExtendableItem implements
+ DependencyArtifactDescriptor, ConfigurationAware {
+
+ private Collection confs = new ArrayList();
+
+ private URL url;
+
+ private String name;
+
+ private String type;
+
+ private String ext;
+
+ private DependencyDescriptor dd;
+
+ /**
+ * @param dd
+ * @param name
+ * @param type
+ * @param url
+ */
+ public DefaultDependencyArtifactDescriptor(DependencyDescriptor dd, String name, String type,
+ String ext, URL url, Map extraAttributes) {
+ super(null, extraAttributes);
+ Checks.checkNotNull(dd, "dd");
+ Checks.checkNotNull(name, "name");
+ Checks.checkNotNull(type, "type");
+ Checks.checkNotNull(ext, "ext");
+ this.dd = dd;
+ this.name = name;
+ this.type = type;
+ this.ext = ext;
+ this.url = url;
+ initStandardAttributes();
+ }
+
+ private void initStandardAttributes() {
+ setStandardAttribute(IvyPatternHelper.ARTIFACT_KEY, getName());
+ setStandardAttribute(IvyPatternHelper.TYPE_KEY, getType());
+ setStandardAttribute(IvyPatternHelper.EXT_KEY, getExt());
+ setStandardAttribute("url", url != null ? String.valueOf(url) : "");
+ }
+
+ public boolean equals(Object obj) {
+ if (!(obj instanceof DependencyArtifactDescriptor)) {
+ return false;
+ }
+ DependencyArtifactDescriptor dad = (DependencyArtifactDescriptor) obj;
+ return getAttributes().equals(dad.getAttributes());
+ }
+
+ public int hashCode() {
+ return getAttributes().hashCode();
+ }
+
+ /**
+ * Add a configuration for this artifact.
+ *
+ * @param conf
+ */
+ public void addConfiguration(String conf) {
+ confs.add(conf);
+ }
+
+ public DependencyDescriptor getDependencyDescriptor() {
+ return dd;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public String getExt() {
+ return ext;
+ }
+
+ public String[] getConfigurations() {
+ return (String[]) confs.toArray(new String[confs.size()]);
+ }
+
+ public URL getUrl() {
+ return url;
+ }
+
+ public String toString() {
+ return "DA:" + name + "." + ext + "(" + type + ") " + "(" + confs + ")"
+ + (url == null ? "" : url.toString());
+ }
+}
diff --git a/src/java/org/apache/ivy/core/module/descriptor/DefaultDependencyDescriptor.java b/src/java/org/apache/ivy/core/module/descriptor/DefaultDependencyDescriptor.java
new file mode 100644
index 0000000..11ca953
--- /dev/null
+++ b/src/java/org/apache/ivy/core/module/descriptor/DefaultDependencyDescriptor.java
@@ -0,0 +1,706 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.module.descriptor;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.ivy.core.module.id.ArtifactId;
+import org.apache.ivy.core.module.id.ModuleId;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.plugins.matcher.MatcherHelper;
+import org.apache.ivy.plugins.namespace.NameSpaceHelper;
+import org.apache.ivy.plugins.namespace.Namespace;
+import org.apache.ivy.plugins.namespace.NamespaceTransformer;
+import org.apache.ivy.util.Checks;
+
+/**
+ * This class can be used as the default implementation for DependencyDescriptor. It implements
+ * required methods and enables to fill dependency information with the addDependencyConfiguration
+ * method.
+ */
+public class DefaultDependencyDescriptor implements DependencyDescriptor {
+ private static final Pattern SELF_FALLBACK_PATTERN = Pattern
+ .compile("@(\\+[^\\(]+)?(\\(.*\\))?");
+
+ private static final Pattern THIS_FALLBACK_PATTERN = Pattern
+ .compile("#(\\+[^\\(]+)?(\\(.*\\))?");
+
+ /**
+ * Transforms the given dependency descriptor of the given namespace and return a new dependency
+ * descriptor in the system namespace. <i>Note that exclude rules are not converted in system
+ * namespace, because they aren't transformable (the name space hasn't the ability to convert
+ * regular expressions). However, method doesExclude will work with system artifacts.</i>
+ *
+ * @param dd
+ * @param ns
+ * @return
+ */
+ public static DependencyDescriptor transformInstance(DependencyDescriptor dd, Namespace ns) {
+ NamespaceTransformer t = ns.getToSystemTransformer();
+ if (t.isIdentity()) {
+ return dd;
+ }
+ DefaultDependencyDescriptor newdd = transformInstance(dd, t, false);
+ newdd.namespace = ns;
+ return newdd;
+ }
+
+ /**
+ * Transforms a dependency descriptor using the given transformer. Note that no namespace info
+ * will be attached to the transformed dependency descriptor, so calling doesExclude is not
+ * recommended (doesExclude only works when namespace is properly set)
+ *
+ * @param dd
+ * @param t
+ * @return
+ */
+ public static DefaultDependencyDescriptor transformInstance(DependencyDescriptor dd,
+ NamespaceTransformer t, boolean fromSystem) {
+ ModuleRevisionId transformParentId = t.transform(dd.getParentRevisionId());
+ ModuleRevisionId transformMrid = t.transform(dd.getDependencyRevisionId());
+ ModuleRevisionId transformDynamicMrid = t.transform(dd
+ .getDynamicConstraintDependencyRevisionId());
+ DefaultDependencyDescriptor newdd = new DefaultDependencyDescriptor(null, transformMrid,
+ transformDynamicMrid, dd.isForce(), dd.isChanging(), dd.isTransitive());
+
+ newdd.parentId = transformParentId;
+ ModuleRevisionId sourceModule = dd.getSourceModule();
+ if (sourceModule != null) {
+ newdd.sourceModule = t.transform(sourceModule);
+ }
+
+ String[] moduleConfs = dd.getModuleConfigurations();
+ if (moduleConfs.length == 1 && "*".equals(moduleConfs[0])) {
+ if (dd instanceof DefaultDependencyDescriptor) {
+ DefaultDependencyDescriptor ddd = (DefaultDependencyDescriptor) dd;
+ newdd.confs = new LinkedHashMap(ddd.confs);
+ newdd.setExcludeRules(new LinkedHashMap(ddd.getExcludeRules()));
+ newdd.setIncludeRules(new LinkedHashMap(ddd.getIncludeRules()));
+ newdd.setDependencyArtifacts(new LinkedHashMap(ddd.getDependencyArtifacts()));
+ } else {
+ throw new IllegalArgumentException(
+ "dependency descriptor transformation does not support * module confs "
+ + "with descriptors which aren't DefaultDependencyDescriptor");
+ }
+ } else {
+ for (int i = 0; i < moduleConfs.length; i++) {
+ newdd.confs.put(moduleConfs[i],
+ new ArrayList(Arrays.asList(dd.getDependencyConfigurations(moduleConfs[i]))));
+ newdd.getExcludeRules().put(moduleConfs[i],
+ new ArrayList(Arrays.asList(dd.getExcludeRules(moduleConfs[i]))));
+ newdd.getIncludeRules().put(moduleConfs[i],
+ new ArrayList(Arrays.asList(dd.getIncludeRules(moduleConfs[i]))));
+ newdd.getDependencyArtifacts().put(moduleConfs[i],
+ new ArrayList(Arrays.asList(dd.getDependencyArtifacts(moduleConfs[i]))));
+ }
+ }
+ if (fromSystem) {
+ newdd.asSystem = dd;
+ }
+ return newdd;
+ }
+
+ private final ModuleRevisionId revId;
+
+ private ModuleRevisionId dynamicRevId;
+
+ private Map/* <String,List<String>> */confs = new LinkedHashMap();
+
+ // Map (String masterConf -> Collection(DependencyArtifactDescriptor))
+ private Map dependencyArtifacts; // initialized on demand only for memory consumption reason
+
+ // Map (String masterConf -> Collection(IncludeRule))
+ private Map includeRules; // initialized on demand only for memory consumption reason
+
+ // Map (String masterConf -> Collection(ExcludeRule))
+ private Map excludeRules; // initialized on demand only for memory consumption reason
+
+ /**
+ * Used to indicate that this revision must be used in case of conflicts, independently of
+ * conflicts manager
+ */
+ private boolean isForce;
+
+ /**
+ * Used to indicate that the dependency is a changing one, i.e. that ivy should not rely on the
+ * version to know if it can trust artifacts in cache
+ */
+ private boolean isChanging;
+
+ private ModuleRevisionId parentId;
+
+ private boolean isTransitive = true;
+
+ /**
+ * This namespace should be used to check
+ */
+ private Namespace namespace = null;
+
+ private final ModuleDescriptor md;
+
+ private DependencyDescriptor asSystem = this;
+
+ private ModuleRevisionId sourceModule;
+
+ private DefaultDependencyDescriptor(DefaultDependencyDescriptor dd, ModuleRevisionId revision) {
+ Checks.checkNotNull(dd, "dd");
+ Checks.checkNotNull(revision, "revision");
+
+ if (!revision.getModuleId().equals(dd.getDependencyId())) {
+ throw new IllegalArgumentException(
+ "new ModuleRevisionId MUST have the same ModuleId as original one."
+ + " original = " + dd.getDependencyId() + " new = "
+ + revision.getModuleId());
+ }
+ md = dd.md;
+ parentId = dd.parentId;
+ revId = revision;
+ dynamicRevId = dd.dynamicRevId;
+ isForce = dd.isForce;
+ isChanging = dd.isChanging;
+ isTransitive = dd.isTransitive;
+ namespace = dd.namespace;
+ confs.putAll(dd.confs);
+ excludeRules = dd.excludeRules == null ? null : new LinkedHashMap(dd.excludeRules);
+ includeRules = dd.includeRules == null ? null : new LinkedHashMap(dd.includeRules);
+ dependencyArtifacts = dd.dependencyArtifacts == null ? null : new LinkedHashMap(
+ dd.dependencyArtifacts);
+ sourceModule = dd.sourceModule;
+ }
+
+ public DefaultDependencyDescriptor(ModuleDescriptor md, ModuleRevisionId mrid, boolean force,
+ boolean changing, boolean transitive) {
+ this(md, mrid, mrid, force, changing, transitive);
+ }
+
+ public DefaultDependencyDescriptor(ModuleRevisionId mrid, boolean force) {
+ this(mrid, force, false);
+ }
+
+ public DefaultDependencyDescriptor(ModuleRevisionId mrid, boolean force, boolean changing) {
+ this(null, mrid, mrid, force, changing, true);
+ }
+
+ public DefaultDependencyDescriptor(ModuleDescriptor md, ModuleRevisionId mrid,
+ ModuleRevisionId dynamicConstraint, boolean force, boolean changing, boolean transitive) {
+ Checks.checkNotNull(mrid, "mrid");
+ Checks.checkNotNull(dynamicConstraint, "dynamicConstraint");
+
+ this.md = md;
+ revId = mrid;
+ dynamicRevId = dynamicConstraint;
+ isForce = force;
+ isChanging = changing;
+ isTransitive = transitive;
+ sourceModule = md == null ? null : md.getModuleRevisionId();
+ }
+
+ public ModuleId getDependencyId() {
+ return getDependencyRevisionId().getModuleId();
+ }
+
+ public ModuleRevisionId getDependencyRevisionId() {
+ return revId;
+ }
+
+ public ModuleRevisionId getDynamicConstraintDependencyRevisionId() {
+ return dynamicRevId;
+ }
+
+ public String[] getModuleConfigurations() {
+ return (String[]) confs.keySet().toArray(new String[confs.keySet().size()]);
+ }
+
+ public String[] getDependencyConfigurations(String moduleConfiguration) {
+ return getDependencyConfigurations(moduleConfiguration, moduleConfiguration);
+ }
+
+ /**
+ * Return the dependency configurations mapped to the given moduleConfiguration, actually
+ * resolved because of the given requestedConfiguration
+ * <p>
+ * Usually requestedConfiguration and moduleConfiguration are the same, except when a conf
+ * extends another, then the moduleConfiguration is the configuration currently resolved (the
+ * extended one), and requestedConfiguration is the one actually requested initially (the
+ * extending one). Both moduleConfiguration and requestedConfiguration are configurations of the
+ * caller, the array returned is composed of the required configurations of the dependency
+ * described by this descriptor.
+ */
+ public String[] getDependencyConfigurations(String moduleConfiguration,
+ String requestedConfiguration) {
+ if (md != null) {
+ Configuration c = md.getConfiguration(moduleConfiguration);
+ if (c instanceof ConfigurationIntersection) {
+ ConfigurationIntersection intersection = (ConfigurationIntersection) c;
+ Set /* <String> */intersectedDepConfs = new HashSet();
+ String[] intersected = intersection.getIntersectedConfigurationNames();
+ for (int i = 0; i < intersected.length; i++) {
+ Collection depConfs = getDependencyConfigurationsIncludingExtending(
+ intersected[i], requestedConfiguration);
+ if (intersectedDepConfs.isEmpty()) {
+ intersectedDepConfs.addAll(depConfs);
+ } else {
+ if (intersectedDepConfs.contains("*")) {
+ intersectedDepConfs.remove("*");
+ intersectedDepConfs.addAll(depConfs);
+ } else if (depConfs.contains("*")) {
+ // nothing to do, intersection of 'something'
+ // with 'everything' is 'something'
+ } else {
+ Set /* <String> */intersectedDepConfsCopy = intersectedDepConfs;
+ intersectedDepConfs = new HashSet();
+ for (Iterator it = intersectedDepConfsCopy.iterator(); it.hasNext();) {
+ String intersectedDepConf = (String) it.next();
+ if (depConfs.contains(intersectedDepConf)) {
+ // the conf is present in both sets,
+ // so it is in the intersection
+ intersectedDepConfs.add(intersectedDepConf);
+ continue;
+ }
+ /*
+ * we do not handle special confs like *!sg or [cond]* in right hand
+ * confs yet: it would require supporting parenthesis grouping in
+ * configurations intersection interpretation
+ *
+ * for (Iterator it2 = depConfs.iterator(); it2.hasNext();) { String
+ * depConf = (String) it2.next(); if (depConf.startsWith("*")) { if
+ * (intersectedDepConf .indexOf("(" + depConf + ")") != -1) {
+ * intersectedDepConfs.add(intersectedDepConf); } else {
+ * intersectedDepConfs.add( "(" + intersectedDepConf + ")+(" +
+ * depConf + ")"); } } else if (intersectedDepConf.startsWith("*"))
+ * { if (depConf .indexOf("(" + intersectedDepConf + ")") != -1) {
+ * intersectedDepConfs.add(depConf); } else {
+ * intersectedDepConfs.add( depConf + "+" + intersectedDepConf); } }
+ * }
+ */
+ }
+ }
+ }
+ }
+ List confsList = (List) confs.get(moduleConfiguration);
+ if (confsList != null) {
+ intersectedDepConfs.addAll(confsList);
+ }
+ if (intersectedDepConfs.isEmpty()) {
+ List defConfs = (List) confs.get("*");
+ if (defConfs != null) {
+ for (Iterator it = defConfs.iterator(); it.hasNext();) {
+ String mappedConf = (String) it.next();
+ if (mappedConf != null && mappedConf.startsWith("@+")) {
+ return new String[] {moduleConfiguration + mappedConf.substring(1)};
+ } else if (mappedConf != null && mappedConf.equals("@")) {
+ return new String[] {moduleConfiguration};
+ }
+ }
+ }
+ }
+ return (String[]) intersectedDepConfs
+ .toArray(new String[intersectedDepConfs.size()]);
+ } else if (c instanceof ConfigurationGroup) {
+ ConfigurationGroup group = (ConfigurationGroup) c;
+ Set /* <String> */groupDepConfs = new HashSet();
+ String[] members = group.getMembersConfigurationNames();
+ for (int i = 0; i < members.length; i++) {
+ Collection depConfs = getDependencyConfigurationsIncludingExtending(members[i],
+ requestedConfiguration);
+ groupDepConfs.addAll(depConfs);
+ }
+ return (String[]) groupDepConfs.toArray(new String[groupDepConfs.size()]);
+ }
+ }
+
+ List confsList = (List) confs.get(moduleConfiguration);
+ if (confsList == null) {
+ // there is no mapping defined for this configuration, add the 'other' mappings.
+ confsList = (List) confs.get("%");
+ }
+ List defConfs = (List) confs.get("*");
+ Collection ret = new LinkedHashSet();
+ if (confsList != null) {
+ ret.addAll(confsList);
+ }
+ if (defConfs != null) {
+ ret.addAll(defConfs);
+ }
+
+ Collection replacedRet = new LinkedHashSet();
+ for (Iterator iter = ret.iterator(); iter.hasNext();) {
+ String c = (String) iter.next();
+ String replacedConf = replaceSelfFallbackPattern(c, moduleConfiguration);
+ if (replacedConf == null) {
+ replacedConf = replaceThisFallbackPattern(c, requestedConfiguration);
+ }
+ if (replacedConf != null) {
+ c = replacedConf;
+ }
+ replacedRet.add(c);
+ }
+ ret = replacedRet;
+ if (ret.remove("*")) {
+ StringBuffer r = new StringBuffer("*");
+ // merge excluded configurations as one conf like *!A!B
+ for (Iterator iter = ret.iterator(); iter.hasNext();) {
+ String c = (String) iter.next();
+ if (c.startsWith("!")) {
+ r.append(c);
+ }
+ }
+ return new String[] {r.toString()};
+ }
+ return (String[]) ret.toArray(new String[ret.size()]);
+ }
+
+ private Collection getDependencyConfigurationsIncludingExtending(String conf,
+ String requestedConfiguration) {
+ Set/* <String> */allDepConfs = new LinkedHashSet();
+ allDepConfs
+ .addAll(Arrays.asList(getDependencyConfigurations(conf, requestedConfiguration)));
+
+ Collection extendingConfs = Configuration.findConfigurationExtending(conf,
+ md.getConfigurations());
+ for (Iterator it = extendingConfs.iterator(); it.hasNext();) {
+ Configuration extendingConf = (Configuration) it.next();
+ allDepConfs.addAll(Arrays.asList(getDependencyConfigurations(extendingConf.getName(),
+ requestedConfiguration)));
+ }
+ return allDepConfs;
+ }
+
+ protected static String replaceSelfFallbackPattern(final String conf,
+ final String moduleConfiguration) {
+ return replaceFallbackConfigurationPattern(SELF_FALLBACK_PATTERN, conf, moduleConfiguration);
+ }
+
+ protected static String replaceThisFallbackPattern(final String conf,
+ final String requestedConfiguration) {
+ return replaceFallbackConfigurationPattern(THIS_FALLBACK_PATTERN, conf,
+ requestedConfiguration);
+ }
+
+ /**
+ * Replaces fallback patterns with correct values if fallback pattern exists.
+ *
+ * @param pattern
+ * pattern to look for
+ * @param conf
+ * configuration mapping from dependency element
+ * @param moduleConfiguration
+ * module's configuration to use for replacement
+ * @return Replaced string if pattern matched. Otherwise null.
+ */
+ protected static String replaceFallbackConfigurationPattern(final Pattern pattern,
+ final String conf, final String moduleConfiguration) {
+ Matcher matcher = pattern.matcher(conf);
+ if (matcher.matches()) {
+ String mappedConf = moduleConfiguration;
+ if (matcher.group(1) != null) {
+ mappedConf = mappedConf + matcher.group(1);
+ }
+ if (matcher.group(2) != null) {
+ mappedConf = mappedConf + matcher.group(2);
+ }
+ return mappedConf;
+ }
+ return null;
+ }
+
+ public String[] getDependencyConfigurations(String[] moduleConfigurations) {
+ Set confs = new LinkedHashSet();
+ for (int i = 0; i < moduleConfigurations.length; i++) {
+ confs.addAll(Arrays.asList(getDependencyConfigurations(moduleConfigurations[i])));
+ }
+ if (confs.contains("*")) {
+ return new String[] {"*"};
+ }
+ return (String[]) confs.toArray(new String[confs.size()]);
+ }
+
+ public DependencyArtifactDescriptor[] getDependencyArtifacts(String moduleConfiguration) {
+ Collection artifacts = getCollectionForConfiguration(moduleConfiguration,
+ dependencyArtifacts);
+ return (DependencyArtifactDescriptor[]) artifacts
+ .toArray(new DependencyArtifactDescriptor[artifacts.size()]);
+ }
+
+ public IncludeRule[] getIncludeRules(String moduleConfiguration) {
+ Collection rules = getCollectionForConfiguration(moduleConfiguration, includeRules);
+ return (IncludeRule[]) rules.toArray(new IncludeRule[rules.size()]);
+ }
+
+ public ExcludeRule[] getExcludeRules(String moduleConfiguration) {
+ Collection rules = getCollectionForConfiguration(moduleConfiguration, excludeRules);
+ return (ExcludeRule[]) rules.toArray(new ExcludeRule[rules.size()]);
+ }
+
+ private Set getCollectionForConfiguration(String moduleConfiguration, Map collectionMap) {
+ if (collectionMap == null || collectionMap.isEmpty()) {
+ return Collections.EMPTY_SET;
+ }
+ Collection artifacts = (Collection) collectionMap.get(moduleConfiguration);
+ Collection defArtifacts = (Collection) collectionMap.get("*");
+ Set ret = new LinkedHashSet();
+ if (artifacts != null) {
+ ret.addAll(artifacts);
+ }
+ if (defArtifacts != null) {
+ ret.addAll(defArtifacts);
+ }
+ return ret;
+ }
+
+ public DependencyArtifactDescriptor[] getDependencyArtifacts(String[] moduleConfigurations) {
+ Set artifacts = new LinkedHashSet();
+ for (int i = 0; i < moduleConfigurations.length; i++) {
+ artifacts.addAll(Arrays.asList(getDependencyArtifacts(moduleConfigurations[i])));
+ }
+ return (DependencyArtifactDescriptor[]) artifacts
+ .toArray(new DependencyArtifactDescriptor[artifacts.size()]);
+ }
+
+ public IncludeRule[] getIncludeRules(String[] moduleConfigurations) {
+ Set rules = new LinkedHashSet();
+ for (int i = 0; i < moduleConfigurations.length; i++) {
+ rules.addAll(Arrays.asList(getIncludeRules(moduleConfigurations[i])));
+ }
+ return (IncludeRule[]) rules.toArray(new IncludeRule[rules.size()]);
+ }
+
+ public ExcludeRule[] getExcludeRules(String[] moduleConfigurations) {
+ Set rules = new LinkedHashSet();
+ for (int i = 0; i < moduleConfigurations.length; i++) {
+ rules.addAll(Arrays.asList(getExcludeRules(moduleConfigurations[i])));
+ }
+ return (ExcludeRule[]) rules.toArray(new ExcludeRule[rules.size()]);
+ }
+
+ public DependencyArtifactDescriptor[] getAllDependencyArtifacts() {
+ if (dependencyArtifacts == null) {
+ return new DependencyArtifactDescriptor[0];
+ }
+ Set ret = mergeAll(dependencyArtifacts);
+ return (DependencyArtifactDescriptor[]) ret.toArray(new DependencyArtifactDescriptor[ret
+ .size()]);
+ }
+
+ public IncludeRule[] getAllIncludeRules() {
+ if (includeRules == null) {
+ return new IncludeRule[0];
+ }
+ Set ret = mergeAll(includeRules);
+ return (IncludeRule[]) ret.toArray(new IncludeRule[ret.size()]);
+ }
+
+ public ExcludeRule[] getAllExcludeRules() {
+ if (excludeRules == null) {
+ return new ExcludeRule[0];
+ }
+ Set ret = mergeAll(excludeRules);
+ return (ExcludeRule[]) ret.toArray(new ExcludeRule[ret.size()]);
+ }
+
+ private Set mergeAll(Map artifactsMap) {
+ Set ret = new LinkedHashSet();
+ for (Iterator it = artifactsMap.values().iterator(); it.hasNext();) {
+ Collection artifacts = (Collection) it.next();
+ ret.addAll(artifacts);
+ }
+ return ret;
+ }
+
+ public void addDependencyConfiguration(String masterConf, String depConf) {
+ if ((md != null) && !"*".equals(masterConf) && !"%".equals(masterConf)) {
+ Configuration config;
+ if (masterConf.startsWith("!")) {
+ config = md.getConfiguration(masterConf.substring(1));
+ } else {
+ config = md.getConfiguration(masterConf);
+ }
+ if (config == null) {
+ throw new IllegalArgumentException("Cannot add dependency '" + revId
+ + "' to configuration '" + masterConf + "' of module "
+ + md.getModuleRevisionId() + " because this configuration doesn't exist!");
+ }
+ if (config instanceof ConfigurationGroup) {
+ ConfigurationGroup group = (ConfigurationGroup) config;
+ String[] members = group.getMembersConfigurationNames();
+ for (int i = 0; i < members.length; i++) {
+ addDependencyConfiguration(members[i], depConf);
+ }
+ return;
+ }
+ }
+
+ List confsList = (List) confs.get(masterConf);
+ if (confsList == null) {
+ confsList = new ArrayList();
+ confs.put(masterConf, confsList);
+ }
+ if (!confsList.contains(depConf)) {
+ confsList.add(depConf);
+ }
+ }
+
+ public void addDependencyArtifact(String masterConf, DependencyArtifactDescriptor dad) {
+ addObjectToConfiguration(masterConf, dad, getDependencyArtifacts());
+ }
+
+ public void addIncludeRule(String masterConf, IncludeRule rule) {
+ addObjectToConfiguration(masterConf, rule, getIncludeRules());
+ }
+
+ public void addExcludeRule(String masterConf, ExcludeRule rule) {
+ addObjectToConfiguration(masterConf, rule, getExcludeRules());
+ }
+
+ private void addObjectToConfiguration(String callerConf, Object toAdd, Map confsMap) {
+ Collection col = (Collection) confsMap.get(callerConf);
+ if (col == null) {
+ col = new ArrayList();
+ confsMap.put(callerConf, col);
+ }
+ col.add(toAdd);
+ }
+
+ /**
+ * only works when namespace is properly set. The behaviour is not specified if namespace is not
+ * set
+ */
+ public boolean doesExclude(String[] moduleConfigurations, ArtifactId artifactId) {
+ if (namespace != null) {
+ artifactId = NameSpaceHelper
+ .transform(artifactId, namespace.getFromSystemTransformer());
+ }
+ ExcludeRule[] rules = getExcludeRules(moduleConfigurations);
+ for (int i = 0; i < rules.length; i++) {
+ if (MatcherHelper.matches(rules[i].getMatcher(), rules[i].getId(), artifactId)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if this descriptor contains any exclusion rule
+ *
+ * @return
+ */
+ public boolean canExclude() {
+ return excludeRules != null && !excludeRules.isEmpty();
+ }
+
+ public String toString() {
+ return "dependency: " + revId + " " + confs;
+ }
+
+ public boolean isForce() {
+ return isForce;
+ }
+
+ public ModuleRevisionId getParentRevisionId() {
+ return md != null ? md.getResolvedModuleRevisionId() : parentId;
+ }
+
+ public boolean isChanging() {
+ return isChanging;
+ }
+
+ public boolean isTransitive() {
+ return isTransitive;
+ }
+
+ public Namespace getNamespace() {
+ return namespace;
+ }
+
+ public String getAttribute(String attName) {
+ return revId.getAttribute(attName);
+ }
+
+ public Map getAttributes() {
+ return revId.getAttributes();
+ }
+
+ public String getExtraAttribute(String attName) {
+ return revId.getExtraAttribute(attName);
+ }
+
+ public Map getExtraAttributes() {
+ return revId.getExtraAttributes();
+ }
+
+ public Map getQualifiedExtraAttributes() {
+ return revId.getQualifiedExtraAttributes();
+ }
+
+ public DependencyDescriptor asSystem() {
+ return asSystem;
+ }
+
+ private void setDependencyArtifacts(Map dependencyArtifacts) {
+ this.dependencyArtifacts = dependencyArtifacts;
+ }
+
+ private Map getDependencyArtifacts() {
+ if (dependencyArtifacts == null) {
+ dependencyArtifacts = new LinkedHashMap();
+ }
+ return dependencyArtifacts;
+ }
+
+ private void setIncludeRules(Map includeRules) {
+ this.includeRules = includeRules;
+ }
+
+ private Map getIncludeRules() {
+ if (includeRules == null) {
+ includeRules = new LinkedHashMap();
+ }
+ return includeRules;
+ }
+
+ private void setExcludeRules(Map excludeRules) {
+ this.excludeRules = excludeRules;
+ }
+
+ private Map getExcludeRules() {
+ if (excludeRules == null) {
+ excludeRules = new LinkedHashMap();
+ }
+ return excludeRules;
+ }
+
+ public ModuleRevisionId getSourceModule() {
+ return sourceModule;
+ }
+
+ public DependencyDescriptor clone(ModuleRevisionId revision) {
+ return new DefaultDependencyDescriptor(this, revision);
+ }
+}
diff --git a/src/java/org/apache/ivy/core/module/descriptor/DefaultExcludeRule.java b/src/java/org/apache/ivy/core/module/descriptor/DefaultExcludeRule.java
new file mode 100644
index 0000000..52acf82
--- /dev/null
+++ b/src/java/org/apache/ivy/core/module/descriptor/DefaultExcludeRule.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.module.descriptor;
+
+import java.util.Map;
+
+import org.apache.ivy.core.module.id.ArtifactId;
+import org.apache.ivy.plugins.matcher.PatternMatcher;
+
+public class DefaultExcludeRule extends AbstractIncludeExcludeRule implements ExcludeRule {
+
+ public DefaultExcludeRule(ArtifactId aid, PatternMatcher matcher, Map extraAttributes) {
+ super(aid, matcher, extraAttributes);
+ }
+
+ public String toString() {
+ return "E:" + super.toString();
+ }
+}
diff --git a/src/java/org/apache/ivy/core/module/descriptor/DefaultExtendsDescriptor.java b/src/java/org/apache/ivy/core/module/descriptor/DefaultExtendsDescriptor.java
new file mode 100644
index 0000000..1c7be57
--- /dev/null
+++ b/src/java/org/apache/ivy/core/module/descriptor/DefaultExtendsDescriptor.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.module.descriptor;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+
+public class DefaultExtendsDescriptor implements ExtendsDescriptor {
+
+ private ModuleDescriptor parent;
+
+ private String location;
+
+ private List extendsTypes;
+
+ private boolean local;
+
+ public DefaultExtendsDescriptor(ModuleDescriptor parent, String location, String[] types) {
+ this(parent, location, types, false);
+ }
+
+ public DefaultExtendsDescriptor(ModuleDescriptor parent, String location, String[] types,
+ boolean local) {
+ this.parent = parent;
+ this.location = location;
+ this.local = local;
+ this.extendsTypes = new ArrayList(types.length);
+ for (int i = 0; i < types.length; ++i) {
+ extendsTypes.add(types[i]);
+ }
+ }
+
+ public ModuleRevisionId getParentRevisionId() {
+ return parent.getModuleRevisionId();
+ }
+
+ public ModuleRevisionId getResolvedParentRevisionId() {
+ return parent.getResolvedModuleRevisionId();
+ }
+
+ public ModuleDescriptor getParentMd() {
+ return parent;
+ }
+
+ public String getLocation() {
+ return location;
+ }
+
+ public String[] getExtendsTypes() {
+ return (String[]) extendsTypes.toArray(new String[extendsTypes.size()]);
+ }
+
+ public boolean isAllInherited() {
+ return extendsTypes.contains("all");
+ }
+
+ public boolean isInfoInherited() {
+ return isAllInherited() || extendsTypes.contains("info");
+ }
+
+ public boolean isDescriptionInherited() {
+ return isAllInherited() || extendsTypes.contains("description");
+ }
+
+ public boolean areConfigurationsInherited() {
+ return isAllInherited() || extendsTypes.contains("configurations");
+ }
+
+ public boolean areDependenciesInherited() {
+ return isAllInherited() || extendsTypes.contains("dependencies");
+ }
+
+ public boolean isLocal() {
+ return local;
+ }
+}
diff --git a/src/java/org/apache/ivy/core/module/descriptor/DefaultIncludeRule.java b/src/java/org/apache/ivy/core/module/descriptor/DefaultIncludeRule.java
new file mode 100644
index 0000000..87ddc58
--- /dev/null
+++ b/src/java/org/apache/ivy/core/module/descriptor/DefaultIncludeRule.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.module.descriptor;
+
+import java.util.Map;
+
+import org.apache.ivy.core.module.id.ArtifactId;
+import org.apache.ivy.plugins.matcher.PatternMatcher;
+
+public class DefaultIncludeRule extends AbstractIncludeExcludeRule implements IncludeRule {
+
+ public DefaultIncludeRule(ArtifactId aid, PatternMatcher matcher, Map extraAttributes) {
+ super(aid, matcher, extraAttributes);
+ }
+
+ public String toString() {
+ return "I:" + super.toString();
+ }
+}
diff --git a/src/java/org/apache/ivy/core/module/descriptor/DefaultModuleDescriptor.java b/src/java/org/apache/ivy/core/module/descriptor/DefaultModuleDescriptor.java
new file mode 100644
index 0000000..2c544c5
--- /dev/null
+++ b/src/java/org/apache/ivy/core/module/descriptor/DefaultModuleDescriptor.java
@@ -0,0 +1,883 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.module.descriptor;
+
+import java.io.File;
+import java.io.IOException;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Stack;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.ivy.core.module.id.ArtifactId;
+import org.apache.ivy.core.module.id.ModuleId;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.module.id.ModuleRules;
+import org.apache.ivy.core.module.status.StatusManager;
+import org.apache.ivy.plugins.conflict.ConflictManager;
+import org.apache.ivy.plugins.matcher.MapMatcher;
+import org.apache.ivy.plugins.matcher.MatcherHelper;
+import org.apache.ivy.plugins.matcher.PatternMatcher;
+import org.apache.ivy.plugins.namespace.NameSpaceHelper;
+import org.apache.ivy.plugins.namespace.Namespace;
+import org.apache.ivy.plugins.namespace.NamespaceTransformer;
+import org.apache.ivy.plugins.parser.ModuleDescriptorParser;
+import org.apache.ivy.plugins.parser.xml.XmlModuleDescriptorParser;
+import org.apache.ivy.plugins.parser.xml.XmlModuleDescriptorWriter;
+import org.apache.ivy.plugins.repository.Resource;
+import org.apache.ivy.plugins.version.VersionMatcher;
+import org.apache.ivy.util.Message;
+
+/**
+ *
+ */
+public class DefaultModuleDescriptor implements ModuleDescriptor {
+
+ public static DefaultModuleDescriptor newDefaultInstance(ModuleRevisionId mrid) {
+ return newDefaultInstance(mrid, null);
+ }
+
+ public static DefaultModuleDescriptor newCallerInstance(ModuleRevisionId mrid, String[] confs,
+ boolean transitive, boolean changing) {
+ DefaultModuleDescriptor moduleDescriptor = new DefaultModuleDescriptor(
+ ModuleRevisionId.newInstance(mrid.getOrganisation(), mrid.getName() + "-caller",
+ "working"), "integration", null, true);
+ for (int i = 0; i < confs.length; i++) {
+ moduleDescriptor.addConfiguration(new Configuration(confs[i]));
+ }
+ moduleDescriptor.setLastModified(System.currentTimeMillis());
+ DefaultDependencyDescriptor dd = new DefaultDependencyDescriptor(moduleDescriptor, mrid,
+ true, changing, transitive);
+ for (int j = 0; j < confs.length; j++) {
+ dd.addDependencyConfiguration(confs[j], confs[j]);
+ }
+ moduleDescriptor.addDependency(dd);
+
+ return moduleDescriptor;
+ }
+
+ public static DefaultModuleDescriptor newCallerInstance(ModuleRevisionId[] mrid,
+ boolean transitive, boolean changing) {
+ DefaultModuleDescriptor moduleDescriptor = new DefaultModuleDescriptor(
+ ModuleRevisionId.newInstance("caller", "all-caller", "working"), "integration",
+ null, true);
+ moduleDescriptor.addConfiguration(new Configuration(DEFAULT_CONFIGURATION));
+ moduleDescriptor.setLastModified(System.currentTimeMillis());
+ for (int i = 0; i < mrid.length; i++) {
+ DefaultDependencyDescriptor dd = new DefaultDependencyDescriptor(moduleDescriptor,
+ mrid[i], true, changing, transitive);
+ dd.addDependencyConfiguration(DEFAULT_CONFIGURATION, "*");
+ moduleDescriptor.addDependency(dd);
+ }
+
+ return moduleDescriptor;
+ }
+
+ public static DefaultModuleDescriptor newDefaultInstance(ModuleRevisionId mrid,
+ DependencyArtifactDescriptor[] artifacts) {
+ DefaultModuleDescriptor moduleDescriptor = new DefaultModuleDescriptor(mrid, "release",
+ null, true);
+ moduleDescriptor.addConfiguration(new Configuration(DEFAULT_CONFIGURATION));
+ if (artifacts != null && artifacts.length > 0) {
+ for (int i = 0; i < artifacts.length; i++) {
+ moduleDescriptor.addArtifact(DEFAULT_CONFIGURATION,
+ new MDArtifact(moduleDescriptor, artifacts[i].getName(),
+ artifacts[i].getType(), artifacts[i].getExt(), artifacts[i].getUrl(),
+ artifacts[i].getExtraAttributes()));
+ }
+ } else {
+ moduleDescriptor.addArtifact(DEFAULT_CONFIGURATION, new MDArtifact(moduleDescriptor,
+ mrid.getName(), "jar", "jar"));
+ }
+ moduleDescriptor.setLastModified(System.currentTimeMillis());
+ return moduleDescriptor;
+ }
+
+ public static DefaultModuleDescriptor newBasicInstance(ModuleRevisionId mrid,
+ Date publicationDate) {
+ DefaultModuleDescriptor moduleDescriptor = new DefaultModuleDescriptor(mrid, "release",
+ publicationDate, false);
+ moduleDescriptor.addConfiguration(new Configuration(DEFAULT_CONFIGURATION));
+ moduleDescriptor.addArtifact(DEFAULT_CONFIGURATION,
+ new MDArtifact(moduleDescriptor, mrid.getName(), "jar", "jar"));
+ return moduleDescriptor;
+ }
+
+ /**
+ * Transforms the given module descriptor of the given namespace and return a new module
+ * descriptor in the system namespace. <i>Note that dependency exclude rules are not converted
+ * in system namespace, because they aren't transformable (the name space hasn't the ability to
+ * convert regular expressions)</i>
+ *
+ * @param md
+ * @param ns
+ * @return
+ */
+ public static ModuleDescriptor transformInstance(ModuleDescriptor md, Namespace ns) {
+ NamespaceTransformer t = ns.getToSystemTransformer();
+ if (t.isIdentity()) {
+ return md;
+ }
+ DefaultModuleDescriptor nmd = new DefaultModuleDescriptor(md.getParser(), md.getResource());
+ nmd.revId = t.transform(md.getModuleRevisionId());
+ nmd.resolvedRevId = t.transform(md.getResolvedModuleRevisionId());
+ nmd.status = md.getStatus();
+ nmd.publicationDate = md.getPublicationDate();
+ nmd.resolvedPublicationDate = md.getResolvedPublicationDate();
+
+ ExtendsDescriptor[] ed = md.getInheritedDescriptors();
+ for (int i = 0; i < ed.length; ++i) {
+ ModuleDescriptor parentMd = ed[i].getParentMd();
+ DefaultModuleDescriptor parentNmd = new DefaultModuleDescriptor(parentMd.getParser(),
+ parentMd.getResource());
+ parentNmd.revId = t.transform(parentMd.getModuleRevisionId());
+ parentNmd.resolvedRevId = t.transform(parentMd.getResolvedModuleRevisionId());
+ parentNmd.status = parentMd.getStatus();
+ parentNmd.publicationDate = parentMd.getPublicationDate();
+ parentNmd.resolvedPublicationDate = parentMd.getResolvedPublicationDate();
+
+ nmd.inheritedDescriptors.add(new DefaultExtendsDescriptor(parentNmd, ed[i]
+ .getLocation(), ed[i].getExtendsTypes()));
+ }
+
+ DependencyDescriptor[] dd = md.getDependencies();
+ for (int i = 0; i < dd.length; i++) {
+ nmd.dependencies.add(NameSpaceHelper.toSystem(dd[i], ns));
+ }
+ Configuration[] confs = md.getConfigurations();
+ for (int i = 0; i < confs.length; i++) {
+ nmd.configurations.put(confs[i].getName(), confs[i]);
+ Artifact[] arts = md.getArtifacts(confs[i].getName());
+ for (int j = 0; j < arts.length; j++) {
+ nmd.addArtifact(confs[i].getName(), NameSpaceHelper.transform(arts[j], t));
+ }
+ }
+ nmd.setDefault(md.isDefault());
+ if (md instanceof DefaultModuleDescriptor) {
+ DefaultModuleDescriptor dmd = (DefaultModuleDescriptor) md;
+ nmd.conflictManagers = (ModuleRules) dmd.conflictManagers.clone();
+ nmd.dependencyDescriptorMediators = (ModuleRules) dmd.dependencyDescriptorMediators
+ .clone();
+ } else {
+ Message.warn("transformed module descriptor is not a default module descriptor: "
+ + "impossible to copy conflict manager and version mediation configuration: "
+ + md);
+ }
+ nmd.licenses.addAll(Arrays.asList(md.getLicenses()));
+ nmd.homePage = md.getHomePage();
+ nmd.description = md.getDescription();
+ nmd.lastModified = md.getLastModified();
+ nmd.extraAttributesNamespaces = md.getExtraAttributesNamespaces();
+ nmd.extraInfos = md.getExtraInfos();
+ nmd.namespace = ns;
+
+ return nmd;
+ }
+
+ private ModuleRevisionId revId;
+
+ private ModuleRevisionId resolvedRevId;
+
+ private String status = StatusManager.getCurrent().getDefaultStatus();
+
+ private Date publicationDate;
+
+ private Date resolvedPublicationDate;
+
+ private List dependencies = new ArrayList(); // List (DependencyDescriptor)
+
+ private Map configurations = new LinkedHashMap(); // Map(String conf -> Configuration)
+
+ private Map artifactsByConf = new HashMap(); // Map (String conf -> Collection(Artifact))
+
+ private Collection artifacts = new LinkedHashSet(); // Collection(Artifact)
+
+ // all artifacts could also be found in the artifactsByConf map, but here we can
+ // preserve the order
+
+ private boolean isDefault = false;
+
+ private ModuleRules conflictManagers = new ModuleRules();
+
+ private ModuleRules dependencyDescriptorMediators = new ModuleRules();
+
+ private List licenses = new ArrayList(); // List(License)
+
+ private String homePage;
+
+ private String description = "";
+
+ private long lastModified = 0;
+
+ private Namespace namespace;
+
+ private String defaultConf;
+
+ private String defaultConfMapping;
+
+ private boolean mappingOverride;
+
+ private ModuleDescriptorParser parser;
+
+ private Resource resource;
+
+ private List excludeRules = new ArrayList(); // List(ExcludeRule)
+
+ private Artifact metadataArtifact;
+
+ private List inheritedDescriptors = new ArrayList(); // List(ExtendsDescriptor)
+
+ private Map/* <String,String> */extraAttributesNamespaces = new LinkedHashMap();
+
+ private List<ExtraInfoHolder> extraInfos = new ArrayList<ExtraInfoHolder>();
+
+ public DefaultModuleDescriptor(ModuleRevisionId id, String status, Date pubDate) {
+ this(id, status, pubDate, false);
+ }
+
+ public DefaultModuleDescriptor(ModuleRevisionId id, String status, Date pubDate,
+ boolean isDefault) {
+ if (id == null) {
+ throw new NullPointerException("null module revision id not allowed");
+ }
+ if (status == null) {
+ throw new NullPointerException("null status not allowed");
+ }
+ this.revId = id;
+ this.resolvedRevId = id;
+ this.status = status;
+ this.publicationDate = pubDate;
+ this.resolvedPublicationDate = publicationDate == null ? new Date() : publicationDate;
+ this.isDefault = isDefault;
+ this.parser = XmlModuleDescriptorParser.getInstance();
+ }
+
+ /**
+ * IMPORTANT : at least call setModuleRevisionId and setResolvedPublicationDate with instances
+ * created by this constructor !
+ */
+ public DefaultModuleDescriptor(ModuleDescriptorParser parser, Resource res) {
+ this.parser = parser;
+ resource = res;
+ }
+
+ public Artifact getMetadataArtifact() {
+ if (metadataArtifact == null) {
+ metadataArtifact = DefaultArtifact.newIvyArtifact(resolvedRevId,
+ resolvedPublicationDate);
+ }
+ return metadataArtifact;
+ }
+
+ public void setModuleArtifact(Artifact moduleArtifact) {
+ this.metadataArtifact = moduleArtifact;
+ }
+
+ public boolean isDefault() {
+ return isDefault;
+ }
+
+ public void setPublicationDate(Date publicationDate) {
+ this.publicationDate = publicationDate;
+ if (resolvedPublicationDate == null) {
+ resolvedPublicationDate = publicationDate == null ? new Date() : publicationDate;
+ }
+ }
+
+ public Date getPublicationDate() {
+ return publicationDate;
+ }
+
+ public void setResolvedPublicationDate(Date publicationDate) {
+ if (publicationDate == null) {
+ throw new NullPointerException("null publication date not allowed");
+ }
+ resolvedPublicationDate = publicationDate;
+ }
+
+ public Date getResolvedPublicationDate() {
+ return resolvedPublicationDate;
+ }
+
+ public String getRevision() {
+ return getResolvedModuleRevisionId().getRevision();
+ }
+
+ public void setModuleRevisionId(ModuleRevisionId revId) {
+ if (revId == null) {
+ throw new NullPointerException("null module revision id not allowed");
+ }
+ this.revId = revId;
+ if (resolvedRevId == null) {
+ resolvedRevId = revId;
+ }
+ }
+
+ public void setResolvedModuleRevisionId(ModuleRevisionId revId) {
+ resolvedRevId = revId;
+ }
+
+ public void setStatus(String status) {
+ this.status = status;
+ }
+
+ public void addInheritedDescriptor(ExtendsDescriptor descriptor) {
+ inheritedDescriptors.add(descriptor);
+ }
+
+ public void addDependency(DependencyDescriptor dependency) {
+ dependencies.add(dependency);
+ }
+
+ public void addConfiguration(Configuration conf) {
+ configurations.put(conf.getName(), conf);
+ }
+
+ /**
+ * Artifact configurations are not used since added artifact may not be entirely completed, so
+ * its configurations data may not be accurate
+ *
+ * @param conf
+ * @param artifact
+ */
+ public void addArtifact(String conf, Artifact artifact) {
+ Configuration c = getConfiguration(conf);
+ if (c == null) {
+ throw new IllegalArgumentException("Cannot add artifact '"
+ + artifact.getId().getArtifactId().getShortDescription()
+ + "' to configuration '" + conf + "' of module " + revId
+ + " because this configuration doesn't exist!");
+ }
+ if (c instanceof ConfigurationGroup) {
+ ConfigurationGroup group = (ConfigurationGroup) c;
+ String[] members = group.getMembersConfigurationNames();
+ for (int i = 0; i < members.length; i++) {
+ addArtifact(members[i], artifact);
+ }
+ } else {
+ Collection artifacts = (Collection) artifactsByConf.get(conf);
+ if (artifacts == null) {
+ artifacts = new ArrayList();
+ artifactsByConf.put(conf, artifacts);
+ }
+ artifacts.add(artifact);
+ this.artifacts.add(artifact);
+ }
+ }
+
+ public ModuleRevisionId getModuleRevisionId() {
+ return revId;
+ }
+
+ public ModuleRevisionId getResolvedModuleRevisionId() {
+ return resolvedRevId;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public ExtendsDescriptor[] getInheritedDescriptors() {
+ return (ExtendsDescriptor[]) inheritedDescriptors
+ .toArray(new ExtendsDescriptor[inheritedDescriptors.size()]);
+ }
+
+ public Configuration[] getConfigurations() {
+ return (Configuration[]) configurations.values().toArray(
+ new Configuration[configurations.size()]);
+ }
+
+ public String[] getConfigurationsNames() {
+ return (String[]) configurations.keySet().toArray(new String[configurations.size()]);
+ }
+
+ public String[] getPublicConfigurationsNames() {
+ List ret = new ArrayList();
+ for (Iterator iter = configurations.values().iterator(); iter.hasNext();) {
+ Configuration conf = (Configuration) iter.next();
+ if (conf.getVisibility() == Configuration.Visibility.PUBLIC) {
+ ret.add(conf.getName());
+ }
+ }
+ return (String[]) ret.toArray(new String[ret.size()]);
+ }
+
+ /**
+ * Returns the configuration object with the given name in the current module descriptor, null
+ * if not found.
+ */
+ public Configuration getConfiguration(String confName) {
+ Configuration configuration = (Configuration) configurations.get(confName);
+ if (configuration == null && confName != null) {
+ // let's first check if the configuration is a conf group
+ Matcher m = Pattern.compile("\\*\\[([^=]+)\\=([^\\]]+)\\]").matcher(confName);
+ if (m.matches()) {
+ String attName = m.group(1);
+ String attValue = m.group(2);
+
+ // this is a conf group, let's search for its members
+ Map /* <String,Configuration> */members = new LinkedHashMap();
+ for (Iterator it = configurations.values().iterator(); it.hasNext();) {
+ Configuration conf = (Configuration) it.next();
+ if (attValue.equals(conf.getAttribute(attName))) {
+ members.put(conf.getName(), conf);
+ }
+ }
+ return new ConfigurationGroup(confName, members);
+ }
+
+ // let's see if a configuration intersection is requested
+ String[] confs = confName.split("\\+");
+ if (confs.length <= 1) {
+ return null;
+ }
+ Map /* <String,Configuration> */intersectedConfs = new LinkedHashMap();
+ for (int i = 0; i < confs.length; i++) {
+ Configuration c = (Configuration) configurations.get(confs[i]);
+ if (c == null) {
+ Message.verbose("missing configuration '" + confs[i] + "' from intersection "
+ + confName + " in " + this);
+ return null;
+ }
+ intersectedConfs.put(confs[i], c);
+ }
+ return new ConfigurationIntersection(confName, intersectedConfs);
+ }
+ return configuration;
+ }
+
+ public Artifact[] getArtifacts(String conf) {
+ Configuration c = getConfiguration(conf);
+ if (c == null) {
+ return new Artifact[0];
+ }
+ Collection artifacts = (Collection) artifactsByConf.get(conf);
+ if (c instanceof ConfigurationIntersection) {
+ ConfigurationIntersection intersection = (ConfigurationIntersection) c;
+ String[] intersected = intersection.getIntersectedConfigurationNames();
+ Set/* <Artifact> */intersectedArtifacts = new LinkedHashSet();
+ for (int j = 0; j < intersected.length; j++) {
+ Collection arts = getArtifactsIncludingExtending(intersected[j]);
+ if (intersectedArtifacts.isEmpty()) {
+ intersectedArtifacts.addAll(arts);
+ } else {
+ intersectedArtifacts.retainAll(arts);
+ }
+ }
+ if (artifacts != null) {
+ intersectedArtifacts.addAll(artifacts);
+ }
+ return (Artifact[]) intersectedArtifacts.toArray(new Artifact[intersectedArtifacts
+ .size()]);
+ } else if (c instanceof ConfigurationGroup) {
+ ConfigurationGroup group = (ConfigurationGroup) c;
+ String[] members = group.getMembersConfigurationNames();
+ Set/* <Artifact> */groupArtifacts = new LinkedHashSet();
+ for (int i = 0; i < members.length; i++) {
+ groupArtifacts.addAll(getArtifactsIncludingExtending(members[i]));
+ }
+ if (artifacts != null) {
+ groupArtifacts.addAll(artifacts);
+ }
+ return (Artifact[]) groupArtifacts.toArray(new Artifact[groupArtifacts.size()]);
+ } else {
+ if (artifacts == null) {
+ return new Artifact[0];
+ } else {
+ return (Artifact[]) artifacts.toArray(new Artifact[artifacts.size()]);
+ }
+ }
+ }
+
+ private Collection/* <Artifact> */getArtifactsIncludingExtending(String conf) {
+ Collection extendingConfs = Configuration.findConfigurationExtending(conf,
+ getConfigurations());
+ Set/* <Artifact> */artifacts = new LinkedHashSet();
+ Collection arts = (Collection) artifactsByConf.get(conf);
+ if (arts != null) {
+ artifacts.addAll(arts);
+ }
+ for (Iterator it = extendingConfs.iterator(); it.hasNext();) {
+ Configuration extendingConf = (Configuration) it.next();
+ arts = (Collection) artifactsByConf.get(extendingConf.getName());
+ if (arts != null) {
+ artifacts.addAll(arts);
+ }
+ }
+ return artifacts;
+ }
+
+ public Artifact[] getAllArtifacts() {
+ return (Artifact[]) artifacts.toArray(new Artifact[artifacts.size()]);
+ }
+
+ public DependencyDescriptor[] getDependencies() {
+ return (DependencyDescriptor[]) dependencies.toArray(new DependencyDescriptor[dependencies
+ .size()]);
+ }
+
+ public boolean dependsOn(VersionMatcher matcher, ModuleDescriptor md) {
+ for (Iterator iter = dependencies.iterator(); iter.hasNext();) {
+ DependencyDescriptor dd = (DependencyDescriptor) iter.next();
+ if (dd.getDependencyId().equals(md.getModuleRevisionId().getModuleId())) {
+ if (md.getResolvedModuleRevisionId().getRevision() == null) {
+ return true;
+ } else if (matcher.accept(dd.getDependencyRevisionId(), md)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public void toIvyFile(File destFile) throws ParseException, IOException {
+ if (parser != null && resource != null) {
+ parser.toIvyFile(resource.openStream(), resource, destFile, this);
+ } else {
+ XmlModuleDescriptorWriter.write(this, destFile);
+ }
+ }
+
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((revId == null) ? 0 : revId.hashCode());
+ return result;
+ }
+
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ DefaultModuleDescriptor other = (DefaultModuleDescriptor) obj;
+ if (revId == null) {
+ if (other.revId != null) {
+ return false;
+ }
+ } else if (!revId.equals(other.revId)) {
+ return false;
+ }
+ return true;
+ }
+
+ public String toString() {
+ return "module: " + revId + " status=" + status + " publication=" + publicationDate
+ + " configurations=" + configurations + " artifacts=" + artifactsByConf
+ + " dependencies=" + dependencies;
+ }
+
+ public void setDefault(boolean b) {
+ isDefault = b;
+ }
+
+ /**
+ * regular expressions as explained in Pattern class may be used in ModuleId organisation and
+ * name
+ *
+ * @param moduleId
+ * @param matcher
+ * @param resolverName
+ */
+ public void addConflictManager(ModuleId moduleId, PatternMatcher matcher,
+ ConflictManager manager) {
+ conflictManagers.defineRule(new MapMatcher(moduleId.getAttributes(), matcher), manager);
+ }
+
+ public ConflictManager getConflictManager(ModuleId moduleId) {
+ return (ConflictManager) conflictManagers.getRule(moduleId);
+ }
+
+ public void addDependencyDescriptorMediator(ModuleId moduleId, PatternMatcher matcher,
+ DependencyDescriptorMediator ddm) {
+ dependencyDescriptorMediators.defineRule(new MapMatcher(moduleId.getAttributes(), matcher),
+ ddm);
+ }
+
+ public DependencyDescriptor mediate(DependencyDescriptor dd) {
+ Object[] mediators = dependencyDescriptorMediators.getRules(dd.getDependencyId());
+ for (int i = 0; i < mediators.length; i++) {
+ dd = ((DependencyDescriptorMediator) mediators[i]).mediate(dd);
+ }
+ return dd;
+ }
+
+ public ModuleRules/* <DependencyDescriptorMediator> */getAllDependencyDescriptorMediators() {
+ return (ModuleRules) dependencyDescriptorMediators.clone();
+ }
+
+ public void addLicense(License license) {
+ licenses.add(license);
+ }
+
+ public License[] getLicenses() {
+ return (License[]) licenses.toArray(new License[licenses.size()]);
+ }
+
+ public String getHomePage() {
+ return homePage;
+ }
+
+ public void setHomePage(String homePage) {
+ this.homePage = homePage;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public long getLastModified() {
+ return lastModified;
+ }
+
+ public void setLastModified(long lastModified) {
+ this.lastModified = lastModified;
+ }
+
+ public Namespace getNamespace() {
+ return namespace;
+ }
+
+ public boolean isNamespaceUseful() {
+ for (Iterator iter = dependencies.iterator(); iter.hasNext();) {
+ DependencyDescriptor dd = (DependencyDescriptor) iter.next();
+ if (dd.getAllExcludeRules().length > 0) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void setNamespace(Namespace ns) {
+ namespace = ns;
+ }
+
+ /**
+ * Throws an exception if the module descriptor is inconsistent For the moment, only extended
+ * configurations existence and cycles are checked
+ */
+ public void check() {
+ Stack confs = new Stack();
+ for (Iterator iter = configurations.values().iterator(); iter.hasNext();) {
+ Configuration conf = (Configuration) iter.next();
+ String[] ext = conf.getExtends();
+ for (int i = 0; i < ext.length; i++) {
+ confs.push(conf.getName());
+ checkConf(confs, ext[i].trim());
+ confs.pop();
+ }
+ }
+ }
+
+ private void checkConf(Stack confs, String confName) {
+ int index = confs.indexOf(confName);
+ if (index != -1) {
+ StringBuffer cycle = new StringBuffer();
+ for (; index < confs.size(); index++) {
+ cycle.append(confs.get(index)).append(" => ");
+ }
+ cycle.append(confName);
+ throw new IllegalStateException("illegal cycle detected in configuration extension: "
+ + cycle);
+ }
+ Configuration conf = getConfiguration(confName);
+ if (conf == null) {
+ throw new IllegalStateException("unknown configuration '" + confName
+ + "'. It is extended by " + confs.get(confs.size() - 1));
+ }
+ String[] ext = conf.getExtends();
+ for (int i = 0; i < ext.length; i++) {
+ confs.push(conf.getName());
+ checkConf(confs, ext[i].trim());
+ confs.pop();
+ }
+ }
+
+ public String getDefaultConf() {
+ return defaultConf;
+ }
+
+ public void setDefaultConf(String defaultConf) {
+ this.defaultConf = defaultConf;
+ }
+
+ public String getDefaultConfMapping() {
+ return defaultConfMapping;
+ }
+
+ public void setDefaultConfMapping(String defaultConfMapping) {
+ this.defaultConfMapping = defaultConfMapping;
+ }
+
+ public void setMappingOverride(boolean override) {
+ mappingOverride = override;
+ }
+
+ public boolean isMappingOverride() {
+ return mappingOverride;
+ }
+
+ public String getAttribute(String attName) {
+ return resolvedRevId.getAttribute(attName);
+ }
+
+ public Map getAttributes() {
+ return resolvedRevId.getAttributes();
+ }
+
+ public String getExtraAttribute(String attName) {
+ return resolvedRevId.getExtraAttribute(attName);
+ }
+
+ public Map getExtraAttributes() {
+ return resolvedRevId.getExtraAttributes();
+ }
+
+ public Map getQualifiedExtraAttributes() {
+ return resolvedRevId.getQualifiedExtraAttributes();
+ }
+
+ public ModuleDescriptorParser getParser() {
+ return parser;
+ }
+
+ public Resource getResource() {
+ return resource;
+ }
+
+ public void addExcludeRule(ExcludeRule rule) {
+ excludeRules.add(rule);
+ }
+
+ public boolean canExclude() {
+ return !excludeRules.isEmpty();
+ }
+
+ /**
+ * only works when namespace is properly set. The behaviour is not specified if namespace is not
+ * set
+ */
+ public boolean doesExclude(String[] moduleConfigurations, ArtifactId artifactId) {
+ if (namespace != null) {
+ artifactId = NameSpaceHelper
+ .transform(artifactId, namespace.getFromSystemTransformer());
+ }
+ ExcludeRule[] rules = getExcludeRules(moduleConfigurations);
+ for (int i = 0; i < rules.length; i++) {
+ if (MatcherHelper.matches(rules[i].getMatcher(), rules[i].getId(), artifactId)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public ExcludeRule[] getAllExcludeRules() {
+ return (ExcludeRule[]) excludeRules.toArray(new ExcludeRule[excludeRules.size()]);
+ }
+
+ public ExcludeRule[] getExcludeRules(String[] moduleConfigurations) {
+ Set rules = new LinkedHashSet();
+ for (Iterator iter = excludeRules.iterator(); iter.hasNext();) {
+ ExcludeRule rule = (ExcludeRule) iter.next();
+ String[] ruleConfs = rule.getConfigurations();
+ if (containsAny(ruleConfs, moduleConfigurations)) {
+ rules.add(rule);
+ }
+ }
+ return (ExcludeRule[]) rules.toArray(new ExcludeRule[rules.size()]);
+ }
+
+ private boolean containsAny(String[] arr1, String[] arr2) {
+ return new ArrayList(Arrays.asList(arr1)).removeAll(Arrays.asList(arr2));
+ }
+
+ public Map getExtraAttributesNamespaces() {
+ return extraAttributesNamespaces;
+ }
+
+ public void addExtraAttributeNamespace(String prefix, String namespace) {
+ extraAttributesNamespaces.put(prefix, namespace);
+ }
+
+ @Deprecated
+ public void addExtraInfo(String infoKey, String value) {
+ extraInfos.add(new ExtraInfoHolder(infoKey, value));
+ }
+
+ @Deprecated
+ public Map<String, String> getExtraInfo() {
+ Map<String, String> map = new HashMap<String, String>();
+ for (ExtraInfoHolder extraInfo : extraInfos) {
+ populateExtraInfoMap(map, extraInfo);
+ }
+ return map;
+ }
+
+ private void populateExtraInfoMap(Map<String, String> map, ExtraInfoHolder extraInfo) {
+ map.put(extraInfo.getName(), extraInfo.getContent());
+ for (ExtraInfoHolder nested : extraInfo.getNestedExtraInfoHolder()) {
+ populateExtraInfoMap(map, nested);
+ }
+ }
+
+ public List<ExtraInfoHolder> getExtraInfos() {
+ return extraInfos;
+ }
+
+ public void addExtraInfo(ExtraInfoHolder extraInfo) {
+ extraInfos.add(extraInfo);
+ }
+
+ public String getExtraInfoContentByTagName(String tagName) {
+ ExtraInfoHolder extraInfoByTagName = getExtraInfoByTagName(tagName);
+ if (extraInfoByTagName != null) {
+ return extraInfoByTagName.getContent();
+ }
+ return null;
+ }
+
+ public ExtraInfoHolder getExtraInfoByTagName(String tagName) {
+ for (ExtraInfoHolder extraInfoHolder : extraInfos) {
+ if (extraInfoHolder.getName().equals(tagName)) {
+ return extraInfoHolder;
+ }
+ }
+ return null;
+
+ }
+}
diff --git a/src/java/org/apache/ivy/core/module/descriptor/DependencyArtifactDescriptor.java b/src/java/org/apache/ivy/core/module/descriptor/DependencyArtifactDescriptor.java
new file mode 100644
index 0000000..1ea78da
--- /dev/null
+++ b/src/java/org/apache/ivy/core/module/descriptor/DependencyArtifactDescriptor.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.module.descriptor;
+
+import java.net.URL;
+
+import org.apache.ivy.util.extendable.ExtendableItem;
+
+/**
+ * This describes an artifact that is asked for a dependency. It is used to define an (additional)
+ * artifact not declared by a dependency module descriptor.
+ */
+public interface DependencyArtifactDescriptor extends ExtendableItem {
+ /**
+ * Returns the dependency descriptor in which this dependency artifact descriptor is declared.
+ *
+ * @return the dependency descriptor in which this dependency artifact descriptor is declared.
+ */
+ public DependencyDescriptor getDependencyDescriptor();
+
+ /**
+ * Returns the name of the artifact asked
+ *
+ * @return
+ */
+ public String getName();
+
+ /**
+ * Returns the type of the artifact asked
+ *
+ * @return
+ */
+ public String getType();
+
+ /**
+ * Returns the ext of the artifact asked
+ *
+ * @return
+ */
+ public String getExt();
+
+ /**
+ * Returns the url to look this artifact at
+ *
+ * @return
+ */
+ public URL getUrl();
+
+ /**
+ * Returns the configurations of the module in which the artifact is asked
+ *
+ * @return an array of configuration names in which the artifact is asked
+ */
+ public String[] getConfigurations();
+}
diff --git a/src/java/org/apache/ivy/core/module/descriptor/DependencyDescriptor.java b/src/java/org/apache/ivy/core/module/descriptor/DependencyDescriptor.java
new file mode 100644
index 0000000..1780d1f
--- /dev/null
+++ b/src/java/org/apache/ivy/core/module/descriptor/DependencyDescriptor.java
@@ -0,0 +1,141 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.module.descriptor;
+
+import org.apache.ivy.core.module.id.ArtifactId;
+import org.apache.ivy.core.module.id.ModuleId;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.plugins.namespace.Namespace;
+import org.apache.ivy.util.extendable.ExtendableItem;
+
+/**
+ * Describes a dependency from a depender to a dependee.
+ * <p>
+ * The main information this descriptor contains is the constraint on the dependency. There is
+ * actually two kind of dependency constraints: the default dependency constraint, which can be
+ * obtained with {@link #getDependencyRevisionId()}, and corresponds to the <code>rev</code>
+ * attribute in Ivy files. This is the constraint as it should be honored by Ivy in default resolve
+ * mode.
+ * </p>
+ * <p>
+ * Then there is the dynamic constraint, which can either be the same as the default constraint, or
+ * the original dependency constraint when an Ivy file is delivered an published to a repository.
+ * This dynamic constraint is returned by {@link #getDynamicConstraintDependencyRevisionId()}, and
+ * corresponds to the <code>revconstraint</code> attribute in the Ivy file. In some resolve mode,
+ * this constraint can be used instead of the default dependency constraint when performing
+ * dependency resolution.
+ * </p>
+ */
+public interface DependencyDescriptor extends ExtendableItem, InheritableItem {
+ ModuleId getDependencyId();
+
+ /**
+ * Used to indicate that this revision must be used in case of conflicts, independently of
+ * conflicts manager. This only works for direct dependencies, and not transitive ones.
+ *
+ * @return true if this dependency should be used, false if conflicts manager can do its work.
+ */
+ boolean isForce();
+
+ /**
+ * Used to indicate that this dependency is a changing one. A changing dependency in ivy means
+ * that the revision may have its artifacts modified without revision change. When new artifacts
+ * are published a new ivy file should also be published with a new publication date to indicate
+ * to ivy that artifacts have changed and that they should be downloaded again.
+ *
+ * @return true if this dependency is a changing one
+ */
+ boolean isChanging();
+
+ boolean isTransitive();
+
+ ModuleRevisionId getParentRevisionId();
+
+ /**
+ * Returns the constraint on dependency this descriptor represents.
+ *
+ * @return the constraint on dependency.
+ */
+ ModuleRevisionId getDependencyRevisionId();
+
+ /**
+ * Returns the dynamic constraint on dependency this descriptor represents.
+ *
+ * @return the dynamic constraint on dependency, or exact constraint if no dynamic constraint is
+ * specified.
+ */
+ ModuleRevisionId getDynamicConstraintDependencyRevisionId();
+
+ String[] getModuleConfigurations();
+
+ String[] getDependencyConfigurations(String moduleConfiguration, String requestedConfiguration);
+
+ String[] getDependencyConfigurations(String moduleConfiguration);
+
+ String[] getDependencyConfigurations(String[] moduleConfigurations);
+
+ Namespace getNamespace();
+
+ DependencyArtifactDescriptor[] getAllDependencyArtifacts();
+
+ DependencyArtifactDescriptor[] getDependencyArtifacts(String moduleConfigurations);
+
+ DependencyArtifactDescriptor[] getDependencyArtifacts(String[] moduleConfigurations);
+
+ IncludeRule[] getAllIncludeRules();
+
+ IncludeRule[] getIncludeRules(String moduleConfigurations);
+
+ IncludeRule[] getIncludeRules(String[] moduleConfigurations);
+
+ ExcludeRule[] getAllExcludeRules();
+
+ ExcludeRule[] getExcludeRules(String moduleConfigurations);
+
+ ExcludeRule[] getExcludeRules(String[] moduleConfigurations);
+
+ /**
+ * Returns true if
+ *
+ * @param moduleConfigurations
+ * @param artifactId
+ * @return
+ */
+ boolean doesExclude(String[] moduleConfigurations, ArtifactId artifactId);
+
+ /**
+ * Returns true if this descriptor contains any exclusion rule
+ *
+ * @return true if this descriptor contains any exclusion rule
+ */
+ public boolean canExclude();
+
+ DependencyDescriptor asSystem();
+
+ /**
+ * Clones current dependency descriptor with another revision.
+ *
+ * @param revision
+ * the revision of the cloned dependency descriptor
+ * @return the cloned dependency descriptor
+ * @throws IllegalArgumentException
+ * if the given {@link ModuleRevisionId} has not the same {@link ModuleId} as the
+ * {@link ModuleRevisionId} of this descriptor.
+ */
+ DependencyDescriptor clone(ModuleRevisionId revision);
+}
diff --git a/src/java/org/apache/ivy/core/module/descriptor/DependencyDescriptorMediator.java b/src/java/org/apache/ivy/core/module/descriptor/DependencyDescriptorMediator.java
new file mode 100644
index 0000000..611f458
--- /dev/null
+++ b/src/java/org/apache/ivy/core/module/descriptor/DependencyDescriptorMediator.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.module.descriptor;
+
+/**
+ * A DependencyDescriptorMediator is responsible for dependency descriptor mediation.
+ * <p>
+ * Dependency descriptor mediation consists in adjusting dependency descriptors according to a
+ * context, environment, the stack of dependers, ...
+ * </p>
+ */
+public interface DependencyDescriptorMediator {
+
+ /**
+ * Mediates the given {@link DependencyDescriptor} according to this {@link ModuleDescriptor}.
+ * <p>
+ * This method gives the opportunity to a ModuleDescriptor to override dependency version
+ * information of any of its transitive dependencies, since it is called by dependency resolvers
+ * before actually resolving a dependency.
+ * </p>
+ *
+ * @param dd
+ * the dependency descriptor which should be mediated.
+ * @return the mediated {@link DependencyDescriptor}, or the original
+ * {@link DependencyDescriptor} if no mediation is required by this ModuleDescriptor.
+ */
+ DependencyDescriptor mediate(DependencyDescriptor dd);
+
+}
diff --git a/src/java/org/apache/ivy/core/module/descriptor/ExcludeRule.java b/src/java/org/apache/ivy/core/module/descriptor/ExcludeRule.java
new file mode 100644
index 0000000..33c82c2
--- /dev/null
+++ b/src/java/org/apache/ivy/core/module/descriptor/ExcludeRule.java
@@ -0,0 +1,50 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.module.descriptor;
+
+import org.apache.ivy.core.module.id.ArtifactId;
+import org.apache.ivy.plugins.matcher.PatternMatcher;
+import org.apache.ivy.util.extendable.ExtendableItem;
+
+/**
+ * This describes a rule of exclusion. It is used to restrict the artifacts asked for a dependency,
+ * by excluding a whole module or some artifacts of a dependency.
+ */
+public interface ExcludeRule extends ExtendableItem {
+
+ /**
+ * Returns the id of the described artifact, without revision information
+ *
+ * @return
+ */
+ public ArtifactId getId();
+
+ /**
+ * Returns the configurations of the module in which the artifact is asked
+ *
+ * @return an array of configuration names in which the artifact is asked
+ */
+ public String[] getConfigurations();
+
+ /**
+ * Returns the matcher to use to know if an artifact match the current descriptor
+ *
+ * @return
+ */
+ public PatternMatcher getMatcher();
+}
diff --git a/src/java/org/apache/ivy/core/module/descriptor/ExtendsDescriptor.java b/src/java/org/apache/ivy/core/module/descriptor/ExtendsDescriptor.java
new file mode 100644
index 0000000..8b7456d
--- /dev/null
+++ b/src/java/org/apache/ivy/core/module/descriptor/ExtendsDescriptor.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.module.descriptor;
+
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+
+/**
+ * Describes parent descriptor information for a module descriptor.
+ */
+public interface ExtendsDescriptor {
+
+ /** get the module revision id of the declared parent descriptor */
+ public ModuleRevisionId getParentRevisionId();
+
+ /**
+ * get the resolved revision id for {@link #getParentRevisionId}, see
+ * {@link org.apache.ivy.core.module.descriptor.ModuleDescriptor#getResolvedModuleRevisionId()}
+ */
+ public ModuleRevisionId getResolvedParentRevisionId();
+
+ public ModuleDescriptor getParentMd();
+
+ /**
+ * If there is an explicit path to check for the parent descriptor, return it. Otherwise returns
+ * null.
+ */
+ public String getLocation();
+
+ /**
+ * Get the parts of the parent descriptor that are inherited. Default supported types are
+ * <code>info</code>, <code>description</code>, <code>configurations</code>,
+ * <code>dependencies</code>, and/or <code>all</code>. Ivy extensions may add support for
+ * additional extends types.
+ */
+ public String[] getExtendsTypes();
+
+ /** @return true if the <code>all</code> extend type is specified, implying all other types */
+ public boolean isAllInherited();
+
+ /** @return true if parent info attributes are inherited (organisation, branch, revision, etc) */
+ public boolean isInfoInherited();
+
+ /** @return true if parent description is inherited */
+ public boolean isDescriptionInherited();
+
+ /** @return true if parent configurations are inherited */
+ public boolean areConfigurationsInherited();
+
+ /** @return true if parent dependencies are inherited */
+ public boolean areDependenciesInherited();
+
+ public boolean isLocal();
+}
diff --git a/src/java/org/apache/ivy/core/module/descriptor/ExtraInfoHolder.java b/src/java/org/apache/ivy/core/module/descriptor/ExtraInfoHolder.java
new file mode 100644
index 0000000..1422378
--- /dev/null
+++ b/src/java/org/apache/ivy/core/module/descriptor/ExtraInfoHolder.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.module.descriptor;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+public class ExtraInfoHolder {
+
+ private String name;
+
+ private Map<String, String> attributes = new LinkedHashMap<String, String>();
+
+ private String content;
+
+ private List<ExtraInfoHolder> nestedExtraInfoHolder = new ArrayList<ExtraInfoHolder>();
+
+ public ExtraInfoHolder() {
+
+ }
+
+ public ExtraInfoHolder(String name, String content) {
+ this.name = name;
+ this.content = content;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Map<String, String> getAttributes() {
+ return attributes;
+ }
+
+ public void setAttributes(Map<String, String> attributes) {
+ this.attributes = attributes;
+ }
+
+ public String getContent() {
+ return content;
+ }
+
+ public void setContent(String content) {
+ this.content = content;
+ }
+
+ public List<ExtraInfoHolder> getNestedExtraInfoHolder() {
+ return nestedExtraInfoHolder;
+ }
+
+ public void setNestedExtraInfoHolder(List<ExtraInfoHolder> nestedExtraInfoHolder) {
+ this.nestedExtraInfoHolder = nestedExtraInfoHolder;
+ }
+
+}
diff --git a/src/java/org/apache/ivy/core/module/descriptor/IncludeRule.java b/src/java/org/apache/ivy/core/module/descriptor/IncludeRule.java
new file mode 100644
index 0000000..7cbeada
--- /dev/null
+++ b/src/java/org/apache/ivy/core/module/descriptor/IncludeRule.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.module.descriptor;
+
+import org.apache.ivy.core.module.id.ArtifactId;
+import org.apache.ivy.plugins.matcher.PatternMatcher;
+import org.apache.ivy.util.extendable.ExtendableItem;
+
+/**
+ * This describes a rule of inclusion. It is used to resctrict the artifacts and modules asked for a
+ * dependency, by including only modules and artifacts matching the rule
+ */
+public interface IncludeRule extends ExtendableItem {
+
+ /**
+ * Returns the id of the described artifact, without revision information
+ *
+ * @return
+ */
+ public ArtifactId getId();
+
+ /**
+ * Returns the configurations of the module in which the artifact is asked
+ *
+ * @return an array of configuration names in which the artifact is asked
+ */
+ public String[] getConfigurations();
+
+ /**
+ * Returns the matcher to use to know if an artifact match the current descriptor
+ *
+ * @return
+ */
+ public PatternMatcher getMatcher();
+
+}
diff --git a/src/java/org/apache/ivy/core/module/descriptor/InheritableItem.java b/src/java/org/apache/ivy/core/module/descriptor/InheritableItem.java
new file mode 100644
index 0000000..07eae0c
--- /dev/null
+++ b/src/java/org/apache/ivy/core/module/descriptor/InheritableItem.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.module.descriptor;
+
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+
+/**
+ * Interface for elements that can be inherited from a parent descriptor by a child descriptor.
+ *
+ * @see Configuration
+ * @see DependencyDescriptor
+ */
+public interface InheritableItem {
+ /**
+ * @return the module in which this item was actually defined, if different from the module in
+ * which the item appears. May be null.
+ */
+ public ModuleRevisionId getSourceModule();
+}
diff --git a/src/java/org/apache/ivy/core/module/descriptor/License.java b/src/java/org/apache/ivy/core/module/descriptor/License.java
new file mode 100644
index 0000000..db1c823
--- /dev/null
+++ b/src/java/org/apache/ivy/core/module/descriptor/License.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.module.descriptor;
+
+public class License {
+ private String name;
+
+ private String url;
+
+ public License(String name, String url) {
+ this.name = name;
+ this.url = url;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getUrl() {
+ return url;
+ }
+
+}
diff --git a/src/java/org/apache/ivy/core/module/descriptor/MDArtifact.java b/src/java/org/apache/ivy/core/module/descriptor/MDArtifact.java
new file mode 100644
index 0000000..0efabbd
--- /dev/null
+++ b/src/java/org/apache/ivy/core/module/descriptor/MDArtifact.java
@@ -0,0 +1,126 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.module.descriptor;
+
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.ivy.core.module.id.ArtifactRevisionId;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+
+/**
+ *
+ */
+public class MDArtifact extends AbstractArtifact {
+
+ public static Artifact newIvyArtifact(ModuleDescriptor md) {
+ return new MDArtifact(md, "ivy", "ivy", "xml", true);
+ }
+
+ private ModuleDescriptor md;
+
+ private String name;
+
+ private String type;
+
+ private String ext;
+
+ private List/* <String> */confs = new ArrayList();
+
+ private Map extraAttributes = null;
+
+ private URL url;
+
+ private boolean isMetadata = false;
+
+ public MDArtifact(ModuleDescriptor md, String name, String type, String ext) {
+ this(md, name, type, ext, null, null);
+ }
+
+ public MDArtifact(ModuleDescriptor md, String name, String type, String ext, boolean isMetadata) {
+ this(md, name, type, ext, null, null);
+ this.isMetadata = isMetadata;
+ }
+
+ public MDArtifact(ModuleDescriptor md, String name, String type, String ext, URL url,
+ Map extraAttributes) {
+ if (md == null) {
+ throw new NullPointerException("null module descriptor not allowed");
+ }
+ if (name == null) {
+ throw new NullPointerException("null name not allowed");
+ }
+ if (type == null) {
+ throw new NullPointerException("null type not allowed");
+ }
+ if (ext == null) {
+ throw new NullPointerException("null ext not allowed");
+ }
+ this.md = md;
+ this.name = name;
+ this.type = type;
+ this.ext = ext;
+ this.url = url;
+ this.extraAttributes = extraAttributes;
+ }
+
+ public ModuleRevisionId getModuleRevisionId() {
+ return md.getResolvedModuleRevisionId();
+ }
+
+ public Date getPublicationDate() {
+ return md.getResolvedPublicationDate();
+ }
+
+ public ArtifactRevisionId getId() {
+ // do not cache the result because the resolvedModuleRevisionId can change!
+ return ArtifactRevisionId.newInstance(md.getResolvedModuleRevisionId(), name, type, ext,
+ extraAttributes);
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public String getExt() {
+ return ext;
+ }
+
+ public String[] getConfigurations() {
+ return (String[]) confs.toArray(new String[confs.size()]);
+ }
+
+ public void addConfiguration(String conf) {
+ confs.add(conf);
+ }
+
+ public URL getUrl() {
+ return url;
+ }
+
+ public boolean isMetadata() {
+ return isMetadata;
+ }
+}
diff --git a/src/java/org/apache/ivy/core/module/descriptor/ModuleDescriptor.java b/src/java/org/apache/ivy/core/module/descriptor/ModuleDescriptor.java
new file mode 100644
index 0000000..4eb4a1e
--- /dev/null
+++ b/src/java/org/apache/ivy/core/module/descriptor/ModuleDescriptor.java
@@ -0,0 +1,293 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.module.descriptor;
+
+import java.io.File;
+import java.io.IOException;
+import java.text.ParseException;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.ivy.core.module.id.ArtifactId;
+import org.apache.ivy.core.module.id.ModuleId;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.module.id.ModuleRules;
+import org.apache.ivy.plugins.conflict.ConflictManager;
+import org.apache.ivy.plugins.latest.ArtifactInfo;
+import org.apache.ivy.plugins.parser.ModuleDescriptorParser;
+import org.apache.ivy.plugins.repository.Resource;
+import org.apache.ivy.plugins.version.VersionMatcher;
+import org.apache.ivy.util.extendable.ExtendableItem;
+
+/**
+ * Descriptor of a module. This is the Java representation of an ivy.xml
+ */
+public interface ModuleDescriptor extends ExtendableItem, ArtifactInfo,
+ DependencyDescriptorMediator {
+
+ public static final String DEFAULT_CONFIGURATION = "default";
+
+ public static final String CALLER_ALL_CONFIGURATION = "all";
+
+ /**
+ * Returns true if this descriptor is a default one, i.e. one generated for a module not
+ * actually having one.
+ *
+ * @return
+ */
+ boolean isDefault();
+
+ ModuleRevisionId getModuleRevisionId();
+
+ /**
+ * The module revision id returned here is the resolved one, i.e. it is never a latest one. If
+ * the revision has not been resolved, a null revision should be returned by getRevision() of
+ * the returned ModuleRevisionId. This revision must be the same as the module descriptor
+ * resolved revision id unless no module descriptor is defined
+ *
+ * @return
+ */
+ ModuleRevisionId getResolvedModuleRevisionId();
+
+ /**
+ * This method update the resolved module revision id
+ *
+ * @param revId
+ */
+ void setResolvedModuleRevisionId(ModuleRevisionId revId);
+
+ /**
+ * Get the list of parent descriptors imported via an <extends> element. Only directly
+ * imported descriptors are included; the parent's parents are not included.
+ */
+ ExtendsDescriptor[] getInheritedDescriptors();
+
+ /**
+ * This method update the resolved publication date
+ *
+ * @param publicationDate
+ */
+ void setResolvedPublicationDate(Date publicationDate);
+
+ String getStatus();
+
+ /**
+ * May be <code>null</code> if unknown in the descriptor itself.
+ *
+ * @return The publication date or <code>null</code> when not knwon.
+ */
+ Date getPublicationDate();
+
+ /**
+ * The publication date of the module revision should be the date at which it has been
+ * published, i.e. in general the date of any of its published artifacts, since all published
+ * artifact of a module should follow the same publishing cycle.
+ */
+ Date getResolvedPublicationDate();
+
+ /**
+ * Returns all the configurations declared by this module as an array. This array is never empty
+ * (a 'default' conf is assumed when none is declared in the ivy file)
+ *
+ * @return all the configurations declared by this module as an array.
+ */
+ Configuration[] getConfigurations();
+
+ String[] getConfigurationsNames();
+
+ String[] getPublicConfigurationsNames();
+
+ Artifact[] getArtifacts(String conf);
+
+ /**
+ * Returns all artifacts of this module, excluding the artifact corresponding to the module
+ * descriptor.
+ *
+ * @return all published artifacts of this module
+ * @see #getMetadataArtifact()
+ */
+ Artifact[] getAllArtifacts();
+
+ /**
+ * @retun The dependencies of the module. If there is no dependencies return an empty array (non
+ * null)
+ */
+ DependencyDescriptor[] getDependencies();
+
+ /**
+ * Returns true if the module described by this descriptor dependes directly upon the given
+ * module descriptor
+ *
+ * @param md
+ * @return
+ */
+ boolean dependsOn(VersionMatcher matcher, ModuleDescriptor md);
+
+ /**
+ * @param confName
+ * @return
+ */
+ Configuration getConfiguration(String confName);
+
+ /**
+ * Returns the conflict manager to use for the given ModuleId, or <code>null</code> if no
+ * specific conflict manager is associated with the given module id in this module descriptor.
+ *
+ * @param id
+ * @return
+ */
+ ConflictManager getConflictManager(ModuleId id);
+
+ /**
+ * Returns the licenses of the module described by this descriptor
+ *
+ * @return
+ */
+ License[] getLicenses();
+
+ String getHomePage();
+
+ String getDescription();
+
+ long getLastModified();
+
+ /**
+ * Writes this module descriptor as an ivy file. If this descriptor was obtained through the
+ * parsing of an ivy file, it should keep the layout of the file the most possible similar to
+ * the original one.
+ *
+ * @param ivyFile
+ * the destination ivy file
+ */
+ void toIvyFile(File ivyFile) throws ParseException, IOException;
+
+ /**
+ * The ModuleDescriptorParser used to parse this module descriptor, null is no parser was used.
+ *
+ * @return
+ */
+ ModuleDescriptorParser getParser();
+
+ /**
+ * The resource being the source of this module descriptor, null if no resource corresponds to
+ * this module descriptor
+ *
+ * @return
+ */
+ Resource getResource();
+
+ /**
+ * Returns the Artifact representing this module descriptor itself.
+ * <p>
+ * Even though the module descriptor is never described as a published artifact of a module in
+ * the module descriptor itself, it is often useful to consider it as any other artifact of the
+ * module. This method allows to access to the Artifact object representing this module
+ * descriptor for this purpose.
+ * </p>
+ *
+ * @return the Artifact representing this module descriptor itself.
+ */
+ Artifact getMetadataArtifact();
+
+ /**
+ * Returns true if this descriptor contains any exclusion rule
+ *
+ * @return true if this descriptor contains any exclusion rule
+ */
+ boolean canExclude();
+
+ /**
+ * Returns true if an exclude rule of this module attached to any of the given configurations
+ * matches the given artifact id, and thus exclude it
+ *
+ * @param moduleConfs
+ * @param artifactId
+ * @return
+ */
+ boolean doesExclude(String[] moduleConfs, ArtifactId artifactId);
+
+ /**
+ * Returns an array of all the exclude rules this module descriptor currently holds. Module
+ * Descriptor exclude rules are used to exclude (usually transitive) dependencies for the whole
+ * module.
+ *
+ * @return an array of {@link ExcludeRule} this module descriptor holds
+ */
+ public ExcludeRule[] getAllExcludeRules();
+
+ /**
+ * Returns all the dependency descriptor mediators used by this {@link ModuleDescriptor}, as an
+ * instance of {@link ModuleRules}.
+ * <p>
+ * All rules in the {@link ModuleRules} object returned are {@link DependencyDescriptorMediator}
+ * .
+ * </p>
+ *
+ * @return all the dependency descriptor mediators used by this {@link ModuleDescriptor}.
+ */
+ public ModuleRules/* <DependencyDescriptorMediator> */getAllDependencyDescriptorMediators();
+
+ /**
+ * Returns the list of xml namespaces used by extra attributes, as Map from prefix to namespace
+ * URIs.
+ * <p>
+ * The returned list is never <code>null</code>, it is empty when no extra attribute is used or
+ * if extra attributes are used without xml namespaces
+ * </p>
+ *
+ * @return the list of xml namespaces used by extra attributes, as Map from prefix to namespace
+ * URIs.
+ */
+ Map<String, String> getExtraAttributesNamespaces();
+
+ /**
+ * Returns the custom info provided in the info tag. All the tags except the description are
+ * given. The key is the name of the tag, the value is its content. <br />
+ *
+ * @deprecated this method is not returning the full content of the extra info: to get the full
+ * structure of the extra infos, use getExtraInfos()
+ */
+ @Deprecated
+ Map<String, String> getExtraInfo();
+
+ /**
+ * Returns a list of extras infos (tag name, attributes and content). All the tags except the
+ * description are given.
+ *
+ * @since 2.4.0
+ * @return
+ */
+ List<ExtraInfoHolder> getExtraInfos();
+
+ /**
+ * Returns content from first extrainfo matching with given tag name
+ *
+ * @since 2.4.0
+ * @return
+ */
+ String getExtraInfoContentByTagName(String tagName);
+
+ /**
+ * Returns first extrainfo matching with given tag name
+ *
+ * @since 2.4.0
+ * @return
+ */
+ ExtraInfoHolder getExtraInfoByTagName(String tagName);
+}
diff --git a/src/java/org/apache/ivy/core/module/descriptor/OverrideDependencyDescriptorMediator.java b/src/java/org/apache/ivy/core/module/descriptor/OverrideDependencyDescriptorMediator.java
new file mode 100644
index 0000000..cbf7482
--- /dev/null
+++ b/src/java/org/apache/ivy/core/module/descriptor/OverrideDependencyDescriptorMediator.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.module.descriptor;
+
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+
+/**
+ * DependencyDescriptorMediator used to override some dependency descriptors values, such as the
+ * branch or version of the dependency.
+ */
+public class OverrideDependencyDescriptorMediator implements DependencyDescriptorMediator {
+ private String version;
+
+ private String branch;
+
+ /**
+ * Constructs a new instance.
+ *
+ * @param branch
+ * the branch to give to mediated dependency descriptors, <code>null</code> to keep
+ * the original branch.
+ * @param version
+ * the version to give to mediated dependency descriptors, <code>null</code> to keep
+ * the original one.
+ */
+ public OverrideDependencyDescriptorMediator(String branch, String version) {
+ this.branch = branch;
+ this.version = version;
+ }
+
+ /**
+ * Returns the version this mediator will give to mediated descriptors, or <code>null</code> if
+ * this mediator does not override version.
+ *
+ * @return the version this mediator will give to mediated descriptors.
+ */
+ public String getVersion() {
+ return version;
+ }
+
+ /**
+ * Returns the branch this mediator will give to mediated descriptors, or <code>null</code> if
+ * this mediator does not override branch.
+ *
+ * @return the branch this mediator will give to mediated descriptors.
+ */
+ public String getBranch() {
+ return branch;
+ }
+
+ public DependencyDescriptor mediate(DependencyDescriptor dd) {
+ ModuleRevisionId mrid = dd.getDependencyRevisionId();
+ if ((version == null || version.equals(mrid.getRevision()))
+ && (branch == null || branch.equals(mrid.getBranch()))) {
+ return dd;
+ }
+
+ String version = this.version == null ? mrid.getRevision() : this.version;
+ String branch = this.branch == null ? mrid.getBranch() : this.branch;
+
+ // if this is a noop, do not construct any new object
+ if (version.equals(dd.getDependencyRevisionId().getRevision())
+ && branch.equals(dd.getDependencyRevisionId().getBranch())) {
+ return dd;
+ }
+
+ return dd.clone(ModuleRevisionId.newInstance(mrid.getOrganisation(), mrid.getName(),
+ branch, version, mrid.getQualifiedExtraAttributes()));
+ }
+}
diff --git a/src/java/org/apache/ivy/core/module/id/ArtifactId.java b/src/java/org/apache/ivy/core/module/id/ArtifactId.java
new file mode 100644
index 0000000..0991c1f
--- /dev/null
+++ b/src/java/org/apache/ivy/core/module/id/ArtifactId.java
@@ -0,0 +1,107 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.module.id;
+
+/**
+ * Identifies an artifact in a module, without revision information
+ *
+ * @see <a href="package-summary.html">org.apache.ivy.core.module.id</a>
+ */
+public class ArtifactId {
+ private ModuleId mid;
+
+ private String name;
+
+ private String type;
+
+ private String ext;
+
+ /**
+ * @param mid
+ * The ModuleId, which is the base of this artifact.
+ * @param name
+ * The name of the artifact.
+ * @param type
+ * The type of the artifact.
+ */
+ public ArtifactId(ModuleId mid, String name, String type, String ext) {
+ this.mid = mid;
+ this.name = name;
+ this.type = type;
+ this.ext = ext;
+ }
+
+ /** {@inheritDoc} */
+ public boolean equals(Object obj) {
+ if (!(obj instanceof ArtifactId)) {
+ return false;
+ }
+ ArtifactId aid = (ArtifactId) obj;
+ return getModuleId().equals(aid.getModuleId()) && getName().equals(aid.getName())
+ && getExt().equals(aid.getExt()) && getType().equals(aid.getType());
+ }
+
+ /** {@inheritDoc} */
+ public int hashCode() {
+ // CheckStyle:MagicNumber| OFF
+ int hash = 17;
+ hash += getModuleId().hashCode() * 37;
+ hash += getName().hashCode() * 37;
+ hash += getType().hashCode() * 37;
+ // CheckStyle:MagicNumber| OFF
+ return hash;
+ }
+
+ /** {@inheritDoc} */
+ public String toString() {
+ return getModuleId() + "!" + getShortDescription();
+ }
+
+ public String getShortDescription() {
+ return getName() + "." + getExt()
+ + (getType().equals(getExt()) ? "" : "(" + getType() + ")");
+ }
+
+ /**
+ * @return Returns the module id.
+ */
+ public ModuleId getModuleId() {
+ return mid;
+ }
+
+ /**
+ * @return Returns the name.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * @return Returns the type.
+ */
+ public String getType() {
+ return type;
+ }
+
+ /**
+ * @return Returns the ext.
+ */
+ public String getExt() {
+ return ext;
+ }
+}
diff --git a/src/java/org/apache/ivy/core/module/id/ArtifactRevisionId.java b/src/java/org/apache/ivy/core/module/id/ArtifactRevisionId.java
new file mode 100644
index 0000000..ca34585
--- /dev/null
+++ b/src/java/org/apache/ivy/core/module/id/ArtifactRevisionId.java
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.module.id;
+
+import java.util.Map;
+
+import org.apache.ivy.core.IvyPatternHelper;
+import org.apache.ivy.util.extendable.UnmodifiableExtendableItem;
+
+/**
+ * Identifies an artifact in a particular module revision
+ *
+ * @see <a href="package-summary.html">org.apache.ivy.core.module.id</a>
+ */
+public class ArtifactRevisionId extends UnmodifiableExtendableItem {
+ public static ArtifactRevisionId newInstance(ModuleRevisionId mrid, String name, String type,
+ String ext) {
+ return newInstance(mrid, name, type, ext, null);
+ }
+
+ public static ArtifactRevisionId newInstance(ModuleRevisionId mrid, String name, String type,
+ String ext, Map extraAttributes) {
+ return new ArtifactRevisionId(new ArtifactId(mrid.getModuleId(), name, type, ext), mrid,
+ extraAttributes);
+ }
+
+ private ArtifactId artifactId;
+
+ private ModuleRevisionId mrid;
+
+ public ArtifactRevisionId(ArtifactId artifactId, ModuleRevisionId mrid) {
+ this(artifactId, mrid, null);
+ }
+
+ public ArtifactRevisionId(ArtifactId artfId, ModuleRevisionId mdlRevId, Map extraAttributes) {
+ super(null, extraAttributes);
+ artifactId = artfId;
+ mrid = mdlRevId;
+
+ setStandardAttribute(IvyPatternHelper.ORGANISATION_KEY, getModuleRevisionId()
+ .getOrganisation());
+ setStandardAttribute(IvyPatternHelper.MODULE_KEY, getModuleRevisionId().getName());
+ setStandardAttribute(IvyPatternHelper.REVISION_KEY, getModuleRevisionId().getRevision());
+ setStandardAttribute(IvyPatternHelper.ARTIFACT_KEY, getName());
+ setStandardAttribute(IvyPatternHelper.TYPE_KEY, getType());
+ setStandardAttribute(IvyPatternHelper.EXT_KEY, getExt());
+ }
+
+ public boolean equals(Object obj) {
+ if (!(obj instanceof ArtifactRevisionId)) {
+ return false;
+ }
+ ArtifactRevisionId arid = (ArtifactRevisionId) obj;
+ return getArtifactId().equals(arid.getArtifactId())
+ && getModuleRevisionId().equals(arid.getModuleRevisionId())
+ && getQualifiedExtraAttributes().equals(arid.getQualifiedExtraAttributes());
+ }
+
+ public int hashCode() {
+ // WARN: uniqueness needs to be relatively strong here
+ // CheckStyle:MagicNumber| OFF
+ int hash = 17;
+ hash += getArtifactId().hashCode() * 37;
+ hash += getModuleRevisionId().hashCode() * 37;
+ hash += getQualifiedExtraAttributes().hashCode() * 37;
+ // CheckStyle:MagicNumber| ON
+
+ return hash;
+ }
+
+ public String toString() {
+ return getModuleRevisionId() + "!" + getName() + "." + getExt()
+ + (getType().equals(getExt()) ? "" : "(" + getType() + ")");
+ }
+
+ /**
+ * @return Returns the artifactId.
+ */
+ public ArtifactId getArtifactId() {
+ return artifactId;
+ }
+
+ public ModuleRevisionId getModuleRevisionId() {
+ return mrid;
+ }
+
+ public String getName() {
+ return artifactId.getName();
+ }
+
+ public String getType() {
+ return artifactId.getType();
+ }
+
+ public String getExt() {
+ return artifactId.getExt();
+ }
+
+ /**
+ * @return Returns the revision.
+ */
+ public String getRevision() {
+ return mrid.getRevision();
+ }
+
+}
diff --git a/src/java/org/apache/ivy/core/module/id/MatcherLookup.java b/src/java/org/apache/ivy/core/module/id/MatcherLookup.java
new file mode 100644
index 0000000..76a44fc
--- /dev/null
+++ b/src/java/org/apache/ivy/core/module/id/MatcherLookup.java
@@ -0,0 +1,153 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.module.id;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.ivy.core.IvyPatternHelper;
+import org.apache.ivy.plugins.matcher.ExactPatternMatcher;
+import org.apache.ivy.plugins.matcher.MapMatcher;
+import org.apache.ivy.plugins.matcher.PatternMatcher;
+
+/**
+ * This class targets to speed up lookup for exact pattern matcher by keys, which are created with
+ * (organization, module) information. When exact pattern matcher is added, the key is created from
+ * matcher's attributes. When matcher is looked up against specific module, the key is recreated
+ * from module's attributes.
+ * <p>
+ * </p>
+ * The lookup doesn't target to speed up lookup for non exact pattern matcher. All non exact
+ * matchers are placed in non-keyed collection.
+ * <p>
+ * </p>
+ * At lookup for matchers against specific module, all non exact pattern matchers are iterated to
+ * match with module attributes, and exact pattern matchers binding to the same key will also
+ * iterated to match with module attributes.
+ * <p>
+ * </p>
+ * If there are much more exact pattern matchers than non exact pattern matchers, the matcher lookup
+ * speed can benefit from this class significantly. A quick example could be user declares lots of
+ * dependencyOverrides which are typically exact pattern matchers.
+ * <p>
+ * </p>
+ * If there are balanced exact and non exact pattern matchers, the matcher lookup speed doesn't hurt
+ * by this class.
+ * <p>
+ * </p>
+ */
+public class MatcherLookup {
+
+ //private static final String FORMAT = "{org:%s, module:%s}";
+
+ private static final String DEFAULT = "{org:" + "default" + ", module:" + "default" + "}";
+
+ private Map/* <String, List<MapMatcher>> */lookup = new HashMap();
+
+ private List/* <MapMatcher> */non_exact_matchers = new ArrayList();
+
+ /**
+ * Add matcher.
+ *
+ * If matcher is exact pattern matcher, it will be associated with a key and placed in keyed
+ * collection.
+ *
+ * If matcher is not exact pattern matcher, it will be placed into non-keyed collection
+ *
+ * @param matcher
+ */
+ public void add(MapMatcher matcher) {
+ if (!(matcher.getPatternMatcher() instanceof ExactPatternMatcher)) {
+ non_exact_matchers.add(matcher);
+ return;
+ }
+ Object key = key(matcher.getAttributes());
+ List exact_matchers = (List) lookup.get(key);
+ if (exact_matchers == null) {
+ exact_matchers = new ArrayList();
+ lookup.put(key, exact_matchers);
+ }
+ exact_matchers.add(matcher);
+ }
+
+ /**
+ * Get a list of matchers which can apply to module with specified attributes
+ *
+ * @param attrs
+ * A map of attributes that matcher should match.
+ *
+ * @return list A list of candidate matchers that matches specified attributes
+ */
+ public List get(Map attrs) {
+ List matchers = new ArrayList();
+ // Step 1: find matchers from non_exact_matchers list
+ if (!non_exact_matchers.isEmpty()) {
+ for (Iterator iter = non_exact_matchers.iterator(); iter.hasNext();) {
+ MapMatcher matcher = (MapMatcher) iter.next();
+ if (matcher.matches(attrs)) {
+ matchers.add(matcher);
+ }
+ }
+ }
+ // Step 2: find matchers from exact_matchers list of key
+ Object key = key(attrs);
+ List exact_matchers = (List) lookup.get(key);
+ if (exact_matchers != null) {
+ for (Iterator iter = exact_matchers.iterator(); iter.hasNext();) {
+ MapMatcher matcher = (MapMatcher) iter.next();
+ if (matcher.matches(attrs)) {
+ matchers.add(matcher);
+ }
+ }
+ }
+ // Step 3: (iff key != DEFAULT) find matchers from exact_matchers of DEFAULT
+ if (key != DEFAULT) {
+ List default_exact_matchers = (List) lookup.get(DEFAULT);
+ if (default_exact_matchers != null) {
+ for (Iterator iter = default_exact_matchers.iterator(); iter.hasNext();) {
+ MapMatcher matcher = (MapMatcher) iter.next();
+ if (matcher.matches(attrs)) {
+ matchers.add(matcher);
+ }
+ }
+ }
+ }
+ return matchers;
+ }
+
+ /**
+ * Create a key from specified attributes
+ *
+ * @param attrs
+ * A map of attributes
+ * @return key object
+ */
+ private Object key(Map attrs) {
+ Object org = attrs.get(IvyPatternHelper.ORGANISATION_KEY);
+ Object module = attrs.get(IvyPatternHelper.MODULE_KEY);
+ if (org == null || PatternMatcher.ANY_EXPRESSION.equals(org) || module == null
+ || PatternMatcher.ANY_EXPRESSION.equals(module)) {
+ return DEFAULT;
+ }
+ return "{org:" + org + ", module:" + module + "}";
+ }
+
+}
diff --git a/src/java/org/apache/ivy/core/module/id/ModuleId.java b/src/java/org/apache/ivy/core/module/id/ModuleId.java
new file mode 100644
index 0000000..4492827
--- /dev/null
+++ b/src/java/org/apache/ivy/core/module/id/ModuleId.java
@@ -0,0 +1,230 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.module.id;
+
+import java.lang.ref.WeakReference;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.WeakHashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.ivy.core.IvyPatternHelper;
+
+/**
+ * Identifies a module, without revision information
+ *
+ * @see <a href="package-summary.html">org.apache.ivy.core.module.id</a>
+ */
+public class ModuleId implements Comparable {
+ static final String ENCODE_SEPARATOR = ":#@#:";
+
+ private static final Map/* <ModuleId, WeakReference<ModuleId>> */CACHE = new WeakHashMap();
+
+ /**
+ * Returns a ModuleId for the given organization and module name.
+ *
+ * @param org
+ * the module's organization, can be <code>null</code>
+ * @param name
+ * the module's name, must not be <code>null</code>
+ * @return a ModuleId instance
+ */
+ public static ModuleId newInstance(String org, String name) {
+ return intern(new ModuleId(org, name));
+ }
+
+ /**
+ * Returns an intern instance of a ModuleId equals to the given ModuleId if any, or the given
+ * ModuleId.
+ * <p>
+ * This is useful to reduce the number of instances of ModuleId kept in memory, and thus reduce
+ * memory footprint.
+ * </p>
+ *
+ * @param moduleId
+ * the module id to return
+ * @return a unit instance of the given module id.
+ */
+ public static ModuleId intern(ModuleId moduleId) {
+ ModuleId r = null;
+
+ synchronized (CACHE) {
+ WeakReference ref = (WeakReference) CACHE.get(moduleId);
+ if (ref != null) {
+ r = (ModuleId) ref.get();
+ }
+ if (r == null) {
+ r = moduleId;
+ CACHE.put(r, new WeakReference(r));
+ }
+ }
+
+ return r;
+ }
+
+ private String organisation;
+
+ private String name;
+
+ private int hash;
+
+ private Map/* <String, String> */attributes = new HashMap();
+
+ /**
+ * Constructor.
+ *
+ * @param organisation
+ * The organisation which creates the module.
+ * @param name
+ * The name of the module.
+ */
+ public ModuleId(String organisation, String name) {
+ if (name == null) {
+ throw new IllegalArgumentException("null name not allowed");
+ }
+ this.organisation = organisation;
+ this.name = name;
+ attributes.put(IvyPatternHelper.ORGANISATION_KEY, organisation);
+ attributes.put(IvyPatternHelper.MODULE_KEY, name);
+ }
+
+ /**
+ * Returns the name of the module.
+ *
+ * @return The name of the module.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Returns the name of the organisation.
+ *
+ * @return The name of the organisation.
+ */
+ public String getOrganisation() {
+ return organisation;
+ }
+
+ /** {@inheritDoc} */
+ public boolean equals(Object obj) {
+ if (!(obj instanceof ModuleId)) {
+ return false;
+ }
+ ModuleId other = (ModuleId) obj;
+ if (other.organisation == null) {
+ return organisation == null && other.name.equals(name);
+ } else {
+ return other.organisation.equals(organisation) && other.name.equals(name);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public int hashCode() {
+ if (hash == 0) {
+ // CheckStyle:MagicNumber| OFF
+ hash = 31;
+ hash = hash * 13 + (organisation == null ? 0 : organisation.hashCode());
+ hash = hash * 13 + name.hashCode();
+ // CheckStyle:MagicNumber| ON
+ }
+ return hash;
+ }
+
+ /** {@inheritDoc} */
+ public String toString() {
+ return organisation + "#" + name;
+ }
+
+ /** {@inheritDoc} */
+ public int compareTo(Object obj) {
+ ModuleId that = (ModuleId) obj;
+ int result = organisation.compareTo(that.organisation);
+ if (result == 0) {
+ result = name.compareTo(that.name);
+ }
+ return result;
+ }
+
+ /**
+ * Returns the encoded String representing this ModuleId.
+ *
+ * @return The ModuleId encoded as String.
+ */
+ public String encodeToString() {
+ return getOrganisation() + ENCODE_SEPARATOR + getName();
+ }
+
+ /**
+ * Returns a Map of all attributes of this module id. The Map keys are attribute names as
+ * Strings, and values are corresponding attribute values (as String too).
+ *
+ * @return A Map instance containing all the attributes and their values.
+ */
+ public Map getAttributes() {
+ return attributes;
+ }
+
+ /**
+ * Returns a ModuleId
+ *
+ * @param encoded
+ * @return The new ModuleId.
+ * @throws IllegalArgumentException
+ * If the given String could not be decoded.
+ */
+ public static ModuleId decode(String encoded) {
+ String[] parts = encoded.split(ENCODE_SEPARATOR);
+ if (parts.length != 2) {
+ throw new IllegalArgumentException("badly encoded module id: '" + encoded + "'");
+ }
+ return new ModuleId(parts[0], parts[1]);
+ }
+
+ /**
+ * Pattern to use to matched mid text representation.
+ *
+ * @see #parse(String)
+ */
+ public static final Pattern MID_PATTERN = Pattern.compile("("
+ + ModuleRevisionId.STRICT_CHARS_PATTERN + "*)" + "#("
+ + ModuleRevisionId.STRICT_CHARS_PATTERN + "+)");
+
+ /**
+ * Parses the module id text representation and returns it as a {@link ModuleId} instance.
+ *
+ * @param mid
+ * the module id text representation to parse
+ * @return the ModuleId instance corresponding to the representation
+ * @throws IllegalArgumentException
+ * if the given text representation cannot be parsed
+ */
+ public static ModuleId parse(String mid) {
+ Matcher m = MID_PATTERN.matcher(mid);
+ if (!m.matches()) {
+ throw new IllegalArgumentException(
+ "module text representation do not match expected pattern." + " given mid='"
+ + mid + "' expected form=" + MID_PATTERN.pattern());
+ }
+
+ // CheckStyle:MagicNumber| OFF
+ return newInstance(m.group(1), m.group(2));
+ // CheckStyle:MagicNumber| ON
+ }
+}
diff --git a/src/java/org/apache/ivy/core/module/id/ModuleRevisionId.java b/src/java/org/apache/ivy/core/module/id/ModuleRevisionId.java
new file mode 100644
index 0000000..6c36f5c
--- /dev/null
+++ b/src/java/org/apache/ivy/core/module/id/ModuleRevisionId.java
@@ -0,0 +1,343 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.module.id;
+
+import java.lang.ref.WeakReference;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.WeakHashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.ivy.Ivy;
+import org.apache.ivy.core.IvyContext;
+import org.apache.ivy.core.IvyPatternHelper;
+import org.apache.ivy.util.extendable.UnmodifiableExtendableItem;
+
+/**
+ * Identifies a module in a particular version
+ *
+ * @see <a href="package-summary.html">org.apache.ivy.core.module.id</a>
+ */
+public class ModuleRevisionId extends UnmodifiableExtendableItem {
+ private static final String ENCODE_SEPARATOR = ModuleId.ENCODE_SEPARATOR;
+
+ private static final String ENCODE_PREFIX = "+";
+
+ private static final String NULL_ENCODE = "@#:NULL:#@";
+
+ static final String STRICT_CHARS_PATTERN = "[a-zA-Z0-9\\-/\\._+=]";
+
+ private static final String REV_STRICT_CHARS_PATTERN = "[a-zA-Z0-9\\-/\\._+=,\\[\\]\\{\\}\\(\\):@]";
+
+ private static final Map/* <ModuleRevisionId, ModuleRevisionId> */CACHE = new WeakHashMap();
+
+ /**
+ * Pattern to use to matched mrid text representation.
+ *
+ * @see #parse(String)
+ */
+ public static final Pattern MRID_PATTERN = Pattern.compile("(" + STRICT_CHARS_PATTERN + "*)"
+ + "#(" + STRICT_CHARS_PATTERN + "+)" + "(?:#(" + STRICT_CHARS_PATTERN + "+))?" + ";("
+ + REV_STRICT_CHARS_PATTERN + "+)");
+
+ /**
+ * Same as MRID_PATTERN but using non capturing groups, useful to build larger regexp
+ */
+ public static final Pattern NON_CAPTURING_PATTERN = Pattern.compile("(?:"
+ + STRICT_CHARS_PATTERN + "*)" + "#(?:" + STRICT_CHARS_PATTERN + "+)" + "(?:#(?:"
+ + STRICT_CHARS_PATTERN + "+))?" + ";(?:" + REV_STRICT_CHARS_PATTERN + "+)");
+
+ /**
+ * Parses a module revision id text representation and returns a new {@link ModuleRevisionId}
+ * instance corresponding to the parsed String.
+ * <p>
+ * The result is unspecified if the module doesn't respect strict name conventions.
+ * </p>
+ *
+ * @param mrid
+ * the text representation of the module (as returned by {@link #toString()}). Must
+ * not be <code>null</code>.
+ * @return a {@link ModuleRevisionId} corresponding to the given text representation
+ * @throws IllegalArgumentException
+ * if the given text representation does not match the {@link ModuleRevisionId} text
+ * representation rules.
+ */
+ public static ModuleRevisionId parse(String mrid) {
+ Matcher m = MRID_PATTERN.matcher(mrid.trim());
+ if (!m.matches()) {
+ throw new IllegalArgumentException(
+ "module revision text representation do not match expected pattern."
+ + " given mrid='" + mrid + "' expected form=" + MRID_PATTERN.pattern());
+ }
+
+ // CheckStyle:MagicNumber| OFF
+ return newInstance(m.group(1), m.group(2), m.group(3), m.group(4));
+ // CheckStyle:MagicNumber| ON
+ }
+
+ public static ModuleRevisionId newInstance(String organisation, String name, String revision) {
+ return intern(new ModuleRevisionId(ModuleId.newInstance(organisation, name), revision));
+ }
+
+ public static ModuleRevisionId newInstance(String organisation, String name, String revision,
+ Map extraAttributes) {
+ return intern(new ModuleRevisionId(ModuleId.newInstance(organisation, name), revision,
+ extraAttributes));
+ }
+
+ public static ModuleRevisionId newInstance(String organisation, String name, String branch,
+ String revision) {
+ return intern(new ModuleRevisionId(ModuleId.newInstance(organisation, name), branch,
+ revision));
+ }
+
+ public static ModuleRevisionId newInstance(String organisation, String name, String branch,
+ String revision, Map extraAttributes) {
+ return intern(new ModuleRevisionId(ModuleId.newInstance(organisation, name), branch,
+ revision, extraAttributes));
+ }
+
+ public static ModuleRevisionId newInstance(String organisation, String name, String branch,
+ String revision, Map extraAttributes, boolean replaceNullBranchWithDefault) {
+ return intern(new ModuleRevisionId(ModuleId.newInstance(organisation, name), branch,
+ revision, extraAttributes, replaceNullBranchWithDefault));
+ }
+
+ public static ModuleRevisionId newInstance(ModuleRevisionId mrid, String rev) {
+ return intern(new ModuleRevisionId(mrid.getModuleId(), mrid.getBranch(), rev,
+ mrid.getQualifiedExtraAttributes()));
+ }
+
+ public static ModuleRevisionId newInstance(ModuleRevisionId mrid, String branch, String rev) {
+ return intern(new ModuleRevisionId(mrid.getModuleId(), branch, rev,
+ mrid.getQualifiedExtraAttributes()));
+ }
+
+ /**
+ * Returns an intern instance of the given ModuleRevisionId if any, or put the given
+ * ModuleRevisionId in a cache of intern instances and returns it.
+ * <p>
+ * This method should be called on ModuleRevisionId created with one of the constructor to
+ * decrease memory footprint.
+ * </p>
+ * <p>
+ * When using static newInstances methods, this method is already called.
+ * </p>
+ *
+ * @param moduleRevisionId
+ * the module revision id to intern
+ * @return an interned ModuleRevisionId
+ */
+ public static ModuleRevisionId intern(ModuleRevisionId moduleRevisionId) {
+ ModuleRevisionId r = null;
+
+ synchronized (CACHE) {
+ WeakReference ref = (WeakReference) CACHE.get(moduleRevisionId);
+ if (ref != null) {
+ r = (ModuleRevisionId) ref.get();
+ }
+ if (r == null) {
+ r = moduleRevisionId;
+ CACHE.put(r, new WeakReference(r));
+ }
+ }
+
+ return r;
+ }
+
+ private final ModuleId moduleId;
+
+ private final String branch;
+
+ private final String revision;
+
+ private int hash;
+
+ // TODO: make these constructors private and use only static factory methods
+
+ public ModuleRevisionId(ModuleId moduleId, String revision) {
+ this(moduleId, null, revision, null);
+ }
+
+ public ModuleRevisionId(ModuleId moduleId, String branch, String revision) {
+ this(moduleId, branch, revision, null);
+ }
+
+ private ModuleRevisionId(ModuleId moduleId, String revision, Map extraAttributes) {
+ this(moduleId, null, revision, extraAttributes);
+ }
+
+ private ModuleRevisionId(ModuleId moduleId, String branch, String revision, Map extraAttributes) {
+ this(moduleId, branch, revision, extraAttributes, true);
+ }
+
+ private ModuleRevisionId(ModuleId moduleId, String branch, String revision,
+ Map extraAttributes, boolean replaceNullBranchWithDefault) {
+ super(null, extraAttributes);
+ this.moduleId = moduleId;
+ IvyContext context = IvyContext.getContext();
+ this.branch = (replaceNullBranchWithDefault && branch == null)
+ // we test if there's already an Ivy instance loaded, to avoid loading a default one
+ // just to get the default branch
+ ? (context.peekIvy() == null ? null : context.getSettings().getDefaultBranch(moduleId))
+ : branch;
+ this.revision = revision == null ? Ivy.getWorkingRevision() : normalizeRevision(revision);
+ setStandardAttribute(IvyPatternHelper.ORGANISATION_KEY, this.moduleId.getOrganisation());
+ setStandardAttribute(IvyPatternHelper.MODULE_KEY, this.moduleId.getName());
+ setStandardAttribute(IvyPatternHelper.BRANCH_KEY, this.branch);
+ setStandardAttribute(IvyPatternHelper.REVISION_KEY, this.revision);
+ }
+
+ public ModuleId getModuleId() {
+ return moduleId;
+ }
+
+ public String getName() {
+ return getModuleId().getName();
+ }
+
+ public String getOrganisation() {
+ return getModuleId().getOrganisation();
+ }
+
+ public String getRevision() {
+ return revision;
+ }
+
+ public boolean equals(Object obj) {
+ if (!(obj instanceof ModuleRevisionId)) {
+ return false;
+ }
+ ModuleRevisionId other = (ModuleRevisionId) obj;
+
+ if (!other.getRevision().equals(getRevision())) {
+ return false;
+ } else if (other.getBranch() == null && getBranch() != null) {
+ return false;
+ } else if (other.getBranch() != null && !other.getBranch().equals(getBranch())) {
+ return false;
+ } else if (!other.getModuleId().equals(getModuleId())) {
+ return false;
+ } else {
+ return other.getQualifiedExtraAttributes().equals(getQualifiedExtraAttributes());
+ }
+ }
+
+ public int hashCode() {
+ if (hash == 0) {
+ // CheckStyle:MagicNumber| OFF
+ hash = 31;
+ hash = hash * 13 + (getBranch() == null ? 0 : getBranch().hashCode());
+ hash = hash * 13 + getRevision().hashCode();
+ hash = hash * 13 + getModuleId().hashCode();
+ hash = hash * 13 + getQualifiedExtraAttributes().hashCode();
+ // CheckStyle:MagicNumber| ON
+ }
+ return hash;
+ }
+
+ public String toString() {
+ return moduleId + (branch == null || branch.length() == 0 ? "" : "#" + branch) + ";"
+ + (revision == null ? "NONE" : revision);
+ }
+
+ public String encodeToString() {
+ StringBuffer buf = new StringBuffer();
+ Map attributes = new HashMap(getAttributes());
+ attributes.keySet().removeAll(getExtraAttributes().keySet());
+ attributes.putAll(getQualifiedExtraAttributes());
+
+ for (Iterator iter = attributes.keySet().iterator(); iter.hasNext();) {
+ String attName = (String) iter.next();
+ String value = (String) attributes.get(attName);
+ value = value == null ? NULL_ENCODE : value;
+ buf.append(ENCODE_PREFIX).append(attName).append(ENCODE_SEPARATOR)
+ .append(ENCODE_PREFIX).append(value).append(ENCODE_SEPARATOR);
+ }
+ return buf.toString();
+ }
+
+ public static ModuleRevisionId decode(String encoded) {
+ String[] parts = encoded.split(ENCODE_SEPARATOR);
+ if (parts.length % 2 != 0) {
+ throw new IllegalArgumentException("badly encoded module revision id: '" + encoded
+ + "'");
+ }
+ Map attributes = new HashMap();
+ for (int i = 0; i < parts.length; i += 2) {
+ String attName = parts[i];
+ if (!attName.startsWith(ENCODE_PREFIX)) {
+ throw new IllegalArgumentException("badly encoded module revision id: '" + encoded
+ + "': " + attName + " doesn't start with " + ENCODE_PREFIX);
+ } else {
+ attName = attName.substring(1);
+ }
+ String attValue = parts[i + 1];
+ if (!attValue.startsWith(ENCODE_PREFIX)) {
+ throw new IllegalArgumentException("badly encoded module revision id: '" + encoded
+ + "': " + attValue + " doesn't start with " + ENCODE_PREFIX);
+ } else {
+ attValue = attValue.substring(1);
+ }
+ if (NULL_ENCODE.equals(attValue)) {
+ attValue = null;
+ }
+ attributes.put(attName, attValue);
+ }
+ String org = (String) attributes.remove(IvyPatternHelper.ORGANISATION_KEY);
+ String mod = (String) attributes.remove(IvyPatternHelper.MODULE_KEY);
+ String rev = (String) attributes.remove(IvyPatternHelper.REVISION_KEY);
+ String branch = (String) attributes.remove(IvyPatternHelper.BRANCH_KEY);
+ if (org == null) {
+ throw new IllegalArgumentException("badly encoded module revision id: '" + encoded
+ + "': no organisation");
+ }
+ if (mod == null) {
+ throw new IllegalArgumentException("badly encoded module revision id: '" + encoded
+ + "': no module name");
+ }
+ if (rev == null) {
+ throw new IllegalArgumentException("badly encoded module revision id: '" + encoded
+ + "': no revision");
+ }
+ return newInstance(org, mod, branch, rev, attributes);
+ }
+
+ public String getBranch() {
+ return branch;
+ }
+
+ /**
+ * [revision] is a valid revision in maven. This method strips the '[' and ']' characters. Cfr.
+ * http://docs.codehaus.org/x/IGU
+ */
+ private static String normalizeRevision(String revision) {
+ if (revision.startsWith("[") && revision.endsWith("]") && revision.indexOf(',') == -1) {
+ if (IvyPatternHelper.getTokenString(IvyPatternHelper.REVISION_KEY).equals(revision)) {
+ // this is the case when listing dynamic revions
+ return revision;
+ }
+
+ return revision.substring(1, revision.length() - 1);
+ } else {
+ return revision;
+ }
+ }
+}
diff --git a/src/java/org/apache/ivy/core/module/id/ModuleRules.java b/src/java/org/apache/ivy/core/module/id/ModuleRules.java
new file mode 100644
index 0000000..f16981d
--- /dev/null
+++ b/src/java/org/apache/ivy/core/module/id/ModuleRules.java
@@ -0,0 +1,250 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.module.id;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.ivy.plugins.matcher.MapMatcher;
+import org.apache.ivy.util.Checks;
+import org.apache.ivy.util.Message;
+import org.apache.ivy.util.filter.Filter;
+import org.apache.ivy.util.filter.NoFilter;
+
+/**
+ * A list of module specific rules.
+ * <p>
+ * This class defines a list of module specific rules. For each module only one rule apply,
+ * sometimes none.
+ * </p>
+ * <p>
+ * To know which rule to apply, they are configured using matchers. So you can define a rule
+ * applying to all module from one particular organization, or to all modules with a revisions
+ * matching a pattern, and so on.
+ * </p>
+ * <p>
+ * Rules condition are evaluated in order, so the first matching rule is returned.
+ * </p>
+ * <p>
+ * Rules themselves can be represented by any object, depending on the purpose of the rule (define
+ * which resolver to use, which TTL in cache, ...)
+ * </p>
+ */
+public class ModuleRules {
+ private Map/* <MapMatcher,Object> */rules = new LinkedHashMap();
+
+ private MatcherLookup matcher_lookup = new MatcherLookup();
+
+ /**
+ * Constructs an empty ModuleRules.
+ */
+ public ModuleRules() {
+ }
+
+ private ModuleRules(Map/* <MapMatcher,Object> */rules) {
+ this.rules = new LinkedHashMap(rules);
+ for (Iterator iter = rules.keySet().iterator(); iter.hasNext();) {
+ matcher_lookup.add((MapMatcher) iter.next());
+ }
+ }
+
+ /**
+ * Defines a new rule for the given condition.
+ *
+ * @param condition
+ * the condition for which the rule should be applied. Must not be <code>null</code>.
+ * @param rule
+ * the rule to apply. Must not be <code>null</code>.
+ */
+ public void defineRule(MapMatcher condition, Object rule) {
+ Checks.checkNotNull(condition, "condition");
+ Checks.checkNotNull(rule, "rule");
+
+ rules.put(condition, rule);
+ matcher_lookup.add(condition);
+ }
+
+ /**
+ * Returns the rule object matching the given {@link ModuleId}, or <code>null</code> if no rule
+ * applies.
+ *
+ * @param mid
+ * the {@link ModuleId} to search the rule for. Must not be <code>null</code>.
+ * @return the rule object matching the given {@link ModuleId}, or <code>null</code> if no rule
+ * applies.
+ * @see #getRule(ModuleId, Filter)
+ */
+ public Object getRule(ModuleId mid) {
+ return getRule(mid, NoFilter.INSTANCE);
+ }
+
+ /**
+ * Returns the rules objects matching the given {@link ModuleId}, or an empty array if no rule
+ * applies.
+ *
+ * @param mid
+ * the {@link ModuleId} to search the rule for. Must not be <code>null</code>.
+ * @return an array of rule objects matching the given {@link ModuleId}.
+ */
+ public Object[] getRules(ModuleId mid) {
+ return getRules(mid.getAttributes(), NoFilter.INSTANCE);
+ }
+
+ /**
+ * Returns the rule object matching the given {@link ModuleRevisionId}, or <code>null</code> if
+ * no rule applies.
+ *
+ * @param mrid
+ * the {@link ModuleRevisionId} to search the rule for. Must not be <code>null</code>
+ * .
+ * @return the rule object matching the given {@link ModuleRevisionId}, or <code>null</code> if
+ * no rule applies.
+ * @see #getRule(ModuleRevisionId, Filter)
+ */
+ public Object getRule(ModuleRevisionId mrid) {
+ return getRule(mrid, NoFilter.INSTANCE);
+ }
+
+ /**
+ * Returns the rule object matching the given {@link ModuleId} and accepted by the given
+ * {@link Filter}, or <code>null</code> if no rule applies.
+ *
+ * @param mid
+ * the {@link ModuleRevisionId} to search the rule for. Must not be <code>null</code>
+ * .
+ * @param filter
+ * the filter to use to filter the rule to return. The {@link Filter#accept(Object)}
+ * method will be called only with rule objects matching the given {@link ModuleId},
+ * and the first rule object accepted by the filter will be returned. Must not be
+ * <code>null</code>.
+ * @return the rule object matching the given {@link ModuleId}, or <code>null</code> if no rule
+ * applies.
+ * @see #getRule(ModuleRevisionId, Filter)
+ */
+ public Object getRule(ModuleId mid, Filter filter) {
+ Checks.checkNotNull(mid, "mid");
+ return getRule(mid.getAttributes(), filter);
+ }
+
+ /**
+ * Returns the rule object matching the given {@link ModuleRevisionId} and accepted by the given
+ * {@link Filter}, or <code>null</code> if no rule applies.
+ *
+ * @param mrid
+ * the {@link ModuleRevisionId} to search the rule for. Must not be <code>null</code>
+ * .
+ * @param filter
+ * the filter to use to filter the rule to return. The {@link Filter#accept(Object)}
+ * method will be called only with rule objects matching the given
+ * {@link ModuleRevisionId}, and the first rule object accepted by the filter will be
+ * returned. Must not be <code>null</code>.
+ * @return the rule object matching the given {@link ModuleRevisionId}, or <code>null</code> if
+ * no rule applies.
+ * @see #getRule(ModuleRevisionId)
+ */
+ public Object getRule(ModuleRevisionId mrid, Filter filter) {
+ Checks.checkNotNull(mrid, "mrid");
+ Checks.checkNotNull(filter, "filter");
+ Map moduleAttributes = mrid.getAttributes();
+ return getRule(moduleAttributes, filter);
+ }
+
+ private Object getRule(Map moduleAttributes, Filter filter) {
+ List matchers = matcher_lookup.get(moduleAttributes);
+ for (Iterator iter = matchers.iterator(); iter.hasNext();) {
+ MapMatcher midm = (MapMatcher) iter.next();
+ Object rule = rules.get(midm);
+ if (filter.accept(rule)) {
+ return rule;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the rules object matching the given {@link ModuleRevisionId} and accepted by the
+ * given {@link Filter}, or an empty array if no rule applies.
+ *
+ * @param mrid
+ * the {@link ModuleRevisionId} to search the rule for. Must not be <code>null</code>
+ * .
+ * @param filter
+ * the filter to use to filter the rule to return. The {@link Filter#accept(Object)}
+ * method will be called only with rule objects matching the given
+ * {@link ModuleRevisionId}. Must not be <code>null</code>.
+ * @return an array of rule objects matching the given {@link ModuleRevisionId}.
+ */
+ public Object[] getRules(ModuleRevisionId mrid, Filter filter) {
+ Checks.checkNotNull(mrid, "mrid");
+ Checks.checkNotNull(filter, "filter");
+ Map moduleAttributes = mrid.getAttributes();
+ return getRules(moduleAttributes, filter);
+ }
+
+ private Object[] getRules(Map moduleAttributes, Filter filter) {
+ List matchers = matcher_lookup.get(moduleAttributes);
+ List matchingRules = new ArrayList();
+ for (Iterator iter = matchers.iterator(); iter.hasNext();) {
+ MapMatcher midm = (MapMatcher) iter.next();
+ Object rule = rules.get(midm);
+ if (filter.accept(rule)) {
+ matchingRules.add(rule);
+ }
+ }
+ return matchingRules.toArray();
+ }
+
+ /**
+ * Dump the list of rules to {@link Message#debug(String)}
+ *
+ * @param prefix
+ * the prefix to use for each line dumped
+ */
+ public void dump(String prefix) {
+ if (rules.isEmpty()) {
+ Message.debug(prefix + "NONE");
+ } else {
+ for (Iterator iter = rules.keySet().iterator(); iter.hasNext();) {
+ MapMatcher midm = (MapMatcher) iter.next();
+ Object rule = rules.get(midm);
+ Message.debug(prefix + midm + " -> " + rule);
+ }
+ }
+ }
+
+ /**
+ * Returns an unmodifiable view of all the rules defined on this ModuleRules.
+ * <p>
+ * The rules are returned in a Map where they keys are the MapMatchers matching the rules
+ * object, and the values are the rules object themselves.
+ * </p>
+ *
+ * @return an unmodifiable view of all the rules defined on this ModuleRules.
+ */
+ public Map/* <MapMatcher,Object> */getAllRules() {
+ return Collections.unmodifiableMap(rules);
+ }
+
+ public Object clone() {
+ return new ModuleRules(rules);
+ }
+}
diff --git a/src/java/org/apache/ivy/core/module/id/package.html b/src/java/org/apache/ivy/core/module/id/package.html
new file mode 100644
index 0000000..9e57242
--- /dev/null
+++ b/src/java/org/apache/ivy/core/module/id/package.html
@@ -0,0 +1,61 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<html>
+<head>
+</head>
+<body>
+<p>
+This packages gathers classes used to represent identifiers of objects involved in dependency management.
+</p>
+<p>
+As identifiers, they do not store any information on the module content and detailed metadata (like dependencies).
+</p>
+<h2>Classes</h2>
+<p>
+<ul>
+<li>ModuleId</li> is used as the identifier of module, without considering its version. It's basically an organization and module name couple.
+<li>ModuleRevisionId</li> is used as the identifier of a module in a particular version or version constraint. It is composed of a ModuleId, a revision, and optionally a branch and extra attributes.
+<li>ArtifactId</li> identifies an artifact without considering its version. It's basically a ModuleId and an artifact name, type and extension, plus optional extra attributes.
+<li>ArtifactRevisionId</li> identifies an artifact of a module in a particular version. It is composed of a ModuleRevisionId along with artifact identifier like the name type and extension.
+</ul>
+</p>
+<h2>Text Representation</h2>
+<p>
+These classes share an homogeneous text representation, which can be easily obtained through the toString() method.
+</p>
+<p>
+The general pattern is: <code>[organisation]#[module]#[branch];[revision]![artifact].[ext]([type])</code>
+The # before the branch is present only if the branch is not empty.<br/>
+The type and surrounding parenthesis are present only if different from the extension.
+</p>
+<p>
+A textual representation can be parsed into an object (supported for ModuleRevisionId only yet), as long as a strict set of characters is used for each field (which is recommended).<br/>
+Allowed characters are:
+<ul>
+<li>organisation</li> a-z A-Z 0-9 - / . _ + =
+<li>module</li> a-z A-Z 0-9 - / . _ + =
+<li>branch</li> a-z A-Z 0-9 - / . _ + =
+<li>revision</li> a-z A-Z 0-9 - / . _ + = , [ ] { } ( ) : @
+<li>artifact</li> a-z A-Z 0-9 - / . _ + =
+<li>extension</li> a-z A-Z 0-9 - / . _ + =
+<li>type</li> a-z A-Z 0-9 - / . _ + =
+</ul>
+
+</body>
\ No newline at end of file
diff --git a/src/java/org/apache/ivy/core/module/status/Status.java b/src/java/org/apache/ivy/core/module/status/Status.java
new file mode 100644
index 0000000..d94d94a
--- /dev/null
+++ b/src/java/org/apache/ivy/core/module/status/Status.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.module.status;
+
+public class Status {
+ private String name;
+
+ private boolean integration;
+
+ public Status() {
+ }
+
+ public Status(String name, boolean integration) {
+ this.name = name;
+ this.integration = integration;
+ }
+
+ public boolean isIntegration() {
+ return integration;
+ }
+
+ public void setIntegration(boolean integration) {
+ this.integration = integration;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
diff --git a/src/java/org/apache/ivy/core/module/status/StatusManager.java b/src/java/org/apache/ivy/core/module/status/StatusManager.java
new file mode 100644
index 0000000..cb45b7f
--- /dev/null
+++ b/src/java/org/apache/ivy/core/module/status/StatusManager.java
@@ -0,0 +1,151 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.module.status;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+
+import org.apache.ivy.core.IvyContext;
+import org.apache.ivy.util.Message;
+
+/**
+ * Note: update methods (such as addStatus) should only be called BEFORE any call to accessor
+ * methods
+ */
+public class StatusManager {
+ public static StatusManager newDefaultInstance() {
+ return new StatusManager(new Status[] {new Status("release", false),
+ new Status("milestone", false), new Status("integration", true)}, "integration");
+ }
+
+ public static StatusManager getCurrent() {
+ return IvyContext.getContext().getSettings().getStatusManager();
+ }
+
+ private List status = new ArrayList();
+
+ private String defaultStatus;
+
+ // for easier querying only
+ private Map statusPriorityMap;
+
+ private Map statusIntegrationMap;
+
+ private String deliveryStatusListString;
+
+ public StatusManager(Status[] status, String defaultStatus) {
+ this.status.addAll(Arrays.asList(status));
+ this.defaultStatus = defaultStatus;
+
+ computeMaps();
+ }
+
+ public StatusManager() {
+ }
+
+ public void addStatus(Status status) {
+ this.status.add(status);
+ }
+
+ public void setDefaultStatus(String defaultStatus) {
+ this.defaultStatus = defaultStatus;
+ }
+
+ public List getStatuses() {
+ return status;
+ }
+
+ private void computeMaps() {
+ if (status.isEmpty()) {
+ throw new IllegalStateException("badly configured statuses: no status found");
+ }
+ statusPriorityMap = new HashMap();
+ for (ListIterator iter = status.listIterator(); iter.hasNext();) {
+ Status status = (Status) iter.next();
+ statusPriorityMap.put(status.getName(), new Integer(iter.previousIndex()));
+ }
+ statusIntegrationMap = new HashMap();
+ for (Iterator iter = status.iterator(); iter.hasNext();) {
+ Status status = (Status) iter.next();
+ statusIntegrationMap.put(status.getName(), Boolean.valueOf(status.isIntegration()));
+ }
+ }
+
+ public boolean isStatus(String status) {
+ if (statusPriorityMap == null) {
+ computeMaps();
+ }
+ return statusPriorityMap.containsKey(status);
+ }
+
+ public int getPriority(String status) {
+ if (statusPriorityMap == null) {
+ computeMaps();
+ }
+ Integer priority = (Integer) statusPriorityMap.get(status);
+ if (priority == null) {
+ Message.debug("unknown status " + status + ": assuming lowest priority");
+ return Integer.MAX_VALUE;
+ }
+ return priority.intValue();
+ }
+
+ public boolean isIntegration(String status) {
+ if (statusIntegrationMap == null) {
+ computeMaps();
+ }
+ Boolean isIntegration = (Boolean) statusIntegrationMap.get(status);
+ if (isIntegration == null) {
+ Message.debug("unknown status " + status + ": assuming integration");
+ return true;
+ }
+ return isIntegration.booleanValue();
+ }
+
+ public String getDeliveryStatusListString() {
+ if (deliveryStatusListString == null) {
+ StringBuffer ret = new StringBuffer();
+ for (Iterator iter = status.iterator(); iter.hasNext();) {
+ Status status = (Status) iter.next();
+ if (!status.isIntegration()) {
+ ret.append(status.getName()).append(",");
+ }
+ }
+ if (ret.length() > 0) {
+ ret.deleteCharAt(ret.length() - 1);
+ }
+ deliveryStatusListString = ret.toString();
+ }
+ return deliveryStatusListString;
+ }
+
+ public String getDefaultStatus() {
+ if (defaultStatus == null) {
+ if (status.isEmpty()) {
+ throw new IllegalStateException("badly configured statuses: no status found");
+ }
+ defaultStatus = ((Status) status.get(status.size() - 1)).getName();
+ }
+ return defaultStatus;
+ }
+}
diff --git a/src/java/org/apache/ivy/core/pack/ArchivePacking.java b/src/java/org/apache/ivy/core/pack/ArchivePacking.java
new file mode 100644
index 0000000..2802ca5
--- /dev/null
+++ b/src/java/org/apache/ivy/core/pack/ArchivePacking.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.pack;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+
+public abstract class ArchivePacking {
+
+ public abstract String[] getNames();
+
+ public abstract void unpack(InputStream packed, File dest) throws IOException;
+
+ public abstract String getUnpackedExtension(String ext);
+
+}
diff --git a/src/java/org/apache/ivy/core/pack/OsgiBundlePacking.java b/src/java/org/apache/ivy/core/pack/OsgiBundlePacking.java
new file mode 100644
index 0000000..a9f9344
--- /dev/null
+++ b/src/java/org/apache/ivy/core/pack/OsgiBundlePacking.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.pack;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.ivy.util.FileUtil;
+
+/**
+ * Packaging which handle OSGi bundles with inner packed jar
+ */
+public class OsgiBundlePacking extends ZipPacking {
+
+ private static final String[] NAMES = {"bundle"};
+
+ @Override
+ public String[] getNames() {
+ return NAMES;
+ }
+
+ @Override
+ protected void writeFile(InputStream zip, File f) throws FileNotFoundException, IOException {
+ // XXX maybe we should only unpack file listed by the 'Bundle-ClassPath' MANIFEST header ?
+ if (f.getName().endsWith(".jar.pack.gz")) {
+ zip = FileUtil.unwrapPack200(zip);
+ f = new File(f.getParentFile(), f.getName().substring(0, f.getName().length() - 8));
+ }
+ super.writeFile(zip, f);
+ }
+}
diff --git a/src/java/org/apache/ivy/core/pack/Pack200Packing.java b/src/java/org/apache/ivy/core/pack/Pack200Packing.java
new file mode 100644
index 0000000..f2039d1
--- /dev/null
+++ b/src/java/org/apache/ivy/core/pack/Pack200Packing.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.pack;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.ivy.util.FileUtil;
+
+public class Pack200Packing extends StreamPacking {
+
+ private static final String[] NAMES = {"pack200"};
+
+ @Override
+ public String[] getNames() {
+ return NAMES;
+ }
+
+ @Override
+ public String getUnpackedExtension(String ext) {
+ if (ext.endsWith("pack.gz")) {
+ ext = ext.substring(0, ext.length() - 7);
+ if (ext.endsWith(".")) {
+ ext = ext.substring(0, ext.length() - 1);
+ }
+ } else if (ext.endsWith("pack")) {
+ ext = ext.substring(0, ext.length() - 4);
+ if (ext.endsWith(".")) {
+ ext = ext.substring(0, ext.length() - 1);
+ }
+ }
+ return ext;
+ }
+
+ @Override
+ public InputStream unpack(InputStream packed) throws IOException {
+ return FileUtil.unwrapPack200(packed);
+ }
+
+}
diff --git a/src/java/org/apache/ivy/core/pack/PackagingManager.java b/src/java/org/apache/ivy/core/pack/PackagingManager.java
new file mode 100644
index 0000000..6b3caf0
--- /dev/null
+++ b/src/java/org/apache/ivy/core/pack/PackagingManager.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.pack;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.DefaultArtifact;
+import org.apache.ivy.core.settings.IvySettings;
+import org.apache.ivy.plugins.IvySettingsAware;
+
+public class PackagingManager implements IvySettingsAware {
+
+ private IvySettings settings;
+
+ public void setSettings(IvySettings settings) {
+ this.settings = settings;
+ }
+
+ public Artifact getUnpackedArtifact(Artifact artifact) {
+ String packaging = artifact.getExtraAttribute("packaging");
+ if (packaging == null) {
+ // not declared as packed, nothing to do
+ return null;
+ }
+
+ String ext = artifact.getExt();
+
+ String[] packings = packaging.split(",");
+ for (int i = packings.length - 1; i >= 1; i--) {
+ ArchivePacking packing = settings.getPackingRegistry().get(packings[i]);
+ if (packing == null) {
+ throw new IllegalStateException("Unknown packing type '" + packings[i]
+ + "' in the packing chain: " + packaging);
+ }
+ if (!(packing instanceof StreamPacking)) {
+ throw new IllegalStateException("Unsupported archive only packing type '"
+ + packings[i] + "' in the streamed chain: " + packaging);
+ }
+ ext = ((StreamPacking) packing).getUnpackedExtension(ext);
+ }
+ ArchivePacking packing = settings.getPackingRegistry().get(packings[0]);
+ if (packing == null) {
+ throw new IllegalStateException("Unknown packing type '" + packings[0]
+ + "' in the packing chain: " + packaging);
+ }
+ ext = packing.getUnpackedExtension(ext);
+
+ DefaultArtifact unpacked = new DefaultArtifact(artifact.getModuleRevisionId(),
+ artifact.getPublicationDate(), artifact.getName(),
+ artifact.getType() + "_unpacked", ext);
+
+ return unpacked;
+ }
+
+ public void unpackArtifact(Artifact artifact, File localFile, File archiveFile)
+ throws IOException {
+ String packaging = artifact.getExtraAttribute("packaging");
+ if (packaging == null) {
+ // not declared as packed, nothing to do
+ return;
+ }
+
+ String[] packings = packaging.split(",");
+ InputStream in = null;
+ try {
+ in = new FileInputStream(localFile);
+ for (int i = packings.length - 1; i >= 1; i--) {
+ ArchivePacking packing = settings.getPackingRegistry().get(packings[i]);
+ if (packing == null) {
+ throw new IllegalStateException("Unknown packing type '" + packings[i]
+ + "' in the packing chain: " + packaging);
+ }
+ if (!(packing instanceof StreamPacking)) {
+ throw new IllegalStateException("Unsupported archive only packing type '"
+ + packings[i] + "' in the streamed chain: " + packaging);
+ }
+ in = ((StreamPacking) packing).unpack(in);
+ }
+ ArchivePacking packing = settings.getPackingRegistry().get(packings[0]);
+ if (packing == null) {
+ throw new IllegalStateException("Unknown packing type '" + packings[0]
+ + "' in the packing chain: " + packaging);
+ }
+ packing.unpack(in, archiveFile);
+ } finally {
+ if (in != null) {
+ try {
+ in.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ }
+ }
+
+}
diff --git a/src/java/org/apache/ivy/core/pack/PackingRegistry.java b/src/java/org/apache/ivy/core/pack/PackingRegistry.java
new file mode 100644
index 0000000..6688677
--- /dev/null
+++ b/src/java/org/apache/ivy/core/pack/PackingRegistry.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.pack;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class PackingRegistry {
+
+ private Map<String, ArchivePacking> packings = new HashMap<String, ArchivePacking>();
+
+ public PackingRegistry() {
+ // register defaults
+ register(new ZipPacking());
+ register(new Pack200Packing());
+ register(new OsgiBundlePacking());
+ }
+
+ public void register(ArchivePacking packing) {
+ for (String name : packing.getNames()) {
+ packings.put(name, packing);
+ }
+ }
+
+ public ArchivePacking get(String type) {
+ return packings.get(type);
+ }
+}
diff --git a/src/java/org/apache/ivy/core/pack/StreamPacking.java b/src/java/org/apache/ivy/core/pack/StreamPacking.java
new file mode 100644
index 0000000..b4b98d0
--- /dev/null
+++ b/src/java/org/apache/ivy/core/pack/StreamPacking.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.pack;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.ivy.util.FileUtil;
+
+public abstract class StreamPacking extends ArchivePacking {
+
+ public abstract InputStream unpack(InputStream packed) throws IOException;
+
+ @Override
+ public void unpack(InputStream packed, File dest) throws IOException {
+ FileUtil.copy(unpack(packed), dest, null);
+ }
+
+}
diff --git a/src/java/org/apache/ivy/core/pack/ZipPacking.java b/src/java/org/apache/ivy/core/pack/ZipPacking.java
new file mode 100644
index 0000000..87cfbdf
--- /dev/null
+++ b/src/java/org/apache/ivy/core/pack/ZipPacking.java
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.pack;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+import org.apache.ivy.util.FileUtil;
+import org.apache.ivy.util.Message;
+
+public class ZipPacking extends ArchivePacking {
+
+ private static final String[] NAMES = {"zip", "jar", "war"};
+
+ @Override
+ public String[] getNames() {
+ return NAMES;
+ }
+
+ @Override
+ public String getUnpackedExtension(String ext) {
+ if (ext.endsWith("zip") || ext.endsWith("jar") || ext.endsWith("war")) {
+ ext = ext.substring(0, ext.length() - 3);
+ if (ext.endsWith(".")) {
+ ext = ext.substring(0, ext.length() - 1);
+ }
+ }
+ return ext;
+ }
+
+ @Override
+ public void unpack(InputStream packed, File dest) throws IOException {
+ ZipInputStream zip = null;
+ try {
+ zip = new ZipInputStream(packed);
+ ZipEntry entry = null;
+ while (((entry = zip.getNextEntry()) != null)) {
+ File f = new File(dest, entry.getName());
+ Message.verbose("\t\texpanding " + entry.getName() + " to " + f);
+
+ // create intermediary directories - sometimes zip don't add them
+ File dirF = f.getParentFile();
+ if (dirF != null) {
+ dirF.mkdirs();
+ }
+
+ if (entry.isDirectory()) {
+ f.mkdirs();
+ } else {
+ writeFile(zip, f);
+ }
+
+ f.setLastModified(entry.getTime());
+ }
+ } finally {
+ if (zip != null) {
+ try {
+ zip.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ }
+ }
+
+ protected void writeFile(InputStream zip, File f) throws FileNotFoundException, IOException {
+ FileOutputStream out = new FileOutputStream(f);
+ try {
+ FileUtil.copy(zip, out, null, false);
+ } finally {
+ try {
+ out.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ }
+
+}
diff --git a/src/java/org/apache/ivy/core/publish/PublishEngine.java b/src/java/org/apache/ivy/core/publish/PublishEngine.java
new file mode 100644
index 0000000..4fa43a2
--- /dev/null
+++ b/src/java/org/apache/ivy/core/publish/PublishEngine.java
@@ -0,0 +1,290 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.publish;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.apache.ivy.core.IvyContext;
+import org.apache.ivy.core.IvyPatternHelper;
+import org.apache.ivy.core.cache.ResolutionCacheManager;
+import org.apache.ivy.core.event.EventManager;
+import org.apache.ivy.core.event.publish.EndArtifactPublishEvent;
+import org.apache.ivy.core.event.publish.StartArtifactPublishEvent;
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.DefaultArtifact;
+import org.apache.ivy.core.module.descriptor.MDArtifact;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.plugins.parser.xml.UpdateOptions;
+import org.apache.ivy.plugins.parser.xml.XmlModuleDescriptorParser;
+import org.apache.ivy.plugins.parser.xml.XmlModuleDescriptorUpdater;
+import org.apache.ivy.plugins.resolver.DependencyResolver;
+import org.apache.ivy.util.ConfigurationUtils;
+import org.apache.ivy.util.Message;
+import org.xml.sax.SAXException;
+
+public class PublishEngine {
+ private PublishEngineSettings settings;
+
+ private EventManager eventManager;
+
+ public PublishEngine(PublishEngineSettings settings, EventManager eventManager) {
+ this.settings = settings;
+ this.eventManager = eventManager;
+ }
+
+ /**
+ * Publishes a module to the repository. The publish can update the ivy file to publish if
+ * update is set to true. In this case it will use the given pubrevision, pubdate and status. If
+ * pubdate is null it will default to the current date. If status is null it will default to the
+ * current ivy file status (which itself defaults to integration if none is found). If update is
+ * false, then if the revision is not the same in the ivy file than the one expected (given as
+ * parameter), this method will fail with an IllegalArgumentException. pubdate and status are
+ * not used if update is false. extra artifacts can be used to publish more artifacts than
+ * actually declared in the ivy file. This can be useful to publish additional metadata or
+ * reports. The extra artifacts array can be null (= no extra artifacts), and if non null only
+ * the name, type, ext url and extra attributes of the artifacts are really used. Other methods
+ * can return null safely.
+ */
+ public Collection publish(ModuleRevisionId mrid, Collection srcArtifactPattern,
+ String resolverName, PublishOptions options) throws IOException {
+ Message.info(":: publishing :: " + mrid.getModuleId());
+ Message.verbose("\tvalidate = " + options.isValidate());
+ long start = System.currentTimeMillis();
+
+ options.setSrcIvyPattern(settings.substitute(options.getSrcIvyPattern()));
+ if (options.getPubBranch() == null) {
+ options.setPubbranch(mrid.getBranch());
+ }
+ if (options.getPubrevision() == null) {
+ options.setPubrevision(mrid.getRevision());
+ }
+ ModuleRevisionId pubmrid = ModuleRevisionId.newInstance(mrid, options.getPubBranch(),
+ options.getPubrevision());
+
+ // let's find the resolved module descriptor
+ ModuleDescriptor md = null;
+ if (options.getSrcIvyPattern() != null) {
+ File ivyFile = settings.resolveFile(IvyPatternHelper.substitute(
+ options.getSrcIvyPattern(), DefaultArtifact.newIvyArtifact(pubmrid, new Date())));
+ if (!ivyFile.exists()) {
+ throw new IllegalArgumentException("ivy file to publish not found for " + mrid
+ + ": call deliver before (" + ivyFile + ")");
+ }
+
+ URL ivyFileURL = ivyFile.toURI().toURL();
+ try {
+ md = XmlModuleDescriptorParser.getInstance().parseDescriptor(settings, ivyFileURL,
+ false);
+
+ if (options.isUpdate()) {
+ File tmp = File.createTempFile("ivy", ".xml");
+ tmp.deleteOnExit();
+
+ String[] confs = ConfigurationUtils.replaceWildcards(options.getConfs(), md);
+ Set confsToRemove = new HashSet(Arrays.asList(md.getConfigurationsNames()));
+ confsToRemove.removeAll(Arrays.asList(confs));
+
+ try {
+ XmlModuleDescriptorUpdater.update(
+ ivyFileURL,
+ tmp,
+ new UpdateOptions()
+ .setSettings(settings)
+ .setStatus(
+ options.getStatus() == null ? md.getStatus() : options
+ .getStatus())
+ .setRevision(options.getPubrevision())
+ .setBranch(options.getPubBranch())
+ .setPubdate(
+ options.getPubdate() == null ? new Date() : options
+ .getPubdate())
+ .setMerge(options.isMerge())
+ .setMergedDescriptor(md)
+ .setConfsToExclude(
+ (String[]) confsToRemove.toArray(new String[confsToRemove
+ .size()])));
+ ivyFile = tmp;
+ // we parse the new file to get updated module descriptor
+ md = XmlModuleDescriptorParser.getInstance().parseDescriptor(settings,
+ ivyFile.toURI().toURL(), false);
+ options.setSrcIvyPattern(ivyFile.getAbsolutePath());
+ } catch (SAXException e) {
+ throw new IllegalStateException("bad ivy file for " + mrid + ": " + ivyFile
+ + ": " + e);
+ }
+ } else if (!options.getPubrevision().equals(md.getModuleRevisionId().getRevision())) {
+ throw new IllegalArgumentException("cannot publish " + ivyFile + " as "
+ + options.getPubrevision()
+ + ": bad revision found in ivy file (Revision: "
+ + md.getModuleRevisionId().getRevision()
+ + "). Use forcedeliver or update.");
+ }
+ } catch (ParseException e) {
+ throw new IllegalStateException("bad ivy file for " + mrid + ": " + ivyFile + ": "
+ + e);
+ }
+ } else {
+ ResolutionCacheManager cacheManager = settings.getResolutionCacheManager();
+ try {
+ md = cacheManager.getResolvedModuleDescriptor(mrid);
+ } catch (ParseException e) {
+ throw new IllegalStateException("bad ivy file in cache for " + mrid + ": " + e);
+ }
+ md.setResolvedModuleRevisionId(pubmrid);
+ }
+
+ DependencyResolver resolver = settings.getResolver(resolverName);
+ if (resolver == null) {
+ throw new IllegalArgumentException("unknown resolver " + resolverName);
+ }
+
+ // collect all declared artifacts of this module
+ Collection missing = publish(md, srcArtifactPattern, resolver, options);
+ Message.verbose("\tpublish done (" + (System.currentTimeMillis() - start) + "ms)");
+ return missing;
+ }
+
+ public Collection publish(ModuleDescriptor md, Collection srcArtifactPattern,
+ DependencyResolver resolver, PublishOptions options) throws IOException {
+ Collection missing = new ArrayList();
+ Set artifactsSet = new LinkedHashSet();
+ String[] confs = ConfigurationUtils.replaceWildcards(options.getConfs(), md);
+
+ for (int i = 0; i < confs.length; i++) {
+ Artifact[] artifacts = md.getArtifacts(confs[i]);
+ for (int j = 0; j < artifacts.length; j++) {
+ artifactsSet.add(artifacts[j]);
+ }
+ }
+ Artifact[] extraArtifacts = options.getExtraArtifacts();
+ if (extraArtifacts != null) {
+ for (int i = 0; i < extraArtifacts.length; i++) {
+ artifactsSet.add(new MDArtifact(md, extraArtifacts[i].getName(), extraArtifacts[i]
+ .getType(), extraArtifacts[i].getExt(), extraArtifacts[i].getUrl(),
+ extraArtifacts[i].getQualifiedExtraAttributes()));
+ }
+ }
+ // now collects artifacts files
+ Map/* <Artifact,File> */artifactsFiles = new LinkedHashMap();
+ for (Iterator iter = artifactsSet.iterator(); iter.hasNext();) {
+ Artifact artifact = (Artifact) iter.next();
+ for (Iterator iterator = srcArtifactPattern.iterator(); iterator.hasNext();) {
+ String pattern = (String) iterator.next();
+ File artifactFile = settings.resolveFile(IvyPatternHelper.substitute(
+ settings.substitute(pattern), artifact));
+ if (artifactFile.exists()) {
+ artifactsFiles.put(artifact, artifactFile);
+ break;
+ }
+ }
+ if (!artifactsFiles.containsKey(artifact)) {
+ StringBuffer sb = new StringBuffer();
+ sb.append("missing artifact " + artifact + ":\n");
+ for (Iterator iterator = srcArtifactPattern.iterator(); iterator.hasNext();) {
+ String pattern = (String) iterator.next();
+ sb.append("\t"
+ + settings.resolveFile(IvyPatternHelper.substitute(pattern, artifact))
+ + " file does not exist\n");
+ }
+ if (options.isWarnOnMissing() || options.isHaltOnMissing()) {
+ Message.warn(sb.toString());
+ } else {
+ Message.verbose(sb.toString());
+ }
+ if (options.isHaltOnMissing()) {
+ throw new IOException("missing artifact " + artifact);
+ }
+ missing.add(artifact);
+ }
+ }
+ if (options.getSrcIvyPattern() != null) {
+ Artifact artifact = MDArtifact.newIvyArtifact(md);
+ File artifactFile = settings.resolveFile(IvyPatternHelper.substitute(
+ options.getSrcIvyPattern(), artifact));
+ if (!artifactFile.exists()) {
+ String msg = "missing ivy file for " + md.getModuleRevisionId() + ": \n"
+ + artifactFile + " file does not exist";
+ if (options.isWarnOnMissing() || options.isHaltOnMissing()) {
+ Message.warn(msg);
+ } else {
+ Message.verbose(msg);
+ }
+ if (options.isHaltOnMissing()) {
+ throw new IOException("missing ivy artifact " + artifact);
+ }
+ missing.add(artifact);
+ } else {
+ artifactsFiles.put(artifact, artifactFile);
+ }
+ }
+
+ // and now do actual publishing
+ boolean successfullyPublished = false;
+ try {
+ resolver.beginPublishTransaction(md.getModuleRevisionId(), options.isOverwrite());
+ // for each declared published artifact in this descriptor, do:
+ for (Iterator iter = artifactsFiles.entrySet().iterator(); iter.hasNext();) {
+ Map.Entry entry = (Entry) iter.next();
+ Artifact artifact = (Artifact) entry.getKey();
+ File artifactFile = (File) entry.getValue();
+ publish(artifact, artifactFile, resolver, options.isOverwrite());
+ }
+ resolver.commitPublishTransaction();
+ successfullyPublished = true;
+ } finally {
+ if (!successfullyPublished) {
+ resolver.abortPublishTransaction();
+ }
+ }
+ return missing;
+ }
+
+ private void publish(Artifact artifact, File src, DependencyResolver resolver, boolean overwrite)
+ throws IOException {
+ IvyContext.getContext().checkInterrupted();
+ // notify triggers that an artifact is about to be published
+ eventManager
+ .fireIvyEvent(new StartArtifactPublishEvent(resolver, artifact, src, overwrite));
+ boolean successful = false; // set to true once the publish succeeds
+ try {
+ if (src.exists()) {
+ resolver.publish(artifact, src, overwrite);
+ successful = true;
+ }
+ } finally {
+ // notify triggers that the publish is finished, successfully or not.
+ eventManager.fireIvyEvent(new EndArtifactPublishEvent(resolver, artifact, src,
+ overwrite, successful));
+ }
+ }
+}
diff --git a/src/java/org/apache/ivy/core/publish/PublishEngineSettings.java b/src/java/org/apache/ivy/core/publish/PublishEngineSettings.java
new file mode 100644
index 0000000..8105b2f
--- /dev/null
+++ b/src/java/org/apache/ivy/core/publish/PublishEngineSettings.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.publish;
+
+import org.apache.ivy.plugins.parser.ParserSettings;
+import org.apache.ivy.plugins.resolver.DependencyResolver;
+
+/**
+ * Settings specific to the publish engine
+ */
+public interface PublishEngineSettings extends ParserSettings {
+
+ String substitute(String srcIvyPattern);
+
+ DependencyResolver getResolver(String resolverName);
+
+}
diff --git a/src/java/org/apache/ivy/core/publish/PublishOptions.java b/src/java/org/apache/ivy/core/publish/PublishOptions.java
new file mode 100644
index 0000000..3c604cd
--- /dev/null
+++ b/src/java/org/apache/ivy/core/publish/PublishOptions.java
@@ -0,0 +1,183 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.publish;
+
+import java.util.Date;
+
+import org.apache.ivy.core.module.descriptor.Artifact;
+
+/**
+ * A set of options used during publish related tasks The publish can update the ivy file to publish
+ * if update is set to true. In this case it will use the given pubrevision, pubdate and status. If
+ * pudate is null it will default to the current date. If status is null it will default to the
+ * current ivy file status (which itself defaults to integration if none is found). If update is
+ * false, then if the revision is not the same in the ivy file than the one expected (given as
+ * parameter), this method will fail with an IllegalArgumentException. pubdate and status are not
+ * used if update is false. extra artifacts can be used to publish more artifacts than actually
+ * declared in the ivy file. This can be useful to publish additional metadata or reports. The extra
+ * artifacts array can be null (= no extra artifacts), and if non null only the name, type, ext url
+ * and extra attributes of the artifacts are really used. Other methods (on the artifacts) can
+ * return null safely.
+ *
+ * @see PublishEngine
+ */
+public class PublishOptions {
+ private String srcIvyPattern;
+
+ private String pubrevision;
+
+ private String status;
+
+ private Date pubdate;
+
+ private Artifact[] extraArtifacts;
+
+ private boolean validate;
+
+ private boolean overwrite;
+
+ private boolean update;
+
+ private boolean merge = true;
+
+ private String[] confs;
+
+ private boolean haltonmissing;
+
+ private String pubBranch;
+
+ private boolean warnonmissing;
+
+ public String[] getConfs() {
+ return confs;
+ }
+
+ public PublishOptions setConfs(String[] confs) {
+ this.confs = confs;
+ return this;
+ }
+
+ public Artifact[] getExtraArtifacts() {
+ return extraArtifacts;
+ }
+
+ public PublishOptions setExtraArtifacts(Artifact[] extraArtifacts) {
+ this.extraArtifacts = extraArtifacts;
+ return this;
+ }
+
+ public boolean isOverwrite() {
+ return overwrite;
+ }
+
+ public PublishOptions setOverwrite(boolean overwrite) {
+ this.overwrite = overwrite;
+ return this;
+ }
+
+ public Date getPubdate() {
+ return pubdate;
+ }
+
+ public PublishOptions setPubdate(Date pubdate) {
+ this.pubdate = pubdate;
+ return this;
+ }
+
+ public String getPubrevision() {
+ return pubrevision;
+ }
+
+ public PublishOptions setPubrevision(String pubrevision) {
+ this.pubrevision = pubrevision;
+ return this;
+ }
+
+ public String getSrcIvyPattern() {
+ return srcIvyPattern;
+ }
+
+ public PublishOptions setSrcIvyPattern(String srcIvyPattern) {
+ this.srcIvyPattern = srcIvyPattern;
+ return this;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public PublishOptions setStatus(String status) {
+ this.status = status;
+ return this;
+ }
+
+ public boolean isUpdate() {
+ return update;
+ }
+
+ public PublishOptions setUpdate(boolean update) {
+ this.update = update;
+ return this;
+ }
+
+ public boolean isMerge() {
+ return merge;
+ }
+
+ public PublishOptions setMerge(boolean merge) {
+ this.merge = merge;
+ return this;
+ }
+
+ public boolean isValidate() {
+ return validate;
+ }
+
+ public PublishOptions setValidate(boolean validate) {
+ this.validate = validate;
+ return this;
+ }
+
+ public boolean isHaltOnMissing() {
+ return haltonmissing;
+ }
+
+ public PublishOptions setHaltOnMissing(boolean haltonmissing) {
+ this.haltonmissing = haltonmissing;
+ return this;
+ }
+
+ public String getPubBranch() {
+ return pubBranch;
+ }
+
+ public PublishOptions setPubbranch(String pubbranch) {
+ this.pubBranch = pubbranch;
+ return this;
+ }
+
+ public boolean isWarnOnMissing() {
+ return warnonmissing;
+ }
+
+ public PublishOptions setWarnOnMissing(boolean warnonmissing) {
+ this.warnonmissing = warnonmissing;
+ return this;
+ }
+
+}
diff --git a/src/java/org/apache/ivy/core/report/ArtifactDownloadReport.java b/src/java/org/apache/ivy/core/report/ArtifactDownloadReport.java
new file mode 100644
index 0000000..76547f7
--- /dev/null
+++ b/src/java/org/apache/ivy/core/report/ArtifactDownloadReport.java
@@ -0,0 +1,195 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.report;
+
+import java.io.File;
+
+import org.apache.ivy.core.cache.ArtifactOrigin;
+import org.apache.ivy.core.module.descriptor.Artifact;
+
+/**
+ * Report on the download of an artifact from a repository to a local (cached) file.
+ * <p>
+ * Note that depending on cache implementation, the artifact may not be actually downloaded, but
+ * used directly from its original location.
+ * </p>
+ */
+public class ArtifactDownloadReport {
+ /**
+ * download details used when the download "fails" when the artifact is simply missing on the
+ * remote repository.
+ * <p>
+ * For historical reason the status can't be used to distinguish a real failure from a missing
+ * artifact by using the status, in both cases it's DownloadStatus.FAILED. The details message
+ * can be used for this purpose though.
+ * </p>
+ */
+ public static final String MISSING_ARTIFACT = "missing artifact";
+
+ private Artifact artifact;
+
+ private ArtifactOrigin origin;
+
+ private File localFile;
+
+ private DownloadStatus downloadStatus;
+
+ private long size;
+
+ private String downloadDetails = "";
+
+ private long downloadTimeMillis;
+
+ private File unpackedLocalFile;
+
+ public ArtifactDownloadReport(Artifact artifact) {
+ this.artifact = artifact;
+ }
+
+ public DownloadStatus getDownloadStatus() {
+ return downloadStatus;
+ }
+
+ public void setDownloadStatus(DownloadStatus downloadStatus) {
+ this.downloadStatus = downloadStatus;
+ }
+
+ public String getName() {
+ return artifact.getName();
+ }
+
+ /**
+ *
+ * @return the type of the downloaded artifact
+ */
+ public String getType() {
+ return artifact.getType();
+ }
+
+ public Artifact getArtifact() {
+ return artifact;
+ }
+
+ public String getExt() {
+ return artifact.getExt();
+ }
+
+ public long getSize() {
+ return size;
+ }
+
+ public void setSize(long size) {
+ this.size = size;
+ }
+
+ public void setArtifactOrigin(ArtifactOrigin origin) {
+ this.origin = origin;
+ }
+
+ public ArtifactOrigin getArtifactOrigin() {
+ return origin;
+ }
+
+ public void setDownloadDetails(String message) {
+ downloadDetails = message;
+ }
+
+ public String getDownloadDetails() {
+ return downloadDetails;
+ }
+
+ public void setDownloadTimeMillis(long l) {
+ downloadTimeMillis = l;
+ }
+
+ public long getDownloadTimeMillis() {
+ return downloadTimeMillis;
+ }
+
+ public String toString() {
+ if (downloadStatus == DownloadStatus.SUCCESSFUL) {
+ return "[SUCCESSFUL ] " + artifact + " (" + downloadTimeMillis + "ms)";
+ } else if (downloadStatus == DownloadStatus.FAILED) {
+ if (downloadDetails == MISSING_ARTIFACT) {
+ return "[NOT FOUND ] " + artifact + " (" + downloadTimeMillis + "ms)";
+ } else {
+ return "[FAILED ] " + artifact + ": " + downloadDetails + " ("
+ + downloadTimeMillis + "ms)";
+ }
+ } else if (downloadStatus == DownloadStatus.NO) {
+ return "[NOT REQUIRED] " + artifact;
+ } else {
+ return super.toString();
+ }
+ }
+
+ /**
+ * Returns the File where the artifact is available on the local filesystem, or
+ * <code>null</code> if and only if the artifact caching failed.
+ *
+ * @return the file where the artifact is now available on the local filesystem.
+ */
+ public File getLocalFile() {
+ return localFile;
+ }
+
+ public void setLocalFile(File localFile) {
+ this.localFile = localFile;
+ }
+
+ public boolean isDownloaded() {
+ return DownloadStatus.SUCCESSFUL == downloadStatus;
+ }
+
+ public void setUnpackedLocalFile(File unpackedLocalFile) {
+ this.unpackedLocalFile = unpackedLocalFile;
+ }
+
+ public File getUnpackedLocalFile() {
+ return unpackedLocalFile;
+ }
+
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((artifact == null) ? 0 : artifact.hashCode());
+ return result;
+ }
+
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ ArtifactDownloadReport other = (ArtifactDownloadReport) obj;
+ if (artifact == null) {
+ if (other.artifact != null) {
+ return false;
+ }
+ } else if (!artifact.equals(other.artifact)) {
+ return false;
+ }
+ return true;
+ }
+
+}
diff --git a/src/java/org/apache/ivy/core/report/ConfigurationResolveReport.java b/src/java/org/apache/ivy/core/report/ConfigurationResolveReport.java
new file mode 100644
index 0000000..a395783
--- /dev/null
+++ b/src/java/org/apache/ivy/core/report/ConfigurationResolveReport.java
@@ -0,0 +1,365 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.report;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.ivy.core.cache.ResolutionCacheManager;
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleId;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.resolve.IvyNode;
+import org.apache.ivy.core.resolve.ResolveEngine;
+import org.apache.ivy.core.resolve.ResolveOptions;
+import org.apache.ivy.core.sort.SortOptions;
+import org.apache.ivy.plugins.report.XmlReportParser;
+import org.apache.ivy.util.Message;
+
+/**
+ * Represents a whole resolution report for a module but for a specific configuration
+ */
+public class ConfigurationResolveReport {
+
+ private final ModuleDescriptor md;
+
+ private final String conf;
+
+ private final Date date;
+
+ private final ResolveOptions options;
+
+ private Map<IvyNode, List<ArtifactDownloadReport>> dependencyReports = new LinkedHashMap<IvyNode, List<ArtifactDownloadReport>>();
+
+ private Map<ModuleRevisionId, IvyNode> dependencies = new LinkedHashMap<ModuleRevisionId, IvyNode>();
+
+ private final ResolveEngine resolveEngine;
+
+ private Map<ModuleId, Collection<IvyNode>> modulesIdsMap = new LinkedHashMap<ModuleId, Collection<IvyNode>>();
+
+ private List<ModuleId> modulesIds;
+
+ private Boolean hasChanged = null;
+
+ public ConfigurationResolveReport(ResolveEngine resolveEngine, ModuleDescriptor md,
+ String conf, Date date, ResolveOptions options) {
+ this.resolveEngine = resolveEngine;
+ this.md = md;
+ this.conf = conf;
+ this.date = date;
+ this.options = options;
+ }
+
+ /**
+ * Check if the set of dependencies has changed since the previous execution of a resolution.<br/>
+ * This function use the report file found in the cache. So the function must be called before
+ * the new report is serialized there.</br> This function also use the internal dependencies
+ * that must already be filled. This function might be 'heavy' because it may have to parse the
+ * previous report.
+ *
+ * @return
+ */
+ public void checkIfChanged() {
+ ResolutionCacheManager cache = resolveEngine.getSettings().getResolutionCacheManager();
+ String resolveId = options.getResolveId();
+ File previousReportFile = cache.getConfigurationResolveReportInCache(resolveId, conf);
+ if (previousReportFile.exists()) {
+ try {
+ XmlReportParser parser = new XmlReportParser();
+ parser.parse(previousReportFile);
+ List<ModuleRevisionId> previousDeps = Arrays.asList(parser
+ .getDependencyRevisionIds());
+ HashSet<ModuleRevisionId> previousDepSet = new HashSet<ModuleRevisionId>(
+ previousDeps);
+ hasChanged = Boolean.valueOf(!previousDepSet.equals(getModuleRevisionIds()));
+ } catch (Exception e) {
+ Message.warn("Error while parsing configuration resolve report "
+ + previousReportFile.getAbsolutePath(), e);
+ hasChanged = Boolean.TRUE;
+ }
+ } else {
+ hasChanged = Boolean.TRUE;
+ }
+ }
+
+ /**
+ * @pre checkIfChanged has been called.
+ */
+ public boolean hasChanged() {
+ return hasChanged.booleanValue();
+ }
+
+ /**
+ * Returns all non evicted and non error dependency mrids The returned set is ordered so that a
+ * dependency will always be found before their own dependencies
+ *
+ * @return all non evicted and non error dependency mrids
+ */
+ public Set<ModuleRevisionId> getModuleRevisionIds() {
+ Set<ModuleRevisionId> mrids = new LinkedHashSet<ModuleRevisionId>();
+ for (IvyNode node : getDependencies()) {
+ if (!node.isEvicted(getConfiguration()) && !node.hasProblem()) {
+ mrids.add(node.getResolvedId());
+ }
+ }
+ return mrids;
+ }
+
+ public void addDependency(IvyNode node) {
+ dependencies.put(node.getId(), node);
+ dependencies.put(node.getResolvedId(), node);
+ dependencyReports.put(node, Collections.<ArtifactDownloadReport> emptyList());
+ }
+
+ public void updateDependency(ModuleRevisionId mrid, IvyNode node) {
+ dependencies.put(mrid, node);
+ }
+
+ public void addDependency(IvyNode node, DownloadReport report) {
+ dependencies.put(node.getId(), node);
+ dependencies.put(node.getResolvedId(), node);
+ List<ArtifactDownloadReport> adrs = new ArrayList<ArtifactDownloadReport>();
+ Artifact[] artifacts = node.getArtifacts(conf);
+ for (Artifact artifact : artifacts) {
+ ArtifactDownloadReport artifactReport = report.getArtifactReport(artifact);
+ if (artifactReport != null) {
+ adrs.add(artifactReport);
+ } else {
+ Message.debug("no report found for " + artifact);
+ }
+ }
+ dependencyReports.put(node, adrs);
+ }
+
+ public String getConfiguration() {
+ return conf;
+ }
+
+ public Date getDate() {
+ return date;
+ }
+
+ public ModuleDescriptor getModuleDescriptor() {
+ return md;
+ }
+
+ public IvyNode[] getUnresolvedDependencies() {
+ List<IvyNode> unresolved = new ArrayList<IvyNode>();
+ for (IvyNode node : getDependencies()) {
+ if (node.hasProblem()) {
+ unresolved.add(node);
+ }
+ }
+ return unresolved.toArray(new IvyNode[unresolved.size()]);
+ }
+
+ private Collection<IvyNode> getDependencies() {
+ return new LinkedHashSet<IvyNode>(dependencies.values());
+ }
+
+ public IvyNode[] getEvictedNodes() {
+ List<IvyNode> evicted = new ArrayList<IvyNode>();
+ for (IvyNode node : getDependencies()) {
+ if (node.isEvicted(conf)) {
+ evicted.add(node);
+ }
+ }
+ return evicted.toArray(new IvyNode[evicted.size()]);
+ }
+
+ private Set<ModuleRevisionId> getEvictedMrids() {
+ Set<ModuleRevisionId> evicted = new LinkedHashSet<ModuleRevisionId>();
+ IvyNode[] evictedNodes = getEvictedNodes();
+ for (IvyNode node : evictedNodes) {
+ evicted.add(node.getId());
+ }
+ return evicted;
+ }
+
+ public IvyNode[] getDownloadedNodes() {
+ List<IvyNode> downloaded = new ArrayList<IvyNode>();
+ for (IvyNode node : getDependencies()) {
+ if (node.isDownloaded() && node.getRealNode() == node) {
+ downloaded.add(node);
+ }
+ }
+ return downloaded.toArray(new IvyNode[downloaded.size()]);
+ }
+
+ public IvyNode[] getSearchedNodes() {
+ List<IvyNode> downloaded = new ArrayList<IvyNode>();
+ for (IvyNode node : getDependencies()) {
+ if (node.isSearched() && node.getRealNode() == node) {
+ downloaded.add(node);
+ }
+ }
+ return downloaded.toArray(new IvyNode[downloaded.size()]);
+ }
+
+ public ArtifactDownloadReport[] getDownloadReports(ModuleRevisionId mrid) {
+ Collection<ArtifactDownloadReport> col = dependencyReports.get(getDependency(mrid));
+ if (col == null) {
+ return new ArtifactDownloadReport[0];
+ }
+ return col.toArray(new ArtifactDownloadReport[col.size()]);
+ }
+
+ public IvyNode getDependency(ModuleRevisionId mrid) {
+ return dependencies.get(mrid);
+ }
+
+ /**
+ * gives all the modules ids concerned by this report, from the most dependent to the least one
+ *
+ * @return a list of ModuleId
+ */
+ public List<ModuleId> getModuleIds() {
+ if (modulesIds == null) {
+ List<IvyNode> sortedDependencies = resolveEngine.getSortEngine().sortNodes(
+ getDependencies(), SortOptions.SILENT);
+ Collections.reverse(sortedDependencies);
+ for (IvyNode dependency : sortedDependencies) {
+ ModuleId mid = dependency.getResolvedId().getModuleId();
+ Collection<IvyNode> deps = modulesIdsMap.get(mid);
+ if (deps == null) {
+ deps = new LinkedHashSet<IvyNode>();
+ modulesIdsMap.put(mid, deps);
+ }
+ deps.add(dependency);
+ }
+ modulesIds = new ArrayList<ModuleId>(modulesIdsMap.keySet());
+ }
+ return Collections.unmodifiableList(modulesIds);
+ }
+
+ public Collection<IvyNode> getNodes(ModuleId mid) {
+ if (modulesIds == null) {
+ getModuleIds();
+ }
+ return modulesIdsMap.get(mid);
+ }
+
+ public ResolveEngine getResolveEngine() {
+ return resolveEngine;
+ }
+
+ public int getArtifactsNumber() {
+ int total = 0;
+ for (Collection<ArtifactDownloadReport> reports : dependencyReports.values()) {
+ total += reports == null ? 0 : reports.size();
+ }
+ return total;
+ }
+
+ /**
+ * Get every report on the download requests.
+ *
+ * @return the list of reports, never <code>null</code>
+ */
+ public ArtifactDownloadReport[] getAllArtifactsReports() {
+ return getArtifactsReports(null, true);
+ }
+
+ /**
+ * Get the report on the download requests. The list of download report can be restricted to a
+ * specific download status, and also remove the download report for the evicted modules.
+ *
+ * @param downloadStatus
+ * the status of download to retreive. Set it to <code>null</code> for no restriction
+ * on the download status
+ * @param withEvicted
+ * set it to <code>true</code> if the report for the evicted modules have to be
+ * retrieved.
+ * @return the list of reports, never <code>null</code>
+ * @see ArtifactDownloadReport
+ */
+ public ArtifactDownloadReport[] getArtifactsReports(DownloadStatus downloadStatus,
+ boolean withEvicted) {
+ Collection<ArtifactDownloadReport> all = new LinkedHashSet<ArtifactDownloadReport>();
+ Collection<ModuleRevisionId> evictedMrids = null;
+ if (!withEvicted) {
+ evictedMrids = getEvictedMrids();
+ }
+ for (Collection<ArtifactDownloadReport> reports : dependencyReports.values()) {
+ for (ArtifactDownloadReport report : reports) {
+ if (downloadStatus != null && report.getDownloadStatus() != downloadStatus) {
+ continue;
+ }
+ if (withEvicted
+ || !evictedMrids.contains(report.getArtifact().getModuleRevisionId())) {
+ all.add(report);
+ }
+ }
+ }
+ return all.toArray(new ArtifactDownloadReport[all.size()]);
+ }
+
+ /**
+ * Get the report on the sucessfull download requests with the evicted modules
+ *
+ * @return the list of reports, never <code>null</code>
+ */
+ public ArtifactDownloadReport[] getDownloadedArtifactsReports() {
+ return getArtifactsReports(DownloadStatus.SUCCESSFUL, true);
+ }
+
+ /**
+ * Get the report on the failed download requests with the evicted modules
+ *
+ * @return the list of reports, never <code>null</code>
+ */
+ public ArtifactDownloadReport[] getFailedArtifactsReports() {
+ ArtifactDownloadReport[] allFailedReports = getArtifactsReports(DownloadStatus.FAILED, true);
+ return filterOutMergedArtifacts(allFailedReports);
+ }
+
+ public boolean hasError() {
+ return getUnresolvedDependencies().length > 0 || getFailedArtifactsReports().length > 0;
+ }
+
+ public int getNodesNumber() {
+ return getDependencies().size();
+ }
+
+ public static ArtifactDownloadReport[] filterOutMergedArtifacts(
+ ArtifactDownloadReport[] allFailedReports) {
+ Collection<ArtifactDownloadReport> adrs = new ArrayList<ArtifactDownloadReport>(
+ Arrays.asList(allFailedReports));
+ for (Iterator<ArtifactDownloadReport> iterator = adrs.iterator(); iterator.hasNext();) {
+ ArtifactDownloadReport adr = iterator.next();
+
+ if (adr.getArtifact().getExtraAttribute("ivy:merged") != null) {
+ iterator.remove();
+ }
+ }
+ return adrs.toArray(new ArtifactDownloadReport[adrs.size()]);
+ }
+
+}
diff --git a/src/java/org/apache/ivy/core/report/DownloadReport.java b/src/java/org/apache/ivy/core/report/DownloadReport.java
new file mode 100644
index 0000000..f2dfcb6
--- /dev/null
+++ b/src/java/org/apache/ivy/core/report/DownloadReport.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.report;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.ivy.core.module.descriptor.Artifact;
+
+/**
+ *
+ */
+public class DownloadReport {
+ private Map artifacts = new HashMap();
+
+ public void addArtifactReport(ArtifactDownloadReport adr) {
+ artifacts.put(adr.getArtifact(), adr);
+ }
+
+ public ArtifactDownloadReport[] getArtifactsReports() {
+ return (ArtifactDownloadReport[]) artifacts.values().toArray(
+ new ArtifactDownloadReport[artifacts.size()]);
+ }
+
+ public ArtifactDownloadReport[] getArtifactsReports(DownloadStatus status) {
+ List ret = new ArrayList(artifacts.size());
+ for (Iterator iter = artifacts.values().iterator(); iter.hasNext();) {
+ ArtifactDownloadReport adr = (ArtifactDownloadReport) iter.next();
+ if (adr.getDownloadStatus() == status) {
+ ret.add(adr);
+ }
+ }
+ return (ArtifactDownloadReport[]) ret.toArray(new ArtifactDownloadReport[ret.size()]);
+ }
+
+ public ArtifactDownloadReport getArtifactReport(Artifact artifact) {
+ return (ArtifactDownloadReport) artifacts.get(artifact);
+ }
+}
diff --git a/src/java/org/apache/ivy/core/report/DownloadStatus.java b/src/java/org/apache/ivy/core/report/DownloadStatus.java
new file mode 100644
index 0000000..f0364c2
--- /dev/null
+++ b/src/java/org/apache/ivy/core/report/DownloadStatus.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.report;
+
+/**
+ *
+ */
+public final class DownloadStatus {
+ private String name;
+
+ private DownloadStatus(String name) {
+ this.name = name;
+ }
+
+ /**
+ * means that download was not required
+ */
+ public static final DownloadStatus NO = new DownloadStatus("no");
+
+ public static final DownloadStatus SUCCESSFUL = new DownloadStatus("successful");
+
+ public static final DownloadStatus FAILED = new DownloadStatus("failed");
+
+ /**
+ * Returns the {@link DownloadStatus} corresponding to the given String representation.
+ *
+ * @return the {@link DownloadStatus} corresponding to the given String representation.
+ * @throws IllegalArgumentException
+ * if the given String does not correspond to any {@link DownloadStatus}.
+ */
+ public static final DownloadStatus fromString(String status) {
+ if (NO.name.equals(status)) {
+ return NO;
+ }
+ if (SUCCESSFUL.name.equals(status)) {
+ return SUCCESSFUL;
+ }
+ if (FAILED.name.equals(status)) {
+ return FAILED;
+ }
+ throw new IllegalArgumentException("unknown download status '" + status + "'");
+ }
+
+ public String toString() {
+ return name;
+ }
+}
diff --git a/src/java/org/apache/ivy/core/report/MetadataArtifactDownloadReport.java b/src/java/org/apache/ivy/core/report/MetadataArtifactDownloadReport.java
new file mode 100644
index 0000000..be3181f
--- /dev/null
+++ b/src/java/org/apache/ivy/core/report/MetadataArtifactDownloadReport.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.report;
+
+import java.io.File;
+
+import org.apache.ivy.core.module.descriptor.Artifact;
+
+public class MetadataArtifactDownloadReport extends ArtifactDownloadReport {
+ private boolean isSearched;
+
+ private File originalLocalFile;
+
+ public MetadataArtifactDownloadReport(Artifact artifact) {
+ super(artifact);
+ }
+
+ /**
+ * Returns <code>true</code> if the resolution of this metadata artifact required at least one
+ * access to the repository, or <code>false</code> if only provisioned data was used.
+ *
+ * @return <code>true</code> if the resolution of this metadata artifact required at least one
+ * access to the repository
+ */
+ public boolean isSearched() {
+ return isSearched;
+ }
+
+ public void setSearched(boolean isSearched) {
+ this.isSearched = isSearched;
+ }
+
+ /**
+ * Returns the location on the local filesystem where the original metadata artifact is
+ * provisioned, or <code>null</code> if the provisioning failed.
+ *
+ * @return the location on the local filesystem where the original metadata artifact is
+ * provisioned.
+ */
+ public File getOriginalLocalFile() {
+ return originalLocalFile;
+ }
+
+ public void setOriginalLocalFile(File originalLocalFile) {
+ this.originalLocalFile = originalLocalFile;
+ }
+
+}
diff --git a/src/java/org/apache/ivy/core/report/ResolveReport.java b/src/java/org/apache/ivy/core/report/ResolveReport.java
new file mode 100644
index 0000000..0f12488
--- /dev/null
+++ b/src/java/org/apache/ivy/core/report/ResolveReport.java
@@ -0,0 +1,436 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.report;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.ivy.core.cache.ResolutionCacheManager;
+import org.apache.ivy.core.module.descriptor.Configuration;
+import org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleId;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.resolve.IvyNode;
+import org.apache.ivy.core.resolve.ResolveOptions;
+import org.apache.ivy.core.settings.IvySettings;
+import org.apache.ivy.plugins.report.ReportOutputter;
+import org.apache.ivy.util.filter.Filter;
+
+/**
+ * Represents a whole resolution report for a module
+ */
+public class ResolveReport {
+ private ModuleDescriptor md;
+
+ /** String conf -> ConfigurationResolveReport report */
+ private Map confReports = new LinkedHashMap();
+
+ private List problemMessages = new ArrayList();
+
+ /**
+ * the list of all dependencies resolved, ordered from the more dependent to the less dependent
+ */
+ private List/* <IvyNode> */dependencies = new ArrayList();
+
+ private List/* <Artifact> */artifacts = new ArrayList();
+
+ private long resolveTime;
+
+ private long downloadTime;
+
+ private String resolveId;
+
+ private long downloadSize;
+
+ public ResolveReport(ModuleDescriptor md) {
+ this(md, ResolveOptions.getDefaultResolveId(md));
+ }
+
+ public ResolveReport(ModuleDescriptor md, String resolveId) {
+ this.md = md;
+ this.resolveId = resolveId;
+ }
+
+ public void addReport(String conf, ConfigurationResolveReport report) {
+ confReports.put(conf, report);
+ }
+
+ public ConfigurationResolveReport getConfigurationReport(String conf) {
+ return (ConfigurationResolveReport) confReports.get(conf);
+ }
+
+ public String[] getConfigurations() {
+ return (String[]) confReports.keySet().toArray(new String[confReports.size()]);
+ }
+
+ public boolean hasError() {
+ boolean hasError = false;
+ for (Iterator it = confReports.values().iterator(); it.hasNext() && !hasError;) {
+ ConfigurationResolveReport report = (ConfigurationResolveReport) it.next();
+ hasError |= report.hasError();
+ }
+ return hasError;
+ }
+
+ public void output(ReportOutputter[] outputters, ResolutionCacheManager cacheMgr,
+ ResolveOptions options) throws IOException {
+ for (int i = 0; i < outputters.length; i++) {
+ outputters[i].output(this, cacheMgr, options);
+ }
+ }
+
+ public ModuleDescriptor getModuleDescriptor() {
+ return md;
+ }
+
+ public IvyNode[] getEvictedNodes() {
+ Collection all = new LinkedHashSet();
+ for (Iterator iter = confReports.values().iterator(); iter.hasNext();) {
+ ConfigurationResolveReport report = (ConfigurationResolveReport) iter.next();
+ all.addAll(Arrays.asList(report.getEvictedNodes()));
+ }
+ return (IvyNode[]) all.toArray(new IvyNode[all.size()]);
+ }
+
+ public IvyNode[] getUnresolvedDependencies() {
+ Collection all = new LinkedHashSet();
+ for (Iterator iter = confReports.values().iterator(); iter.hasNext();) {
+ ConfigurationResolveReport report = (ConfigurationResolveReport) iter.next();
+ all.addAll(Arrays.asList(report.getUnresolvedDependencies()));
+ }
+ return (IvyNode[]) all.toArray(new IvyNode[all.size()]);
+ }
+
+ /**
+ * Get every report on the download requests.
+ *
+ * @return the list of reports, never <code>null</code>
+ */
+ public ArtifactDownloadReport[] getFailedArtifactsReports() {
+ return ConfigurationResolveReport.filterOutMergedArtifacts(getArtifactsReports(
+ DownloadStatus.FAILED, true));
+ }
+
+ /**
+ * Get every report on the download requests.
+ *
+ * @return the list of reports, never <code>null</code>
+ */
+ public ArtifactDownloadReport[] getAllArtifactsReports() {
+ return getArtifactsReports(null, true);
+ }
+
+ /**
+ * Get the report on the download requests. The list of download report can be restricted to a
+ * specific download status, and also remove the download report for the evicted modules.
+ *
+ * @param downloadStatus
+ * the status of download to retreive. Set it to <code>null</code> for no restriction
+ * on the download status
+ * @param withEvicted
+ * set it to <code>true</code> if the report for the evicted modules have to be
+ * retrieved, <code>false</code> to exclude reports from modules evicted in all
+ * configurations.
+ * @return the list of reports, never <code>null</code>
+ * @see ConfigurationResolveReport#getArtifactsReports(DownloadStatus, boolean)
+ */
+ public ArtifactDownloadReport[] getArtifactsReports(DownloadStatus downloadStatus,
+ boolean withEvicted) {
+ Collection all = new LinkedHashSet();
+ for (Iterator iter = confReports.values().iterator(); iter.hasNext();) {
+ ConfigurationResolveReport report = (ConfigurationResolveReport) iter.next();
+ ArtifactDownloadReport[] reports = report.getArtifactsReports(downloadStatus,
+ withEvicted);
+ all.addAll(Arrays.asList(reports));
+ }
+ return (ArtifactDownloadReport[]) all.toArray(new ArtifactDownloadReport[all.size()]);
+ }
+
+ public ArtifactDownloadReport[] getArtifactsReports(ModuleRevisionId mrid) {
+ Collection all = new LinkedHashSet();
+ for (Iterator iter = confReports.values().iterator(); iter.hasNext();) {
+ ConfigurationResolveReport report = (ConfigurationResolveReport) iter.next();
+ all.addAll(Arrays.asList(report.getDownloadReports(mrid)));
+ }
+ return (ArtifactDownloadReport[]) all.toArray(new ArtifactDownloadReport[all.size()]);
+ }
+
+ public void checkIfChanged() {
+ for (Iterator iter = confReports.values().iterator(); iter.hasNext();) {
+ ConfigurationResolveReport report = (ConfigurationResolveReport) iter.next();
+ report.checkIfChanged();
+ }
+ }
+
+ /** Can only be called if checkIfChanged has been called */
+ public boolean hasChanged() {
+ for (Iterator iter = confReports.values().iterator(); iter.hasNext();) {
+ ConfigurationResolveReport report = (ConfigurationResolveReport) iter.next();
+ if (report.hasChanged()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void setProblemMessages(List problems) {
+ problemMessages = problems;
+ }
+
+ public List getProblemMessages() {
+ return problemMessages;
+ }
+
+ public List getAllProblemMessages() {
+ List ret = new ArrayList(problemMessages);
+ for (Iterator iter = confReports.values().iterator(); iter.hasNext();) {
+ ConfigurationResolveReport r = (ConfigurationResolveReport) iter.next();
+ IvyNode[] unresolved = r.getUnresolvedDependencies();
+ for (int i = 0; i < unresolved.length; i++) {
+ String errMsg = unresolved[i].getProblemMessage();
+ if (errMsg.length() > 0) {
+ ret.add("unresolved dependency: " + unresolved[i].getId() + ": " + errMsg);
+ } else {
+ ret.add("unresolved dependency: " + unresolved[i].getId());
+ }
+ }
+ ArtifactDownloadReport[] adrs = r.getFailedArtifactsReports();
+ for (int i = 0; i < adrs.length; i++) {
+ ret.add("download failed: " + adrs[i].getArtifact());
+ }
+ }
+ return ret;
+ }
+
+ public void setDependencies(List dependencies, Filter artifactFilter) {
+ this.dependencies = dependencies;
+ // collect list of artifacts
+ artifacts = new ArrayList();
+ for (Iterator iter = dependencies.iterator(); iter.hasNext();) {
+ IvyNode dependency = (IvyNode) iter.next();
+ if (!dependency.isCompletelyEvicted() && !dependency.hasProblem()) {
+ artifacts.addAll(Arrays.asList(dependency.getSelectedArtifacts(artifactFilter)));
+ }
+ // update the configurations reports with the dependencies
+ // these reports will be completed later with download information, if any
+ String[] dconfs = dependency.getRootModuleConfigurations();
+ for (int j = 0; j < dconfs.length; j++) {
+ ConfigurationResolveReport configurationReport = getConfigurationReport(dconfs[j]);
+ if (configurationReport != null) {
+ configurationReport.addDependency(dependency);
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns the list of all dependencies concerned by this report as a List of IvyNode ordered
+ * from the more dependent to the least one
+ *
+ * @return The list of all dependencies.
+ */
+ public List/* <IvyNode> */getDependencies() {
+ return dependencies;
+ }
+
+ /**
+ * Returns the list of all artifacts which should be downloaded per this resolve To know if the
+ * artifact have actually been downloaded use information found in ConfigurationResolveReport.
+ *
+ * @return The list of all artifacts.
+ */
+ public List/* <Artifact> */getArtifacts() {
+ return artifacts;
+ }
+
+ /**
+ * gives all the modules ids concerned by this report, from the most dependent to the least one
+ *
+ * @return a list of ModuleId
+ */
+ public List getModuleIds() {
+ List ret = new ArrayList();
+ List sortedDependencies = new ArrayList(dependencies);
+ for (Iterator iter = sortedDependencies.iterator(); iter.hasNext();) {
+ IvyNode dependency = (IvyNode) iter.next();
+ ModuleId mid = dependency.getResolvedId().getModuleId();
+ if (!ret.contains(mid)) {
+ ret.add(mid);
+ }
+ }
+ return ret;
+ }
+
+ public void setResolveTime(long elapsedTime) {
+ resolveTime = elapsedTime;
+ }
+
+ public long getResolveTime() {
+ return resolveTime;
+ }
+
+ public void setDownloadTime(long elapsedTime) {
+ downloadTime = elapsedTime;
+ }
+
+ public long getDownloadTime() {
+ return downloadTime;
+ }
+
+ public void setDownloadSize(long size) {
+ this.downloadSize = size;
+ }
+
+ /**
+ * The total size of downloaded artifacts, in bytes.
+ * <p>
+ * This only includes artifacts actually downloaded to cache (DownloadStatus.SUCCESSFUL), and
+ * not artifacts already in cache or used at their original location.
+ * </p>
+ *
+ * @return The total size of downloaded artifacts, in bytes.
+ */
+ public long getDownloadSize() {
+ return downloadSize;
+ }
+
+ public String getResolveId() {
+ return resolveId;
+ }
+
+ /**
+ * Get every configuration which extends the specified one. The returned list also includes the
+ * specified one.
+ *
+ * @param extended
+ * @return
+ */
+ private String[] getExtendingConfs(String extended) {
+ String[] allConfs = md.getConfigurationsNames();
+ Set<String> extendingConfs = new HashSet<String>();
+ extendingConfs.add(extended);
+ for (int i = 0; i < allConfs.length; i++) {
+ gatherExtendingConfs(extendingConfs, allConfs[i], extended);
+ }
+ return extendingConfs.toArray(new String[extendingConfs.size()]);
+ }
+
+ private boolean gatherExtendingConfs(Set<String> extendingConfs, String conf, String extended) {
+ if (extendingConfs.contains(conf)) {
+ return true;
+ }
+ String[] ext = md.getConfiguration(conf).getExtends();
+ if (ext == null || ext.length == 0) {
+ return false;
+ }
+ for (int i = 0; i < ext.length; i++) {
+ if (extendingConfs.contains(ext[i])) {
+ extendingConfs.add(conf);
+ return true;
+ }
+ if (ext[i].equals(extended)) {
+ extendingConfs.add(conf);
+ return true;
+ }
+ if (gatherExtendingConfs(extendingConfs, ext[i], extended)) {
+ extendingConfs.add(conf);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public ModuleDescriptor toFixedModuleDescriptor(IvySettings settings, List<ModuleId> midToKeep) {
+ DefaultModuleDescriptor fixedmd = new DefaultModuleDescriptor(md.getModuleRevisionId(),
+ md.getStatus(), new Date());
+ fixedmd.getExtraInfos().addAll(md.getExtraInfos());
+
+ // copy configurations
+ List<String> resolvedConfs = Arrays.asList(getConfigurations());
+ for (String conf : resolvedConfs) {
+ fixedmd.addConfiguration(new Configuration(conf));
+ }
+
+ if (midToKeep != null && !midToKeep.isEmpty()) {
+ // add dependency we want to keep from the original module descriptor
+ DependencyDescriptor[] deps = md.getDependencies();
+ for (int i = 0; i < deps.length; i++) {
+ if (midToKeep.contains(deps[i].getDependencyId())) {
+ DefaultDependencyDescriptor dep = new DefaultDependencyDescriptor(fixedmd,
+ deps[i].getDependencyRevisionId(), true, false, false);
+ List<String> confs = Arrays.asList(deps[i].getModuleConfigurations());
+ if (confs.size() == 1 && confs.get(0).equals("*")) {
+ confs = resolvedConfs;
+ }
+ for (String conf : confs) {
+ String[] extendedConfs = getExtendingConfs(conf);
+ String[] depConfs = deps[i].getDependencyConfigurations(conf);
+ for (String extendedConf : extendedConfs) {
+ if (resolvedConfs.contains(extendedConf)) {
+ for (String depConf : depConfs) {
+ dep.addDependencyConfiguration(extendedConf, depConf);
+ }
+ }
+ }
+ }
+ fixedmd.addDependency(dep);
+ }
+ }
+ }
+
+ // add resolved dependencies
+ for (int i = 0; i < dependencies.size(); i++) {
+ IvyNode node = (IvyNode) dependencies.get(i);
+ if (midToKeep != null && midToKeep.contains(node.getModuleId())) {
+ continue;
+ }
+ String[] rootConfs = node.getRootModuleConfigurations();
+ for (int j = 0; j < rootConfs.length; j++) {
+ if (node.isEvicted(rootConfs[j])) {
+ continue;
+ }
+ if (node.getAllArtifacts().length == 0) {
+ // no artifact: it was probably useful transitively, hence it is useless here
+ break;
+ }
+ DefaultDependencyDescriptor dep = new DefaultDependencyDescriptor(fixedmd,
+ node.getResolvedId(), true, false, false);
+ String[] targetConfs = node.getConfigurations(rootConfs[j]);
+ for (int k = 0; k < targetConfs.length; k++) {
+ dep.addDependencyConfiguration(rootConfs[j], targetConfs[k]);
+ }
+ fixedmd.addDependency(dep);
+ }
+ }
+
+ return fixedmd;
+ }
+}
diff --git a/src/java/org/apache/ivy/core/repository/RepositoryManagementEngine.java b/src/java/org/apache/ivy/core/repository/RepositoryManagementEngine.java
new file mode 100644
index 0000000..fe552ed
--- /dev/null
+++ b/src/java/org/apache/ivy/core/repository/RepositoryManagementEngine.java
@@ -0,0 +1,339 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.repository;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.TreeSet;
+
+import org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.resolve.ResolveData;
+import org.apache.ivy.core.resolve.ResolveEngine;
+import org.apache.ivy.core.resolve.ResolveOptions;
+import org.apache.ivy.core.resolve.ResolvedModuleRevision;
+import org.apache.ivy.core.search.SearchEngine;
+import org.apache.ivy.plugins.latest.ArtifactInfo;
+import org.apache.ivy.plugins.matcher.PatternMatcher;
+import org.apache.ivy.plugins.matcher.RegexpPatternMatcher;
+import org.apache.ivy.plugins.version.VersionMatcher;
+import org.apache.ivy.util.MemoryUtil;
+import org.apache.ivy.util.Message;
+
+/**
+ * The repository management can be used to load all metadata from a repository, analyze them, and
+ * provide a bunch of information about the whole repository state.
+ * <p>
+ * Since loading all metadata from a repository is not a light task, this engine should only be used
+ * on a machine having good access to the repository (on the same filesystem being usually the best
+ * suited).
+ * </p>
+ * <p>
+ * To access information, you usually have before to call a method to init the data: {@link #load()}
+ * is used to load repository metadata, {@link #analyze()} is used to analyze them. These methods
+ * being very time consuming, they must always be called explicitly.
+ * </p>
+ * <p>
+ * On a large repository, this engine can be very memory consuming to use, it is not suited to be
+ * used in a long running process, but rather in short process loading data and taking action about
+ * the current state of the repository.
+ * </p>
+ * <p>
+ * This engine is not intended to be used concurrently with publish, the order of repository loaded
+ * being undeterministic and long, it could end up in having an inconsistent in memory state.
+ * </p>
+ * <p>
+ * For better performance, we strongly suggest using this engine with cache in useOrigin mode.
+ * </p>
+ */
+public class RepositoryManagementEngine {
+ private static final double THOUSAND = 1000.0;
+
+ private static final int KILO = 1024;
+
+ // /////////////////////////////////////////
+ // state loaded on #load()
+ // /////////////////////////////////////////
+
+ /**
+ * True if the repository has already been loaded, false otherwise.
+ */
+ private boolean loaded;
+
+ /**
+ * ModuleDescriptors stored by ModuleRevisionId
+ */
+ private Map/* <ModuleRevisionId,ModuleDescriptor> */revisions = new HashMap();
+
+ /**
+ * ModuleRevisionId for which loading was not possible, with corresponding error message.
+ */
+ private Map/* <ModuleRevisionId,String> */errors = new HashMap();
+
+ /**
+ * List of ModuleRevisionId per ModuleId.
+ */
+ private Map/* <ModuleId,Collection<ModuleRevisionId>> */modules = new HashMap();
+
+ // /////////////////////////////////////////
+ // state loaded on #analyze()
+ // /////////////////////////////////////////
+
+ /**
+ * True when the repository has been analyzed, false otherwise
+ */
+ private boolean analyzed;
+
+ /**
+ * Cache from requested module revision id to actual module revision id.
+ */
+ private Map/* <ModuleRevisionId,ModuleRevisionId> */cache = new HashMap();
+
+ /**
+ * list of dependers per ModuleRevisionId.
+ */
+ private Map/* <ModuleRevisionId,List<ModuleRevisionId>> */dependers = new HashMap();
+
+ // /////////////////////////////////////////
+ // dependencies
+ // /////////////////////////////////////////
+ private SearchEngine searchEngine;
+
+ private ResolveEngine resolveEngine;
+
+ private RepositoryManagementEngineSettings settings;
+
+ public RepositoryManagementEngine(RepositoryManagementEngineSettings settings,
+ SearchEngine searchEngine, ResolveEngine resolveEngine) {
+ this.settings = settings;
+ this.searchEngine = searchEngine;
+ this.resolveEngine = resolveEngine;
+ }
+
+ /**
+ * Loads data from the repository.
+ * <p>
+ * This method usually takes a long time to proceed. It should never be called from event
+ * dispatch thread in a GUI.
+ * </p>
+ */
+ public void load() {
+ long startingMemoryUse = 0;
+ if (settings.dumpMemoryUsage()) {
+ startingMemoryUse = MemoryUtil.getUsedMemory();
+ }
+ long startTime = System.currentTimeMillis();
+ Message.rawinfo("searching modules... ");
+ ModuleRevisionId[] mrids = searchModules();
+ Message.info("loading repository metadata...");
+ for (int i = 0; i < mrids.length; i++) {
+ try {
+ loadModuleRevision(mrids[i]);
+ } catch (Exception e) {
+ Message.debug(e);
+ errors.put(mrids[i], e.getMessage());
+ }
+ }
+ long endTime = System.currentTimeMillis();
+ Message.info("\nrepository loaded: "
+ + modules.size()
+ + " modules; "
+ + revisions.size()
+ + " revisions; "
+ + (settings.dumpMemoryUsage() ? (MemoryUtil.getUsedMemory() - startingMemoryUse)
+ / KILO + "kB; " : "") + (endTime - startTime) / THOUSAND + "s");
+ loaded = true;
+ }
+
+ /**
+ * Analyze data in the repository.
+ * <p>
+ * This method may take a long time to proceed. It should never be called from event dispatch
+ * thread in a GUI.
+ * </p>
+ *
+ * @throws IllegalStateException
+ * if the repository has not been loaded yet
+ * @see #load()
+ */
+ public void analyze() {
+ ensureLoaded();
+ Message.info("\nanalyzing dependencies...");
+ for (Iterator iterator = revisions.values().iterator(); iterator.hasNext();) {
+ ModuleDescriptor md = (ModuleDescriptor) iterator.next();
+ DependencyDescriptor[] dds = md.getDependencies();
+ for (int i = 0; i < dds.length; i++) {
+ ModuleRevisionId dep = getDependency(dds[i]);
+ if (dep == null) {
+ Message.warn("inconsistent repository: declared dependency not found: "
+ + dds[i]);
+ } else {
+ getDependers(dep).add(md.getModuleRevisionId());
+ }
+ }
+ Message.progress();
+ }
+ analyzed = true;
+ }
+
+ /**
+ * Returns the number of Module Revision in the repository.
+ *
+ * @return the number of module revisions in the repository.
+ * @throws IllegalStateException
+ * if the repository has not been loaded yet
+ * @see #load()
+ */
+ public int getRevisionsNumber() {
+ ensureLoaded();
+ return revisions.size();
+ }
+
+ /**
+ * Returns the number of ModuleId in the repository.
+ *
+ * @return the number of ModuleId in the repository.
+ * @throws IllegalStateException
+ * if the repository has not been loaded yet
+ * @see #load()
+ */
+ public int getModuleIdsNumber() {
+ ensureLoaded();
+ return modules.size();
+ }
+
+ /**
+ * Returns Module Revisions which have no dependers.
+ *
+ * @return a Collection of the {@link ModuleRevisionId} of module revisions which have no
+ * dependers in the repository.
+ * @throws IllegalStateException
+ * if the repository has not been analyzed yet
+ * @see #analyze()
+ */
+ public Collection getOrphans() {
+ ensureAnalyzed();
+ Collection orphans = new HashSet(revisions.keySet());
+ orphans.removeAll(dependers.keySet());
+ return orphans;
+ }
+
+ private ModuleRevisionId[] searchModules() {
+ ModuleRevisionId[] mrids = searchEngine.listModules(ModuleRevisionId.newInstance(
+ PatternMatcher.ANY_EXPRESSION, PatternMatcher.ANY_EXPRESSION,
+ PatternMatcher.ANY_EXPRESSION, PatternMatcher.ANY_EXPRESSION),
+ RegexpPatternMatcher.INSTANCE);
+ return mrids;
+ }
+
+ private ModuleRevisionId getDependency(DependencyDescriptor dd) {
+ ModuleRevisionId askedMrid = dd.getDependencyRevisionId();
+ VersionMatcher vmatcher = settings.getVersionMatcher();
+ if (vmatcher.isDynamic(askedMrid)) {
+ ModuleRevisionId mrid = (ModuleRevisionId) cache.get(askedMrid);
+ if (mrid == null) {
+ Collection revs = getAllRevisions(askedMrid);
+ for (Iterator iterator = revs.iterator(); iterator.hasNext();) {
+ ModuleDescriptor md = (ModuleDescriptor) iterator.next();
+ if (vmatcher.needModuleDescriptor(askedMrid, md.getResolvedModuleRevisionId())) {
+ if (vmatcher.accept(askedMrid, md)) {
+ mrid = md.getResolvedModuleRevisionId();
+ break;
+ }
+ } else {
+ if (vmatcher.accept(askedMrid, md.getResolvedModuleRevisionId())) {
+ mrid = md.getResolvedModuleRevisionId();
+ break;
+ }
+ }
+ }
+ if (mrid == null) {
+ return null;
+ } else {
+ cache.put(askedMrid, mrid);
+ }
+ }
+ return mrid;
+ } else {
+ return askedMrid;
+ }
+ }
+
+ private Collection getDependers(ModuleRevisionId id) {
+ Collection depders = (Collection) dependers.get(id);
+ if (depders == null) {
+ depders = new ArrayList();
+ dependers.put(id, depders);
+ }
+ return depders;
+ }
+
+ private void loadModuleRevision(ModuleRevisionId mrid) throws Exception {
+ ResolvedModuleRevision module = settings.getResolver(mrid).getDependency(
+ new DefaultDependencyDescriptor(mrid, false), newResolveData());
+ if (module == null) {
+ Message.warn("module not found while listed: " + mrid);
+ } else {
+ revisions.put(module.getId(), module.getDescriptor());
+ getAllRevisions(module.getId()).add(module.getDescriptor());
+ }
+ Message.progress();
+ }
+
+ private Collection getAllRevisions(ModuleRevisionId id) {
+ Collection revisions = (Collection) modules.get(id.getModuleId());
+ if (revisions == null) {
+ revisions = new TreeSet(new Comparator() {
+ public int compare(Object o1, Object o2) {
+ ModuleDescriptor md1 = (ModuleDescriptor) o1;
+ ModuleDescriptor md2 = (ModuleDescriptor) o2;
+ // we use reverse order compared to latest revision, to have latest revision
+ // first
+ return settings.getDefaultLatestStrategy().sort(new ArtifactInfo[] {md1, md2})
+ .get(0).equals(md1) ? 1 : -1;
+ }
+ });
+ modules.put(id.getModuleId(), revisions);
+ }
+ return revisions;
+ }
+
+ private ResolveData newResolveData() {
+ return new ResolveData(resolveEngine, new ResolveOptions());
+ }
+
+ private void ensureAnalyzed() {
+ if (!analyzed) {
+ throw new IllegalStateException(
+ "repository must have been analyzed to perform this method");
+ }
+ }
+
+ private void ensureLoaded() {
+ if (!loaded) {
+ throw new IllegalStateException("repository must have be loaded to perform this method");
+ }
+ }
+}
diff --git a/src/java/org/apache/ivy/core/repository/RepositoryManagementEngineSettings.java b/src/java/org/apache/ivy/core/repository/RepositoryManagementEngineSettings.java
new file mode 100644
index 0000000..ff946f6
--- /dev/null
+++ b/src/java/org/apache/ivy/core/repository/RepositoryManagementEngineSettings.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.repository;
+
+import org.apache.ivy.core.resolve.ResolveEngineSettings;
+
+public interface RepositoryManagementEngineSettings extends ResolveEngineSettings {
+ public boolean dumpMemoryUsage();
+}
diff --git a/src/java/org/apache/ivy/core/resolve/DownloadOptions.java b/src/java/org/apache/ivy/core/resolve/DownloadOptions.java
new file mode 100644
index 0000000..e3f8555
--- /dev/null
+++ b/src/java/org/apache/ivy/core/resolve/DownloadOptions.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.resolve;
+
+import org.apache.ivy.core.LogOptions;
+
+public class DownloadOptions extends LogOptions {
+
+ public DownloadOptions() {
+ }
+
+}
diff --git a/src/java/org/apache/ivy/core/resolve/IvyNode.java b/src/java/org/apache/ivy/core/resolve/IvyNode.java
new file mode 100644
index 0000000..dec51d5
--- /dev/null
+++ b/src/java/org/apache/ivy/core/resolve/IvyNode.java
@@ -0,0 +1,1344 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.resolve;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Stack;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.ivy.core.IvyContext;
+import org.apache.ivy.core.LogOptions;
+import org.apache.ivy.core.event.resolve.EndResolveDependencyEvent;
+import org.apache.ivy.core.event.resolve.StartResolveDependencyEvent;
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.Configuration;
+import org.apache.ivy.core.module.descriptor.DefaultArtifact;
+import org.apache.ivy.core.module.descriptor.DependencyArtifactDescriptor;
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.IncludeRule;
+import org.apache.ivy.core.module.descriptor.MDArtifact;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ArtifactId;
+import org.apache.ivy.core.module.id.ModuleId;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.resolve.IvyNodeCallers.Caller;
+import org.apache.ivy.core.resolve.IvyNodeEviction.EvictionData;
+import org.apache.ivy.plugins.conflict.ConflictManager;
+import org.apache.ivy.plugins.conflict.LatestCompatibleConflictManager;
+import org.apache.ivy.plugins.matcher.MatcherHelper;
+import org.apache.ivy.plugins.resolver.DependencyResolver;
+import org.apache.ivy.util.Message;
+import org.apache.ivy.util.StringUtils;
+import org.apache.ivy.util.filter.Filter;
+import org.apache.ivy.util.filter.FilterHelper;
+
+public class IvyNode implements Comparable {
+ private static final Pattern FALLBACK_CONF_PATTERN = Pattern.compile("(.+)\\((.*)\\)");
+
+ // //////// CONTEXT
+ private ResolveData data;
+
+ private ResolveEngineSettings settings;
+
+ // //////// DELEGATES
+ private IvyNodeCallers callers;
+
+ private IvyNodeEviction eviction;
+
+ // //////// MAIN DATA
+
+ private IvyNode root;
+
+ // id as requested, i.e. may be with latest rev
+ private ModuleRevisionId id;
+
+ // set only when node has been built or updated from a DependencyDescriptor
+ // Map(IvyNode parent -> DependencyDescriptor)
+ private Map dds = new HashMap();
+
+ // Set when data has been loaded only, or when constructed from a module descriptor
+ private ModuleDescriptor md;
+
+ private ResolvedModuleRevision module;
+
+ // //////// LOADING METADATA
+ private Exception problem = null;
+
+ private boolean downloaded = false;
+
+ private boolean searched = false;
+
+ private Collection confsToFetch = new HashSet();
+
+ private Collection fetchedConfigurations = new HashSet();
+
+ private Collection loadedRootModuleConfs = new HashSet();
+
+ // //////// USAGE DATA
+
+ private IvyNodeUsage usage = new IvyNodeUsage(this);
+
+ // usage information merged from evicted nodes this node is "replacing"
+ private Map/* <ModuleRevisionId, IvyNodeUsage> */mergedUsages = new LinkedHashMap();
+
+ public IvyNode(ResolveData data, IvyNode parent, DependencyDescriptor dd) {
+ id = dd.getDependencyRevisionId();
+ dds.put(parent, dd);
+ root = parent.getRoot();
+ init(data);
+ }
+
+ public IvyNode(ResolveData data, ModuleDescriptor md) {
+ id = md.getModuleRevisionId();
+ this.md = md;
+ root = this;
+ init(data);
+ }
+
+ private void init(ResolveData data) {
+ this.data = data;
+ settings = data.getSettings();
+ eviction = new IvyNodeEviction(this);
+ callers = new IvyNodeCallers(this);
+ }
+
+ /**
+ * After the call node may be discarded. To avoid using discarded node, make sure to get the
+ * real node after the call IvyNode node = ... node.loadData(); node = node.getRealNode(); ...
+ */
+ public boolean loadData(String rootModuleConf, IvyNode parent, String parentConf, String conf,
+ boolean shouldBePublic, IvyNodeUsage usage) {
+ Message.debug("loadData of " + this.toString() + " of rootConf=" + rootModuleConf);
+ if (!isRoot() && (data.getReport() != null)) {
+ data.getReport().addDependency(this);
+ }
+
+ boolean loaded = false;
+ if (hasProblem()) {
+ Message.debug("Node has problem. Skip loading");
+ } else if (isEvicted(rootModuleConf)) {
+ Message.debug(rootModuleConf + " is evicted. Skip loading");
+ } else if (!hasConfigurationsToLoad() && isRootModuleConfLoaded(rootModuleConf)) {
+ Message.debug(rootModuleConf + " is loaded and no conf to load. Skip loading");
+ } else {
+ markRootModuleConfLoaded(rootModuleConf);
+ if (md == null) {
+ DependencyResolver resolver = data.getSettings().getResolver(getId());
+ if (resolver == null) {
+ Message.error("no resolver found for " + getModuleId()
+ + ": check your configuration");
+ problem = new RuntimeException("no resolver found for " + getModuleId()
+ + ": check your configuration");
+ return false;
+ }
+ try {
+ Message.debug("\tusing " + resolver + " to resolve " + getId());
+ DependencyDescriptor dependencyDescriptor = getDependencyDescriptor(parent);
+ long start = System.currentTimeMillis();
+ ModuleRevisionId requestedRevisionId = dependencyDescriptor
+ .getDependencyRevisionId();
+ data.getEventManager().fireIvyEvent(
+ new StartResolveDependencyEvent(resolver, dependencyDescriptor,
+ requestedRevisionId));
+ module = resolver.getDependency(dependencyDescriptor, data);
+ data.getEventManager().fireIvyEvent(
+ new EndResolveDependencyEvent(resolver, dependencyDescriptor,
+ requestedRevisionId, module, System.currentTimeMillis() - start));
+
+ if (module != null) {
+ module.getResolver()
+ .getRepositoryCacheManager()
+ .saveResolvers(module.getDescriptor(),
+ module.getResolver().getName(),
+ module.getArtifactResolver().getName());
+ if (settings.logModuleWhenFound()
+ && LogOptions.LOG_DEFAULT.equals(getData().getOptions().getLog())) {
+ Message.info("\tfound " + module.getId() + " in "
+ + module.getResolver().getName());
+ } else {
+ Message.verbose("\tfound " + module.getId() + " in "
+ + module.getResolver().getName());
+ }
+
+ // IVY-56: check if revision has actually been resolved
+ if (settings.getVersionMatcher().isDynamic(getId())
+ && settings.getVersionMatcher().isDynamic(module.getId())) {
+ Message.error("impossible to resolve dynamic revision for " + getId()
+ + ": check your configuration and "
+ + "make sure revision is part of your pattern");
+ problem = new RuntimeException("impossible to resolve dynamic revision");
+ return false;
+ }
+ if (!getId().equals(module.getId())) {
+ IvyNode resolved = data.getNode(module.getId());
+ if (resolved != null) {
+ // found revision has already been resolved
+ // => update it and discard this node
+ md = module.getDescriptor(); // needed for handleConfiguration
+ if (!handleConfiguration(loaded, rootModuleConf, parent,
+ parentConf, conf, shouldBePublic, usage)) {
+ return false;
+ }
+
+ moveToRealNode(rootModuleConf, parent, parentConf, conf,
+ shouldBePublic, resolved);
+
+ return true;
+ }
+ String log = "\t[" + module.getId().getRevision() + "] " + getId();
+ if (!settings.getVersionMatcher().isDynamic(getId())) {
+ log += " (forced)";
+ }
+ if (settings.logResolvedRevision()
+ && LogOptions.LOG_DEFAULT.equals(getData().getOptions()
+ .getLog())) {
+ Message.info(log);
+ } else {
+ Message.verbose(log);
+ }
+ }
+ downloaded = module.getReport().isDownloaded();
+ searched = module.getReport().isSearched();
+ loaded = true;
+
+ md = module.getDescriptor();
+ confsToFetch.remove("*");
+ updateConfsToFetch(Arrays
+ .asList(resolveSpecialConfigurations(getRequiredConfigurations(
+ parent, parentConf))));
+ } else {
+ Message.warn("\tmodule not found: " + getId());
+ resolver.reportFailure();
+ problem = new RuntimeException("not found");
+ return false;
+ }
+ } catch (ResolveProcessException e) {
+ throw e;
+ } catch (Exception e) {
+ problem = e;
+ Message.debug("Unexpected error: " + problem.getMessage(), problem);
+ return false;
+ }
+ } else {
+ loaded = true;
+ }
+ }
+ handleConfiguration(loaded, rootModuleConf, parent, parentConf, conf, shouldBePublic, usage);
+ if (hasProblem()) {
+ Message.debug("problem : " + problem.getMessage());
+ return false;
+ } else {
+ DependencyDescriptor dd = getDependencyDescriptor(parent);
+ if (dd != null) {
+ usage.addUsage(rootModuleConf, dd, parentConf);
+ }
+ return loaded;
+ }
+ }
+
+ private void moveToRealNode(String rootModuleConf, IvyNode parent, String parentConf,
+ String conf, boolean shouldBePublic, IvyNode resolved) {
+ if (resolved.md == null) {
+ resolved.md = md;
+ }
+ if (resolved.module == null) {
+ resolved.module = module;
+ }
+ resolved.downloaded |= module.getReport().isDownloaded();
+ resolved.searched |= module.getReport().isSearched();
+ resolved.dds.putAll(dds);
+ resolved.updateDataFrom(this, rootModuleConf, true);
+ resolved.loadData(rootModuleConf, parent, parentConf, conf, shouldBePublic, usage);
+ resolved.usage.updateDataFrom(getAllUsages(), rootModuleConf);
+ usage = resolved.usage;
+
+ data.replaceNode(getId(), resolved, rootModuleConf); // this actually discards the node
+
+ if (settings.logResolvedRevision()
+ && LogOptions.LOG_DEFAULT.equals(getData().getOptions().getLog())) {
+ Message.info("\t[" + module.getId().getRevision() + "] " + getId());
+ } else {
+ Message.verbose("\t[" + module.getId().getRevision() + "] " + getId());
+ }
+ }
+
+ public Collection getDependencies(String rootModuleConf, String[] confs, String requestedConf) {
+ if (md == null) {
+ throw new IllegalStateException(
+ "impossible to get dependencies when data has not been loaded");
+ }
+ if (Arrays.asList(confs).contains("*")) {
+ if (isRoot()) {
+ confs = md.getConfigurationsNames();
+ } else {
+ confs = md.getPublicConfigurationsNames();
+ }
+ }
+ Collection deps = new HashSet();
+ for (int i = 0; i < confs.length; i++) {
+ deps.addAll(getDependencies(rootModuleConf, confs[i], requestedConf));
+ }
+ return deps;
+ }
+
+ /**
+ * Load the dependencies of the current node
+ * <p>
+ * The resulting collection of nodes may have some configuration to load
+ *
+ * @param rootModuleConf
+ * the requested configuration of the root module
+ * @param conf
+ * the configuration to load of this node
+ * @param requestedConf
+ * the actual node conf requested, possibly extending the <code>conf</code> one.
+ * @return
+ */
+ public Collection/* <IvyNode> */getDependencies(String rootModuleConf, String conf,
+ String requestedConf) {
+ if (md == null) {
+ throw new IllegalStateException(
+ "impossible to get dependencies when data has not been loaded");
+ }
+ DependencyDescriptor[] dds = md.getDependencies();
+ Map/* <ModuleRevisionId, IvyNode> */dependencies = new LinkedHashMap(); // it's important to
+ // respect order
+ for (int i = 0; i < dds.length; i++) {
+ DependencyDescriptor dd = data.mediate(dds[i]);
+ String[] dependencyConfigurations = dd.getDependencyConfigurations(conf, requestedConf);
+ if (dependencyConfigurations.length == 0) {
+ // no configuration of the dependency is required for current confs :
+ // it is exactly the same as if there was no dependency at all on it
+ continue;
+ }
+ ModuleRevisionId requestedDependencyRevisionId = dd.getDependencyRevisionId();
+ if (isDependencyModuleExcluded(dd, rootModuleConf, requestedDependencyRevisionId, conf)) {
+ // the whole module is excluded, it is considered as not being part of dependencies
+ // at all
+ Message.verbose("excluding " + dd + " in " + conf);
+ continue;
+ }
+
+ // check if not already loaded here
+ IvyNode depNode = (IvyNode) dependencies.get(requestedDependencyRevisionId);
+ if (depNode == null) {
+ // check if not already loaded during the resolve session
+ depNode = data.getNode(requestedDependencyRevisionId);
+ }
+
+ if (depNode == null) {
+ depNode = new IvyNode(data, this, dd);
+ } else {
+ depNode.addDependencyDescriptor(this, dd);
+ if (depNode.hasProblem()) {
+ // dependency already tried to be resolved, but unsuccessfully
+ // nothing special to do
+ }
+
+ }
+ String[] confsArray = depNode.resolveSpecialConfigurations(dependencyConfigurations);
+ Collection confs = Arrays.asList(confsArray);
+ depNode.updateConfsToFetch(confs);
+ depNode.addRootModuleConfigurations(depNode.usage, rootModuleConf, confsArray);
+ depNode.usage.setRequiredConfs(this, conf, confs);
+
+ depNode.addCaller(rootModuleConf, this, conf, requestedConf, dependencyConfigurations,
+ dd);
+ dependencies.put(requestedDependencyRevisionId, depNode);
+ }
+ return dependencies.values();
+ }
+
+ private void addDependencyDescriptor(IvyNode parent, DependencyDescriptor dd) {
+ dds.put(parent, dd);
+ }
+
+ public DependencyDescriptor getDependencyDescriptor(IvyNode parent) {
+ return (DependencyDescriptor) dds.get(parent);
+ }
+
+ private boolean isDependencyModuleExcluded(DependencyDescriptor dd, String rootModuleConf,
+ ModuleRevisionId dependencyRevisionId, String conf) {
+ Artifact a = DefaultArtifact.newIvyArtifact(dependencyRevisionId, null);
+ if (isRoot()) {
+ // no callers, but maybe some exclude
+ Boolean exclude = doesExclude(md, rootModuleConf, new String[] {rootModuleConf}, dd, a,
+ new Stack());
+ return exclude == null ? false : exclude.booleanValue();
+ }
+ return callers.doesCallersExclude(rootModuleConf, a);
+ }
+
+ Boolean doesExclude(ModuleDescriptor md, String rootModuleConf, String[] moduleConfs,
+ DependencyDescriptor dd, Artifact artifact, Stack callersStack) {
+ // artifact is excluded if it match any of the exclude pattern for this dependency...
+ if (dd != null) {
+ if (dd.doesExclude(moduleConfs, artifact.getId().getArtifactId())) {
+ return Boolean.TRUE;
+ }
+ }
+ if (md.doesExclude(moduleConfs, artifact.getId().getArtifactId())) {
+ return Boolean.TRUE;
+ }
+ // ... or if it is excluded by all its callers
+ IvyNode c = getData().getNode(md.getModuleRevisionId());
+ if (c != null) {
+ if (callersStack.contains(c.getId())) {
+ // a circular dependency, we cannot be conclusive here
+ return null;
+ }
+ return Boolean.valueOf(c.doesCallersExclude(rootModuleConf, artifact, callersStack));
+ } else {
+ return Boolean.FALSE;
+ }
+ }
+
+ public boolean hasConfigurationsToLoad() {
+ return !confsToFetch.isEmpty();
+ }
+
+ private boolean markRootModuleConfLoaded(String rootModuleConf) {
+ return loadedRootModuleConfs.add(rootModuleConf);
+ }
+
+ private boolean isRootModuleConfLoaded(String rootModuleConf) {
+ return loadedRootModuleConfs.contains(rootModuleConf);
+ }
+
+ private boolean handleConfiguration(boolean loaded, String rootModuleConf, IvyNode parent,
+ String parentConf, String conf, boolean shouldBePublic, IvyNodeUsage usage) {
+ if (md != null) {
+ String[] confs = getRealConfs(conf);
+ addRootModuleConfigurations(usage, rootModuleConf, confs);
+ for (int i = 0; i < confs.length; i++) {
+ Configuration c = md.getConfiguration(confs[i]);
+ if (c == null) {
+ confsToFetch.remove(conf);
+ if (isConfRequiredByMergedUsageOnly(rootModuleConf, conf)) {
+ Message.verbose("configuration required by evicted revision is not available in "
+ + "selected revision. skipping " + conf + " in " + this);
+ } else if (!conf.equals(confs[i])) {
+ problem = new RuntimeException("configuration not found in " + this + ": '"
+ + conf + "'. Missing configuration: '" + confs[i]
+ + "'. It was required from " + parent + " " + parentConf);
+ } else {
+ problem = new RuntimeException("configuration not found in " + this + ": '"
+ + confs[i] + "'. It was required from " + parent + " " + parentConf);
+ }
+ return false;
+ } else if (shouldBePublic && !isRoot()
+ && c.getVisibility() != Configuration.Visibility.PUBLIC) {
+ confsToFetch.remove(conf);
+ if (isConfRequiredByMergedUsageOnly(rootModuleConf, conf)) {
+ Message.verbose("configuration required by evicted revision is not visible in "
+ + "selected revision. skipping " + conf + " in " + this);
+ } else {
+ problem = new RuntimeException("configuration not public in " + this
+ + ": '" + c + "'. It was required from " + parent + " "
+ + parentConf);
+ }
+ return false;
+ }
+ }
+ if (loaded) {
+ fetchedConfigurations.add(conf);
+ confsToFetch.removeAll(Arrays.asList(confs));
+ confsToFetch.remove(conf);
+ }
+ }
+ return true;
+ }
+
+ private String getDefaultConf(String conf) {
+ Matcher m = FALLBACK_CONF_PATTERN.matcher(conf);
+ if (m.matches()) {
+ return m.group(2);
+ } else {
+ return conf;
+ }
+ }
+
+ private String getMainConf(String conf) {
+ Matcher m = FALLBACK_CONF_PATTERN.matcher(conf);
+ if (m.matches()) {
+ return m.group(1);
+ } else {
+ return null;
+ }
+ }
+
+ public void updateConfsToFetch(Collection confs) {
+ confsToFetch.addAll(confs);
+ confsToFetch.removeAll(fetchedConfigurations);
+ }
+
+ /**
+ * resolve the '*' special configurations if necessary and possible
+ */
+ private String[] resolveSpecialConfigurations(String[] dependencyConfigurations) {
+ if (dependencyConfigurations.length == 1 && dependencyConfigurations[0].startsWith("*")
+ && isLoaded()) {
+ String conf = dependencyConfigurations[0];
+ if ("*".equals(conf)) {
+ return getDescriptor().getPublicConfigurationsNames();
+ }
+ // there are exclusions in the configuration
+ List exclusions = Arrays.asList(conf.substring(2).split("\\!"));
+
+ List ret = new ArrayList(Arrays.asList(getDescriptor().getPublicConfigurationsNames()));
+ ret.removeAll(exclusions);
+
+ return (String[]) ret.toArray(new String[ret.size()]);
+ }
+ return dependencyConfigurations;
+ }
+
+ /**
+ * returns the required configurations from the given node
+ *
+ * @param in
+ * @return
+ */
+ public String[] getRequiredConfigurations(IvyNode in, String inConf) {
+ Collection req = new LinkedHashSet();
+ addAllIfNotNull(req, usage.getRequiredConfigurations(in, inConf));
+ for (Iterator iterator = mergedUsages.values().iterator(); iterator.hasNext();) {
+ IvyNodeUsage usage = (IvyNodeUsage) iterator.next();
+ addAllIfNotNull(req, usage.getRequiredConfigurations(in, inConf));
+ }
+ return req == null ? new String[0] : (String[]) req.toArray(new String[req.size()]);
+ }
+
+ private void addAllIfNotNull(Collection into, Collection col) {
+ if (col != null) {
+ into.addAll(col);
+ }
+ }
+
+ /**
+ * returns all the current required configurations of the node
+ *
+ * @return
+ */
+ public String[] getRequiredConfigurations() {
+ Collection required = new ArrayList(confsToFetch.size() + fetchedConfigurations.size());
+ required.addAll(fetchedConfigurations);
+ required.addAll(confsToFetch);
+ return (String[]) required.toArray(new String[required.size()]);
+ }
+
+ public Configuration getConfiguration(String conf) {
+ if (md == null) {
+ throw new IllegalStateException(
+ "impossible to get configuration when data has not been loaded");
+ }
+ String defaultConf = getDefaultConf(conf);
+ conf = getMainConf(conf);
+ Configuration configuration = md.getConfiguration(conf);
+ if (configuration == null) {
+ configuration = md.getConfiguration(defaultConf);
+ }
+ return configuration;
+ }
+
+ /**
+ * Returns the configurations of the dependency required in a given root module configuration.
+ *
+ * @param rootModuleConf
+ * @return
+ */
+ public String[] getConfigurations(String rootModuleConf) {
+ Set depConfs = new LinkedHashSet();
+ addAllIfNotNull(depConfs, usage.getConfigurations(rootModuleConf));
+ for (Iterator iterator = mergedUsages.values().iterator(); iterator.hasNext();) {
+ IvyNodeUsage usage = (IvyNodeUsage) iterator.next();
+ addAllIfNotNull(depConfs, usage.getConfigurations(rootModuleConf));
+ }
+ return (String[]) depConfs.toArray(new String[depConfs.size()]);
+ }
+
+ protected boolean isConfRequiredByMergedUsageOnly(String rootModuleConf, String conf) {
+ Set confs = usage.getConfigurations(rootModuleConf);
+ return confs == null || !confs.contains(conf);
+ }
+
+ // This is never called. Could we remove it?
+ public void discardConf(String rootModuleConf, String conf) {
+ Set depConfs = usage.addAndGetConfigurations(rootModuleConf);
+ if (md != null) {
+ // remove all given dependency configurations to the set + extended ones
+ Configuration c = md.getConfiguration(conf);
+ if (conf != null) {
+ String[] exts = c.getExtends();
+ for (int i = 0; i < exts.length; i++) {
+ discardConf(rootModuleConf, exts[i]); // recursive remove of extended
+ // configurations
+ }
+ depConfs.remove(c.getName());
+ } else {
+ Message.warn("unknown configuration in " + getId() + ": " + conf);
+ }
+ } else {
+ depConfs.remove(conf);
+ }
+ }
+
+ private void addRootModuleConfigurations(IvyNodeUsage usage, String rootModuleConf,
+ String[] dependencyConfs) {
+ Set depConfs = usage.addAndGetConfigurations(rootModuleConf);
+ if (md != null) {
+ // add all given dependency configurations to the set + extended ones
+ for (int i = 0; i < dependencyConfs.length; i++) {
+ depConfs.add(dependencyConfs[i]);
+ Configuration conf = md.getConfiguration(dependencyConfs[i]);
+ if (conf != null) {
+ String[] exts = conf.getExtends();
+ // recursive add of extended
+ addRootModuleConfigurations(usage, rootModuleConf, exts);
+ }
+ }
+ } else {
+ for (int i = 0; i < dependencyConfs.length; i++) {
+ depConfs.add(dependencyConfs[i]);
+ }
+ }
+ }
+
+ /**
+ * Returns the root module configurations in which this dependency is required
+ *
+ * @return
+ */
+ public String[] getRootModuleConfigurations() {
+ Set confs = getRootModuleConfigurationsSet();
+ return (String[]) confs.toArray(new String[confs.size()]);
+ }
+
+ /**
+ * Returns the root module configurations in which this dependency is required
+ *
+ * @return
+ */
+ public Set getRootModuleConfigurationsSet() {
+ Set confs = new LinkedHashSet();
+ addAllIfNotNull(confs, usage.getRootModuleConfigurations());
+ for (Iterator iterator = mergedUsages.values().iterator(); iterator.hasNext();) {
+ IvyNodeUsage usage = (IvyNodeUsage) iterator.next();
+ addAllIfNotNull(confs, usage.getRootModuleConfigurations());
+ }
+ return confs;
+ }
+
+ public String[] getConfsToFetch() {
+ return (String[]) confsToFetch.toArray(new String[confsToFetch.size()]);
+ }
+
+ public String[] getRealConfs(String conf) {
+ if (md == null) {
+ return new String[] {conf};
+ }
+ String defaultConf = getDefaultConf(conf);
+ conf = getMainConf(conf);
+ if ((md.getConfiguration(conf) == null)
+ || Configuration.Visibility.PRIVATE.equals(md.getConfiguration(conf)
+ .getVisibility())) {
+ if ("".equals(defaultConf)) {
+ return new String[0];
+ }
+ conf = defaultConf;
+ }
+ if (conf.startsWith("*")) {
+ return resolveSpecialConfigurations(new String[] {conf});
+ } else if (conf.indexOf(',') != -1) {
+ String[] confs = conf.split(",");
+ for (int i = 0; i < confs.length; i++) {
+ confs[i] = confs[i].trim();
+ }
+ }
+ return new String[] {conf};
+
+ }
+
+ /**
+ * Finds and returns a path in callers from the given module id to the current node
+ *
+ * @param from
+ * the module id to start the path from
+ * @return a collection representing the path, starting with the from node, followed by the list
+ * of nodes being one path to the current node, excluded
+ */
+ private Collection findPath(ModuleId from) {
+ return findPath(from, this, new LinkedList());
+ }
+
+ private Collection findPath(ModuleId from, IvyNode node, List path) {
+ IvyNode parent = node.getDirectCallerFor(from);
+ if (parent == null) {
+ throw new IllegalArgumentException("no path from " + from + " to " + getId() + " found");
+ }
+ if (path.contains(parent)) {
+ path.add(0, parent);
+ Message.verbose("circular dependency found while looking for the path for another one: "
+ + "was looking for " + from + " as a caller of " + path.get(path.size() - 1));
+ return path;
+ }
+ path.add(0, parent);
+ if (parent.getId().getModuleId().equals(from)) {
+ return path;
+ }
+ return findPath(from, parent, path);
+ }
+
+ /**
+ * Update data in this node from data of the given node, for the given root module
+ * configuration.
+ *
+ * @param node
+ * the source node from which data should be copied
+ * @param rootModuleConf
+ * the root module configuration for which data should be updated
+ * @param real
+ * true if the node to update from actually corresponds to the same real node
+ * (usually updated because of dynamic revision resolution), false if it's not the
+ * same real node (usually updated because of node eviction)
+ */
+ private void updateDataFrom(IvyNode node, String rootModuleConf, boolean real) {
+ // update callers
+ callers.updateFrom(node.callers, rootModuleConf, real);
+
+ if (real) {
+ usage.updateDataFrom(node.getAllUsages(), rootModuleConf);
+ } else {
+ // let's copy usage information for the given rootModuleConf, into a separate usage
+ // object to keep detailed data about where usage comes from
+ IvyNodeUsage mergedUsage = (IvyNodeUsage) mergedUsages.get(node.getId());
+ if (mergedUsage == null) {
+ mergedUsage = new IvyNodeUsage(node);
+ mergedUsages.put(node.getId(), mergedUsage);
+ }
+ mergedUsage.updateDataFrom(node.getAllUsages(), rootModuleConf);
+ }
+
+ // update confsToFetch
+ updateConfsToFetch(node.fetchedConfigurations);
+ updateConfsToFetch(node.confsToFetch);
+ }
+
+ private Collection/* <IvyNodeUsage> */getAllUsages() {
+ Collection usages = new ArrayList();
+ usages.add(usage);
+ usages.addAll(mergedUsages.values());
+ return usages;
+ }
+
+ /**
+ * Returns all the artifacts of this dependency required in all the root module configurations
+ *
+ * @return
+ */
+ public Artifact[] getAllArtifacts() {
+ Set ret = new HashSet();
+
+ for (Iterator it = getRootModuleConfigurationsSet().iterator(); it.hasNext();) {
+ String rootModuleConf = (String) it.next();
+ ret.addAll(Arrays.asList(getArtifacts(rootModuleConf)));
+ }
+ return (Artifact[]) ret.toArray(new Artifact[ret.size()]);
+ }
+
+ /**
+ * Returns all the artifacts of this dependency required in the root module configurations in
+ * which the node is not evicted nor blacklisted
+ *
+ * @param artifactFilter
+ * @return
+ */
+ public Artifact[] getSelectedArtifacts(Filter artifactFilter) {
+ Collection ret = new HashSet();
+ for (Iterator it = getRootModuleConfigurationsSet().iterator(); it.hasNext();) {
+ String rootModuleConf = (String) it.next();
+ if (!isEvicted(rootModuleConf) && !isBlacklisted(rootModuleConf)) {
+ ret.addAll(Arrays.asList(getArtifacts(rootModuleConf)));
+ }
+ }
+ ret = FilterHelper.filter(ret, artifactFilter);
+ return (Artifact[]) ret.toArray(new Artifact[ret.size()]);
+ }
+
+ /**
+ * Returns the artifacts of this dependency required in the configurations themselves required
+ * in the given root module configuration
+ *
+ * @param rootModuleConf
+ * @return
+ */
+ public Artifact[] getArtifacts(String rootModuleConf) {
+ // first we look for the dependency configurations required
+ // in the given root module configuration
+ String[] confs = getConfigurations(rootModuleConf);
+ if (confs == null || confs.length == 0) {
+ // no configuration required => no artifact required
+ return new Artifact[0];
+ }
+ if (md == null) {
+ throw new IllegalStateException(
+ "impossible to get artifacts when data has not been loaded. IvyNode = "
+ + this.toString());
+ }
+
+ Set artifacts = new HashSet(); // the set we fill before returning
+
+ // we check if we have dependencyArtifacts includes description for this rootModuleConf
+ Set dependencyArtifacts = usage.getDependencyArtifactsSet(rootModuleConf);
+
+ if (md.isDefault() && dependencyArtifacts != null && !dependencyArtifacts.isEmpty()) {
+ addArtifactsFromOwnUsage(artifacts, dependencyArtifacts);
+ addArtifactsFromMergedUsage(rootModuleConf, artifacts);
+ } else {
+ Set includes = new LinkedHashSet();
+ addAllIfNotNull(includes, usage.getDependencyIncludesSet(rootModuleConf));
+ for (Iterator iterator = mergedUsages.values().iterator(); iterator.hasNext();) {
+ IvyNodeUsage usage = (IvyNodeUsage) iterator.next();
+ addAllIfNotNull(includes, usage.getDependencyIncludesSet(rootModuleConf));
+ }
+
+ if ((dependencyArtifacts == null || dependencyArtifacts.isEmpty())
+ && (includes.isEmpty())) {
+ // no artifacts / includes: we get all artifacts as defined by the descriptor
+ for (int i = 0; i < confs.length; i++) {
+ artifacts.addAll(Arrays.asList(md.getArtifacts(confs[i])));
+ }
+ } else {
+ // we have to get only artifacts listed as "includes"
+
+ // first we get all artifacts as defined by the module descriptor
+ // and classify them by artifact id
+ Map allArtifacts = new HashMap();
+ for (int i = 0; i < confs.length; i++) {
+ Artifact[] arts = md.getArtifacts(confs[i]);
+ for (int j = 0; j < arts.length; j++) {
+ allArtifacts.put(arts[j].getId().getArtifactId(), arts[j]);
+ }
+ }
+
+ // now we add caller defined ones
+ if (dependencyArtifacts != null) {
+ addArtifactsFromOwnUsage(artifacts, dependencyArtifacts);
+ }
+ addArtifactsFromMergedUsage(rootModuleConf, artifacts);
+
+ // and now we filter according to include rules
+ for (Iterator it = includes.iterator(); it.hasNext();) {
+ IncludeRule dad = (IncludeRule) it.next();
+ Collection arts = findArtifactsMatching(dad, allArtifacts);
+ if (arts.isEmpty()) {
+ Message.error("a required artifact is not listed by module descriptor: "
+ + dad.getId());
+ // we remove it from required list to prevent message to be displayed more
+ // than once
+ it.remove();
+ } else {
+ Message.debug(this + " in " + rootModuleConf + ": including " + arts);
+ artifacts.addAll(arts);
+ }
+ }
+ }
+ }
+
+ // now excludes artifacts that aren't accepted by any caller
+ for (Iterator iter = artifacts.iterator(); iter.hasNext();) {
+ Artifact artifact = (Artifact) iter.next();
+ boolean excluded = callers.doesCallersExclude(rootModuleConf, artifact);
+ if (excluded) {
+ Message.debug(this + " in " + rootModuleConf + ": excluding " + artifact);
+ iter.remove();
+ }
+ }
+ return (Artifact[]) artifacts.toArray(new Artifact[artifacts.size()]);
+ }
+
+ private void addArtifactsFromOwnUsage(Set artifacts, Set dependencyArtifacts) {
+ for (Iterator it = dependencyArtifacts.iterator(); it.hasNext();) {
+ DependencyArtifactDescriptor dad = (DependencyArtifactDescriptor) it.next();
+ artifacts.add(new MDArtifact(md, dad.getName(), dad.getType(), dad.getExt(), dad
+ .getUrl(), dad.getQualifiedExtraAttributes()));
+ }
+ }
+
+ private void addArtifactsFromMergedUsage(String rootModuleConf, Set artifacts) {
+ for (Iterator iterator = mergedUsages.values().iterator(); iterator.hasNext();) {
+ IvyNodeUsage usage = (IvyNodeUsage) iterator.next();
+ Set mergedDependencyArtifacts = usage.getDependencyArtifactsSet(rootModuleConf);
+ if (mergedDependencyArtifacts != null) {
+ for (Iterator it = mergedDependencyArtifacts.iterator(); it.hasNext();) {
+ DependencyArtifactDescriptor dad = (DependencyArtifactDescriptor) it.next();
+ Map extraAttributes = new HashMap(dad.getQualifiedExtraAttributes());
+ MDArtifact artifact = new MDArtifact(md, dad.getName(), dad.getType(),
+ dad.getExt(), dad.getUrl(), extraAttributes);
+
+ if (!artifacts.contains(artifact)) {
+ // this is later used to know that this is a merged artifact
+ extraAttributes.put("ivy:merged", dad.getDependencyDescriptor()
+ .getParentRevisionId() + " -> " + usage.getNode().getId());
+ artifacts.add(artifact);
+ }
+ }
+ }
+ }
+ }
+
+ private static Collection findArtifactsMatching(IncludeRule rule, Map allArtifacts) {
+ Collection ret = new ArrayList();
+ for (Iterator iter = allArtifacts.keySet().iterator(); iter.hasNext();) {
+ ArtifactId aid = (ArtifactId) iter.next();
+ if (MatcherHelper.matches(rule.getMatcher(), rule.getId(), aid)) {
+ ret.add(allArtifacts.get(aid));
+ }
+ }
+ return ret;
+ }
+
+ public boolean hasProblem() {
+ return problem != null;
+ }
+
+ public Exception getProblem() {
+ return problem;
+ }
+
+ public String getProblemMessage() {
+ return StringUtils.getErrorMessage(problem);
+ }
+
+ public boolean isDownloaded() {
+ return downloaded;
+ }
+
+ public boolean isSearched() {
+ return searched;
+ }
+
+ public boolean isLoaded() {
+ return md != null;
+ }
+
+ public boolean isFetched(String conf) {
+ return fetchedConfigurations.contains(conf);
+ }
+
+ public IvyNode findNode(ModuleRevisionId mrid) {
+ return data.getNode(mrid);
+ }
+
+ boolean isRoot() {
+ return root == this;
+ }
+
+ public IvyNode getRoot() {
+ return root;
+ }
+
+ public ConflictManager getConflictManager(ModuleId mid) {
+ if (md == null) {
+ throw new IllegalStateException(
+ "impossible to get conflict manager when data has not been loaded. IvyNode = "
+ + this.toString());
+ }
+ ConflictManager cm = md.getConflictManager(mid);
+ return cm == null ? settings.getConflictManager(mid) : cm;
+ }
+
+ public IvyNode getRealNode() {
+ IvyNode real = data.getNode(getId());
+ return real != null ? real : this;
+ }
+
+ public ModuleRevisionId getId() {
+ return id;
+ }
+
+ public ModuleId getModuleId() {
+ return id.getModuleId();
+ }
+
+ public ModuleDescriptor getDescriptor() {
+ return md;
+ }
+
+ public ResolveData getData() {
+ return data;
+ }
+
+ public ResolvedModuleRevision getModuleRevision() {
+ return module;
+ }
+
+ public long getPublication() {
+ if (module != null) {
+ return module.getPublicationDate().getTime();
+ }
+ return 0;
+ }
+
+ /**
+ * Returns the last modified timestamp of the module represented by this Node, or 0 if the last
+ * modified timestamp is currently unkwown (module not loaded)
+ *
+ * @return the last modified timestamp of the module represented by this Node
+ */
+ public long getLastModified() {
+ if (md != null) {
+ return md.getLastModified();
+ }
+ return 0;
+ }
+
+ public ModuleRevisionId getResolvedId() {
+ if (md != null && md.getResolvedModuleRevisionId().getRevision() != null) {
+ return md.getResolvedModuleRevisionId();
+ } else if (module != null) {
+ return module.getId();
+ } else {
+ return getId();
+ }
+ }
+
+ /**
+ * Clean data related to one root module configuration only
+ */
+ public void clean() {
+ confsToFetch.clear();
+ }
+
+ // /////////////////////////////////////////////////////////////////////////////
+ // CALLERS MANAGEMENT
+ // /////////////////////////////////////////////////////////////////////////////
+
+ boolean canExclude(String rootModuleConf) {
+ Caller[] callers = getCallers(rootModuleConf);
+ for (int i = 0; i < callers.length; i++) {
+ if (callers[i].canExclude()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private IvyNode getDirectCallerFor(ModuleId from) {
+ return callers.getDirectCallerFor(from);
+ }
+
+ public Caller[] getCallers(String rootModuleConf) {
+ return callers.getCallers(rootModuleConf);
+ }
+
+ public Collection getAllCallersModuleIds() {
+ return callers.getAllCallersModuleIds();
+ }
+
+ public Caller[] getAllCallers() {
+ return callers.getAllCallers();
+ }
+
+ public Caller[] getAllRealCallers() {
+ return callers.getAllRealCallers();
+ }
+
+ public void addCaller(String rootModuleConf, IvyNode callerNode, String callerConf,
+ String requestedConf, String[] dependencyConfs, DependencyDescriptor dd) {
+ callers.addCaller(rootModuleConf, callerNode, callerConf, requestedConf, dependencyConfs,
+ dd);
+ boolean isCircular = callers.getAllCallersModuleIds().contains(getId().getModuleId());
+ if (isCircular) {
+ IvyContext.getContext().getCircularDependencyStrategy()
+ .handleCircularDependency(toMrids(findPath(getId().getModuleId()), this));
+ }
+ }
+
+ public boolean doesCallersExclude(String rootModuleConf, Artifact artifact, Stack callersStack) {
+ return callers.doesCallersExclude(rootModuleConf, artifact, callersStack);
+ }
+
+ private ModuleRevisionId[] toMrids(Collection path, IvyNode depNode) {
+ ModuleRevisionId[] ret = new ModuleRevisionId[path.size() + 1];
+ int i = 0;
+ for (Iterator iter = path.iterator(); iter.hasNext(); i++) {
+ IvyNode node = (IvyNode) iter.next();
+ ret[i] = node.getId();
+ }
+ ret[ret.length - 1] = depNode.getId();
+ return ret;
+ }
+
+ // /////////////////////////////////////////////////////////////////////////////
+ // EVICTION MANAGEMENT
+ // /////////////////////////////////////////////////////////////////////////////
+
+ /** A copy of the set of resolved nodes (real nodes) */
+ public Set getResolvedNodes(ModuleId moduleId, String rootModuleConf) {
+ return eviction.getResolvedNodes(moduleId, rootModuleConf);
+ }
+
+ public Collection getResolvedRevisions(ModuleId moduleId, String rootModuleConf) {
+ return eviction.getResolvedRevisions(moduleId, rootModuleConf);
+ }
+
+ public void markEvicted(EvictionData evictionData) {
+ eviction.markEvicted(evictionData);
+ String rootModuleConf = evictionData.getRootModuleConf();
+
+ // bug 105: update selected data with evicted one
+ if (evictionData.getSelected() != null) {
+ for (Iterator iter = evictionData.getSelected().iterator(); iter.hasNext();) {
+ IvyNode selected = (IvyNode) iter.next();
+ selected.updateDataFrom(this, rootModuleConf, false);
+ }
+ }
+ }
+
+ public Collection getAllEvictingConflictManagers() {
+ return eviction.getAllEvictingConflictManagers();
+ }
+
+ public Collection getAllEvictingNodes() {
+ return eviction.getAllEvictingNodes();
+ }
+
+ public Collection/* <String> */getAllEvictingNodesDetails() {
+ return eviction.getAllEvictingNodesDetails();
+ }
+
+ public String[] getEvictedConfs() {
+ return eviction.getEvictedConfs();
+ }
+
+ public EvictionData getEvictedData(String rootModuleConf) {
+ return eviction.getEvictedData(rootModuleConf);
+ }
+
+ public Collection getEvictedNodes(ModuleId mid, String rootModuleConf) {
+ return eviction.getEvictedNodes(mid, rootModuleConf);
+ }
+
+ public Collection getEvictedRevisions(ModuleId mid, String rootModuleConf) {
+ return eviction.getEvictedRevisions(mid, rootModuleConf);
+ }
+
+ public EvictionData getEvictionDataInRoot(String rootModuleConf, IvyNode ancestor) {
+ return eviction.getEvictionDataInRoot(rootModuleConf, ancestor);
+ }
+
+ public boolean isCompletelyEvicted() {
+ return eviction.isCompletelyEvicted();
+ }
+
+ public boolean isEvicted(String rootModuleConf) {
+ return eviction.isEvicted(rootModuleConf);
+ }
+
+ public void markEvicted(String rootModuleConf, IvyNode node, ConflictManager conflictManager,
+ Collection resolved) {
+ EvictionData evictionData = new EvictionData(rootModuleConf, node, conflictManager,
+ resolved);
+ markEvicted(evictionData);
+ }
+
+ public void setEvictedNodes(ModuleId moduleId, String rootModuleConf, Collection evicted) {
+ eviction.setEvictedNodes(moduleId, rootModuleConf, evicted);
+ }
+
+ public void setResolvedNodes(ModuleId moduleId, String rootModuleConf, Collection resolved) {
+ eviction.setResolvedNodes(moduleId, rootModuleConf, resolved);
+ }
+
+ public String toString() {
+ return getResolvedId().toString();
+ }
+
+ public boolean equals(Object obj) {
+ if (!(obj instanceof IvyNode)) {
+ return false;
+ }
+ IvyNode node = (IvyNode) obj;
+ return node.getId().equals(getId());
+ }
+
+ public int compareTo(Object obj) {
+ IvyNode that = (IvyNode) obj;
+ return this.getModuleId().compareTo(that.getModuleId());
+ }
+
+ public int hashCode() {
+ return getId().hashCode();
+ }
+
+ /**
+ * Returns a collection of Nodes in conflict for which conflict has been detected but conflict
+ * resolution hasn't been done yet
+ *
+ * @param rootModuleConf
+ * @param mid
+ * the module id for which pending conflicts should be found
+ * @return a Collection of IvyNode in pending conflict
+ */
+ public Collection getPendingConflicts(String rootModuleConf, ModuleId mid) {
+ return eviction.getPendingConflicts(rootModuleConf, mid);
+ }
+
+ public void setPendingConflicts(ModuleId moduleId, String rootModuleConf, Collection conflicts) {
+ eviction.setPendingConflicts(moduleId, rootModuleConf, conflicts);
+ }
+
+ // /////////////////////////////////////////////////////////////////////////////
+ // BLACKLISTING MANAGEMENT
+ // /////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Blacklists the current node, so that a new resolve process won't ever consider this node as
+ * available in the repository.
+ * <p>
+ * This is useful in combination with {@link RestartResolveProcess} for conflict manager
+ * implementation which use a best effort strategy to find compatible dependency set, like
+ * {@link LatestCompatibleConflictManager}
+ * </p>
+ *
+ * @param rootModuleConf
+ * the root module configuration in which the node should be blacklisted
+ */
+ public void blacklist(IvyNodeBlacklist bdata) {
+ if (data.getSettings().logResolvedRevision()) {
+ Message.info("BLACKLISTING " + bdata);
+ } else {
+ Message.verbose("BLACKLISTING " + bdata);
+ }
+
+ Stack callerStack = new Stack();
+ callerStack.push(this);
+ clearEvictionDataInAllCallers(bdata.getRootModuleConf(), callerStack);
+
+ usage.blacklist(bdata);
+ data.blacklist(this);
+ }
+
+ private void clearEvictionDataInAllCallers(String rootModuleConf,
+ Stack/* <IvyNode> */callerStack) {
+ IvyNode node = (IvyNode) callerStack.peek();
+ Caller[] callers = node.getCallers(rootModuleConf);
+ for (int i = 0; i < callers.length; i++) {
+ IvyNode callerNode = findNode(callers[i].getModuleRevisionId());
+ if (callerNode != null) {
+ callerNode.eviction = new IvyNodeEviction(callerNode);
+ if (!callerStack.contains(callerNode)) {
+ callerStack.push(callerNode);
+ clearEvictionDataInAllCallers(rootModuleConf, callerStack);
+ callerStack.pop();
+ }
+ }
+ }
+ }
+
+ /**
+ * Indicates if this node has been blacklisted in the given root module conf.
+ * <p>
+ * A blacklisted node should be considered as if it doesn't even exist on the repository.
+ * </p>
+ *
+ * @param rootModuleConf
+ * the root module conf for which we'd like to know if the node is blacklisted
+ *
+ * @return true if this node is blacklisted int he given root module conf, false otherwise
+ * @see #blacklist(String)
+ */
+ public boolean isBlacklisted(String rootModuleConf) {
+ return usage.isBlacklisted(rootModuleConf);
+ }
+
+ /**
+ * Indicates if this node has been blacklisted in all root module configurations.
+ *
+ * @return true if this node is blacklisted in all root module configurations, false otherwise
+ * @see #blacklist(String)
+ */
+ public boolean isCompletelyBlacklisted() {
+ if (isRoot()) {
+ return false;
+ }
+ String[] rootModuleConfigurations = getRootModuleConfigurations();
+ for (int i = 0; i < rootModuleConfigurations.length; i++) {
+ if (!isBlacklisted(rootModuleConfigurations[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns the blacklist data of this node in the given root module conf, or <code>null</code>
+ * if this node is not blacklisted in this root module conf.
+ *
+ * @param rootModuleConf
+ * the root module configuration to consider
+ * @return the blacklist data if any
+ */
+ public IvyNodeBlacklist getBlacklistData(String rootModuleConf) {
+ return usage.getBlacklistData(rootModuleConf);
+ }
+
+ public IvyNodeUsage getMainUsage() {
+ return usage;
+ }
+
+ /**
+ * Indicates if there is any of the merged usages of this node which has a depender with
+ * transitive dependency descriptor.
+ * <p>
+ * If at there is at least one usage from the merged usages for which there is a depender in the
+ * given root module conf which has a dependency descriptor with transitive == true, then it
+ * returns true. Otherwise it returns false.
+ * </p>
+ *
+ * @param rootModuleConf
+ * the root module configuration to consider
+ * @return true if there is any merged usage with transitive dd, false otherwise.
+ */
+ public boolean hasAnyMergedUsageWithTransitiveDependency(String rootModuleConf) {
+ if (mergedUsages == null) {
+ return false;
+ }
+ for (Iterator iterator = mergedUsages.values().iterator(); iterator.hasNext();) {
+ IvyNodeUsage usage = (IvyNodeUsage) iterator.next();
+ if (usage.hasTransitiveDepender(rootModuleConf)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/src/java/org/apache/ivy/core/resolve/IvyNodeBlacklist.java b/src/java/org/apache/ivy/core/resolve/IvyNodeBlacklist.java
new file mode 100644
index 0000000..466d2b6
--- /dev/null
+++ b/src/java/org/apache/ivy/core/resolve/IvyNodeBlacklist.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.resolve;
+
+/**
+ * Information about a blacklisted module, providing context information in which it has been
+ * blacklisted
+ */
+public class IvyNodeBlacklist {
+ private IvyNode conflictParent;
+
+ private IvyNode selectedNode;
+
+ private IvyNode evictedNode;
+
+ private IvyNode blacklistedNode;
+
+ private String rootModuleConf;
+
+ public IvyNodeBlacklist(IvyNode conflictParent, IvyNode selectedNode, IvyNode evictedNode,
+ IvyNode blacklistedNode, String rootModuleConf) {
+ this.conflictParent = conflictParent;
+ this.selectedNode = selectedNode;
+ this.evictedNode = evictedNode;
+ this.blacklistedNode = blacklistedNode;
+ this.rootModuleConf = rootModuleConf;
+ }
+
+ public IvyNode getConflictParent() {
+ return conflictParent;
+ }
+
+ public IvyNode getSelectedNode() {
+ return selectedNode;
+ }
+
+ public IvyNode getEvictedNode() {
+ return evictedNode;
+ }
+
+ public IvyNode getBlacklistedNode() {
+ return blacklistedNode;
+ }
+
+ public String getRootModuleConf() {
+ return rootModuleConf;
+ }
+
+ public String toString() {
+ return "[" + blacklistedNode + " blacklisted to evict " + evictedNode + " in favor of "
+ + selectedNode + " in " + conflictParent + " for " + rootModuleConf + "]";
+ }
+}
diff --git a/src/java/org/apache/ivy/core/resolve/IvyNodeCallers.java b/src/java/org/apache/ivy/core/resolve/IvyNodeCallers.java
new file mode 100644
index 0000000..399a60a
--- /dev/null
+++ b/src/java/org/apache/ivy/core/resolve/IvyNodeCallers.java
@@ -0,0 +1,296 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.resolve;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.Stack;
+
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.Configuration;
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleId;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+
+public class IvyNodeCallers {
+ public static class Caller {
+ private ModuleDescriptor md;
+
+ private ModuleRevisionId mrid;
+
+ private Map confs = new HashMap(); // Map (String callerConf -> String[] dependencyConfs)
+
+ private DependencyDescriptor dd;
+
+ private boolean callerCanExclude;
+
+ private boolean real = true;
+
+ public Caller(ModuleDescriptor md, ModuleRevisionId mrid, DependencyDescriptor dd,
+ boolean callerCanExclude) {
+ this.md = md;
+ this.mrid = mrid;
+ this.dd = dd;
+ this.callerCanExclude = callerCanExclude;
+ }
+
+ public void addConfiguration(String callerConf, String[] dependencyConfs) {
+ updateConfs(callerConf, dependencyConfs);
+ Configuration conf = md.getConfiguration(callerConf);
+ if (conf != null) {
+ String[] confExtends = conf.getExtends();
+ if (confExtends != null) {
+ for (int i = 0; i < confExtends.length; i++) {
+ addConfiguration(confExtends[i], dependencyConfs);
+ }
+ }
+ }
+ }
+
+ private void updateConfs(String callerConf, String[] dependencyConfs) {
+ String[] prevDepConfs = (String[]) confs.get(callerConf);
+ if (prevDepConfs != null) {
+ Set newDepConfs = new HashSet(Arrays.asList(prevDepConfs));
+ newDepConfs.addAll(Arrays.asList(dependencyConfs));
+ confs.put(callerConf, newDepConfs.toArray(new String[newDepConfs.size()]));
+ } else {
+ confs.put(callerConf, dependencyConfs);
+ }
+ }
+
+ public String[] getCallerConfigurations() {
+ return (String[]) confs.keySet().toArray(new String[confs.keySet().size()]);
+ }
+
+ public ModuleRevisionId getModuleRevisionId() {
+ return mrid;
+ }
+
+ public boolean equals(Object obj) {
+ if (!(obj instanceof Caller)) {
+ return false;
+ }
+ Caller other = (Caller) obj;
+ return other.confs.equals(confs) && mrid.equals(other.mrid);
+ }
+
+ public int hashCode() {
+ // CheckStyle:MagicNumber| OFF
+ int hash = 31;
+ hash = hash * 13 + confs.hashCode();
+ hash = hash * 13 + mrid.hashCode();
+ // CheckStyle:MagicNumber| ON
+ return hash;
+ }
+
+ public String toString() {
+ return mrid.toString();
+ }
+
+ public ModuleRevisionId getAskedDependencyId(ResolveData resolveData) {
+ return dd.getDependencyRevisionId();
+ }
+
+ public ModuleDescriptor getModuleDescriptor() {
+ return md;
+ }
+
+ public boolean canExclude() {
+ return callerCanExclude || md.canExclude() || dd.canExclude();
+ }
+
+ public DependencyDescriptor getDependencyDescriptor() {
+ return dd;
+ }
+
+ public void setRealCaller(boolean b) {
+ this.real = b;
+ }
+
+ public boolean isRealCaller() {
+ return real;
+ }
+ }
+
+ // Map (String rootModuleConf -> Map (ModuleRevisionId -> Caller)): key in second map is used to
+ // easily get a caller by its mrid
+ private Map callersByRootConf = new HashMap();
+
+ // this map contains all the module ids calling this one (including transitively) as keys.
+ // the mapped nodes (values) correspond to a direct caller from which the transitive caller
+ // comes
+
+ private Map allCallers = new HashMap(); // Map (ModuleId -> IvyNode)
+
+ private IvyNode node;
+
+ public IvyNodeCallers(IvyNode node) {
+ this.node = node;
+ }
+
+ /**
+ * @param rootModuleConf
+ * @param mrid
+ * @param callerConf
+ * @param dependencyConfs
+ * '*' must have been resolved
+ * @param dd
+ * the dependency revision id asked by the caller
+ */
+ public void addCaller(String rootModuleConf, IvyNode callerNode, String callerConf,
+ String requestedConf, String[] dependencyConfs, DependencyDescriptor dd) {
+ ModuleDescriptor md = callerNode.getDescriptor();
+ ModuleRevisionId mrid = callerNode.getResolvedId();
+ if (mrid.getModuleId().equals(node.getId().getModuleId())) {
+ throw new IllegalArgumentException("a module is not authorized to depend on itself: "
+ + node.getId());
+ }
+ Map callers = (Map) callersByRootConf.get(rootModuleConf);
+ if (callers == null) {
+ callers = new HashMap();
+ callersByRootConf.put(rootModuleConf, callers);
+ }
+ Caller caller = (Caller) callers.get(mrid);
+ if (caller == null) {
+ caller = new Caller(md, mrid, dd, callerNode.canExclude(rootModuleConf));
+ callers.put(mrid, caller);
+ }
+ caller.addConfiguration(requestedConf, dependencyConfs);
+
+ IvyNode parent = callerNode.getRealNode();
+ for (Iterator iter = parent.getAllCallersModuleIds().iterator(); iter.hasNext();) {
+ ModuleId mid = (ModuleId) iter.next();
+ allCallers.put(mid, parent);
+ }
+ allCallers.put(mrid.getModuleId(), callerNode);
+ }
+
+ void removeCaller(String rootModuleConf, ModuleRevisionId callerMrid) {
+ allCallers.remove(callerMrid.getModuleId());
+ Map callers = (Map) callersByRootConf.get(rootModuleConf);
+ if (callers != null) {
+ callers.remove(callerMrid);
+ }
+ }
+
+ public Caller[] getCallers(String rootModuleConf) {
+ Map callers = (Map) callersByRootConf.get(rootModuleConf);
+ if (callers == null) {
+ return new Caller[0];
+ }
+ return (Caller[]) callers.values().toArray(new Caller[callers.values().size()]);
+ }
+
+ public Caller[] getAllCallers() {
+ Set all = new HashSet();
+ for (Iterator iter = callersByRootConf.values().iterator(); iter.hasNext();) {
+ Map callers = (Map) iter.next();
+ all.addAll(callers.values());
+ }
+ return (Caller[]) all.toArray(new Caller[all.size()]);
+ }
+
+ public Caller[] getAllRealCallers() {
+ Set all = new HashSet();
+ for (Iterator iter = callersByRootConf.values().iterator(); iter.hasNext();) {
+ Map callers = (Map) iter.next();
+ for (Iterator iterator = callers.values().iterator(); iterator.hasNext();) {
+ Caller c = (Caller) iterator.next();
+ if (c.isRealCaller()) {
+ all.add(c);
+ }
+ }
+ }
+ return (Caller[]) all.toArray(new Caller[all.size()]);
+ }
+
+ public Collection getAllCallersModuleIds() {
+ return allCallers.keySet();
+ }
+
+ void updateFrom(IvyNodeCallers callers, String rootModuleConf, boolean real) {
+ Map nodecallers = (Map) callers.callersByRootConf.get(rootModuleConf);
+ if (nodecallers != null) {
+ Map thiscallers = (Map) callersByRootConf.get(rootModuleConf);
+ if (thiscallers == null) {
+ thiscallers = new HashMap();
+ callersByRootConf.put(rootModuleConf, thiscallers);
+ }
+ for (Iterator iter = nodecallers.values().iterator(); iter.hasNext();) {
+ Caller caller = (Caller) iter.next();
+ if (!thiscallers.containsKey(caller.getModuleRevisionId())) {
+ if (!real) {
+ caller.setRealCaller(false);
+ }
+ thiscallers.put(caller.getModuleRevisionId(), caller);
+ }
+ }
+ }
+ }
+
+ public IvyNode getDirectCallerFor(ModuleId from) {
+ return (IvyNode) allCallers.get(from);
+ }
+
+ /**
+ * Returns true if ALL callers exclude the given artifact in the given root module conf
+ *
+ * @param rootModuleConf
+ * @param artifact
+ * @return
+ */
+ boolean doesCallersExclude(String rootModuleConf, Artifact artifact) {
+ return doesCallersExclude(rootModuleConf, artifact, new Stack());
+ }
+
+ boolean doesCallersExclude(String rootModuleConf, Artifact artifact, Stack callersStack) {
+ callersStack.push(node.getId());
+ try {
+ Caller[] callers = getCallers(rootModuleConf);
+ if (callers.length == 0) {
+ return false;
+ }
+ boolean allUnconclusive = true;
+ for (int i = 0; i < callers.length; i++) {
+ if (!callers[i].canExclude()) {
+ return false;
+ }
+ ModuleDescriptor md = callers[i].getModuleDescriptor();
+ Boolean doesExclude = node.doesExclude(md, rootModuleConf,
+ callers[i].getCallerConfigurations(), callers[i].getDependencyDescriptor(),
+ artifact, callersStack);
+ if (doesExclude != null) {
+ if (!doesExclude.booleanValue()) {
+ return false;
+ }
+ allUnconclusive = false;
+ }
+ }
+ return allUnconclusive ? false : true;
+ } finally {
+ callersStack.pop();
+ }
+ }
+
+}
diff --git a/src/java/org/apache/ivy/core/resolve/IvyNodeEviction.java b/src/java/org/apache/ivy/core/resolve/IvyNodeEviction.java
new file mode 100644
index 0000000..5c757ee
--- /dev/null
+++ b/src/java/org/apache/ivy/core/resolve/IvyNodeEviction.java
@@ -0,0 +1,439 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.resolve;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.ivy.core.module.id.ModuleId;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.plugins.conflict.ConflictManager;
+
+public class IvyNodeEviction {
+ /**
+ * This class contains data about the eviction of an {@link IvyNode}.
+ */
+ public static class EvictionData {
+ /**
+ * Can be null in case of transitive eviction.
+ */
+ private IvyNode parent;
+
+ /**
+ * Can be null in case of transitive eviction.
+ */
+ private ConflictManager conflictManager;
+
+ /**
+ * Can be null in case of transitive eviction.
+ */
+ private Collection selected; // Collection(IvyNode)
+
+ private String rootModuleConf;
+
+ private String detail;
+
+ /**
+ * Creates a new object containing the eviction data of an {@link IvyNode}.
+ *
+ * @param rootModuleConf
+ * the root module configuration
+ * @param parent
+ * the parent node (or <tt>null</tt> in case of transitive eviction)
+ * @param conflictManager
+ * the conflict manager which evicted the node (or <tt>null</tt> in case of
+ * transitive eviction)
+ * @param selected
+ * a collection of {@link IvyNode}s which evict the evicted node (or
+ * <tt>null</tt> in case of transitive eviction)
+ */
+ public EvictionData(String rootModuleConf, IvyNode parent, ConflictManager conflictManager,
+ Collection selected) {
+ this(rootModuleConf, parent, conflictManager, selected, null);
+ }
+
+ /**
+ * Creates a new object containing the eviction data of an {@link IvyNode}.
+ *
+ * @param rootModuleConf
+ * the root module configuration
+ * @param parent
+ * the parent node (or <tt>null</tt> in case of transitive eviction)
+ * @param conflictManager
+ * the conflict manager which evicted the node (or <tt>null</tt> in case of
+ * transitive eviction)
+ * @param selected
+ * a collection of {@link IvyNode}s which evict the evicted node (or
+ * <tt>null</tt> in case of transitive eviction)
+ * @param detail
+ * a String detailing the reason why the node was evicted
+ */
+ public EvictionData(String rootModuleConf, IvyNode parent, ConflictManager conflictManager,
+ Collection selected, String detail) {
+ this.rootModuleConf = rootModuleConf;
+ this.parent = parent;
+ this.conflictManager = conflictManager;
+ this.selected = selected;
+ this.detail = detail;
+ }
+
+ public String toString() {
+ if (selected != null) {
+ return selected + " in " + parent + (detail == null ? "" : " " + detail) + " ("
+ + conflictManager + ") [" + rootModuleConf + "]";
+ } else {
+ return "transitively [" + rootModuleConf + "]";
+ }
+ }
+
+ public ConflictManager getConflictManager() {
+ return conflictManager;
+ }
+
+ public IvyNode getParent() {
+ return parent;
+ }
+
+ public Collection getSelected() {
+ return selected;
+ }
+
+ public String getRootModuleConf() {
+ return rootModuleConf;
+ }
+
+ public boolean isTransitivelyEvicted() {
+ return parent == null;
+ }
+
+ public String getDetail() {
+ return detail;
+ }
+ }
+
+ private static final class ModuleIdConf {
+ private ModuleId moduleId;
+
+ private String conf;
+
+ public ModuleIdConf(ModuleId mid, String conf) {
+ if (mid == null) {
+ throw new NullPointerException("mid cannot be null");
+ }
+ if (conf == null) {
+ throw new NullPointerException("conf cannot be null");
+ }
+ moduleId = mid;
+ this.conf = conf;
+ }
+
+ public final String getConf() {
+ return conf;
+ }
+
+ public final ModuleId getModuleId() {
+ return moduleId;
+ }
+
+ public boolean equals(Object obj) {
+ if (!(obj instanceof ModuleIdConf)) {
+ return false;
+ }
+ return getModuleId().equals(((ModuleIdConf) obj).getModuleId())
+ && getConf().equals(((ModuleIdConf) obj).getConf());
+ }
+
+ public int hashCode() {
+ // CheckStyle:MagicNumber| OFF
+ int hash = 33;
+ hash += getModuleId().hashCode() * 17;
+ hash += getConf().hashCode() * 17;
+ // CheckStyle:MagicNumber| ON
+ return hash;
+ }
+ }
+
+ private IvyNode node;
+
+ private Map selectedDeps = new HashMap(); // Map (ModuleIdConf -> Set(Node)) // map indicating
+
+ // for each dependency which node has been selected
+
+ private Map pendingConflicts = new HashMap(); // Map (ModuleIdConf -> Set(Node)) // map
+
+ // indicating for each dependency which nodes
+ // are in pending conflict (conflict detected
+ // but not yet resolved)
+
+ private Map evictedDeps = new HashMap(); // Map (ModuleIdConf -> Set(Node)) // map indicating
+
+ // for each dependency which node has been evicted
+
+ private Map evictedRevs = new HashMap(); // Map (ModuleIdConf -> Set(ModuleRevisionId)) //
+
+ // map indicating for each dependency which revision
+ // has been evicted
+
+ private Map evicted = new HashMap(); // Map (root module conf -> EvictionData) // indicates
+
+ // if the node is evicted in each root module conf
+
+ public IvyNodeEviction(IvyNode node) {
+ if (node == null) {
+ throw new NullPointerException("node must not be null");
+ }
+ this.node = node;
+ }
+
+ /**
+ * @return A copy of the set of resolved nodes (real nodes)
+ */
+ public Set getResolvedNodes(ModuleId mid, String rootModuleConf) {
+ Collection resolved = (Collection) selectedDeps.get(new ModuleIdConf(mid, rootModuleConf));
+ Set ret = new HashSet();
+ if (resolved != null) {
+ for (Iterator iter = resolved.iterator(); iter.hasNext();) {
+ IvyNode node = (IvyNode) iter.next();
+ ret.add(node.getRealNode());
+ }
+ }
+ return ret;
+ }
+
+ public Collection getResolvedRevisions(ModuleId mid, String rootModuleConf) {
+ Collection resolved = (Collection) selectedDeps.get(new ModuleIdConf(mid, rootModuleConf));
+ if (resolved == null) {
+ return new HashSet();
+ } else {
+ Collection resolvedRevs = new HashSet();
+ for (Iterator iter = resolved.iterator(); iter.hasNext();) {
+ IvyNode node = (IvyNode) iter.next();
+ ModuleRevisionId resolvedId = node.getResolvedId();
+ resolvedRevs.add(node.getId());
+ resolvedRevs.add(resolvedId);
+
+ // in case there are extra attributes on the resolved module we also add the
+ // the module without these extra attributes (cfr. IVY-1236)
+ if (!resolvedId.getExtraAttributes().isEmpty()) {
+ resolvedRevs.add(ModuleRevisionId.newInstance(resolvedId.getOrganisation(),
+ resolvedId.getName(), resolvedId.getBranch(), resolvedId.getRevision()));
+ }
+ }
+ return resolvedRevs;
+ }
+ }
+
+ public void setResolvedNodes(ModuleId moduleId, String rootModuleConf, Collection resolved) {
+ ModuleIdConf moduleIdConf = new ModuleIdConf(moduleId, rootModuleConf);
+ selectedDeps.put(moduleIdConf, new HashSet(resolved));
+ }
+
+ public Collection getEvictedNodes(ModuleId mid, String rootModuleConf) {
+ Collection resolved = (Collection) evictedDeps.get(new ModuleIdConf(mid, rootModuleConf));
+ Set ret = new HashSet();
+ if (resolved != null) {
+ for (Iterator iter = resolved.iterator(); iter.hasNext();) {
+ IvyNode node = (IvyNode) iter.next();
+ ret.add(node.getRealNode());
+ }
+ }
+ return ret;
+ }
+
+ public Collection getEvictedRevisions(ModuleId mid, String rootModuleConf) {
+ Collection evicted = (Collection) evictedRevs.get(new ModuleIdConf(mid, rootModuleConf));
+ if (evicted == null) {
+ return new HashSet();
+ } else {
+ return new HashSet(evicted);
+ }
+ }
+
+ public void setEvictedNodes(ModuleId moduleId, String rootModuleConf, Collection evicted) {
+ ModuleIdConf moduleIdConf = new ModuleIdConf(moduleId, rootModuleConf);
+ evictedDeps.put(moduleIdConf, new HashSet(evicted));
+ Collection evictedRevs = new HashSet();
+ for (Iterator iter = evicted.iterator(); iter.hasNext();) {
+ IvyNode node = (IvyNode) iter.next();
+ evictedRevs.add(node.getId());
+ evictedRevs.add(node.getResolvedId());
+ }
+ this.evictedRevs.put(moduleIdConf, evictedRevs);
+ }
+
+ public boolean isEvicted(String rootModuleConf) {
+ cleanEvicted();
+ if (node.isRoot()) {
+ return false;
+ }
+ EvictionData evictedData = getEvictedData(rootModuleConf);
+ if (evictedData == null) {
+ return false;
+ }
+ IvyNode root = node.getRoot();
+ ModuleId moduleId = node.getId().getModuleId();
+ Collection resolvedRevisions = root.getResolvedRevisions(moduleId, rootModuleConf);
+ return !resolvedRevisions.contains(node.getResolvedId())
+ || evictedData.isTransitivelyEvicted();
+ }
+
+ public boolean isCompletelyEvicted() {
+ cleanEvicted();
+ if (node.isRoot()) {
+ return false;
+ }
+ String[] rootModuleConfigurations = node.getRootModuleConfigurations();
+ for (int i = 0; i < rootModuleConfigurations.length; i++) {
+ if (!isEvicted(rootModuleConfigurations[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private void cleanEvicted() {
+ // check if it was evicted by a node that we are now the real node for
+ for (Iterator iter = evicted.keySet().iterator(); iter.hasNext();) {
+ String rootModuleConf = (String) iter.next();
+ EvictionData ed = (EvictionData) evicted.get(rootModuleConf);
+ Collection sel = ed.getSelected();
+ if (sel != null) {
+ for (Iterator iterator = sel.iterator(); iterator.hasNext();) {
+ IvyNode n = (IvyNode) iterator.next();
+ if (n.getRealNode().equals(node)) {
+ // yes, we are the real node for a selected one !
+ // we are no more evicted in this conf !
+ iter.remove();
+ }
+ }
+ }
+ }
+ }
+
+ public void markEvicted(EvictionData evictionData) {
+ evicted.put(evictionData.getRootModuleConf(), evictionData);
+ }
+
+ public EvictionData getEvictedData(String rootModuleConf) {
+ cleanEvicted();
+ return (EvictionData) evicted.get(rootModuleConf);
+ }
+
+ public String[] getEvictedConfs() {
+ cleanEvicted();
+ return (String[]) evicted.keySet().toArray(new String[evicted.keySet().size()]);
+ }
+
+ /**
+ * Returns null if this node has only be evicted transitively, or the the collection of selected
+ * nodes if it has been evicted by other selected nodes
+ *
+ * @return
+ */
+ public Collection getAllEvictingNodes() {
+ Collection allEvictingNodes = null;
+ for (Iterator iter = evicted.values().iterator(); iter.hasNext();) {
+ EvictionData ed = (EvictionData) iter.next();
+ Collection selected = ed.getSelected();
+ if (selected != null) {
+ if (allEvictingNodes == null) {
+ allEvictingNodes = new HashSet();
+ }
+ allEvictingNodes.addAll(selected);
+ }
+ }
+ return allEvictingNodes;
+ }
+
+ public Collection/* <String> */getAllEvictingNodesDetails() {
+ Collection ret = null;
+ for (Iterator iter = evicted.values().iterator(); iter.hasNext();) {
+ EvictionData ed = (EvictionData) iter.next();
+ Collection selected = ed.getSelected();
+ if (selected != null) {
+ if (ret == null) {
+ ret = new HashSet();
+ }
+ if (selected.size() == 1) {
+ ret.add(selected.iterator().next()
+ + (ed.getDetail() == null ? "" : " " + ed.getDetail()));
+ } else if (selected.size() > 1) {
+ ret.add(selected + (ed.getDetail() == null ? "" : " " + ed.getDetail()));
+ }
+ }
+ }
+ return ret;
+ }
+
+ public Collection getAllEvictingConflictManagers() {
+ Collection ret = new HashSet();
+ for (Iterator iter = evicted.values().iterator(); iter.hasNext();) {
+ EvictionData ed = (EvictionData) iter.next();
+ ret.add(ed.getConflictManager());
+ }
+ return ret;
+ }
+
+ /**
+ * Returns the eviction data for this node if it has been previously evicted in the root, null
+ * otherwise (if it hasn't been evicted in root) for the given rootModuleConf. Note that this
+ * method only works if conflict resolution has already be done in all the ancestors.
+ *
+ * @param rootModuleConf
+ * @param ancestor
+ * @return
+ */
+ public EvictionData getEvictionDataInRoot(String rootModuleConf, IvyNode ancestor) {
+ Collection selectedNodes = node.getRoot().getResolvedNodes(node.getModuleId(),
+ rootModuleConf);
+ for (Iterator iter = selectedNodes.iterator(); iter.hasNext();) {
+ IvyNode node = (IvyNode) iter.next();
+ if (node.getResolvedId().equals(this.node.getResolvedId())) {
+ // the node is part of the selected ones for the root: no eviction data to return
+ return null;
+ }
+ }
+ // we didn't find this mrid in the selected ones for the root: it has been previously
+ // evicted
+ return new EvictionData(rootModuleConf, ancestor, node.getRoot().getConflictManager(
+ node.getModuleId()), selectedNodes);
+ }
+
+ public Collection getPendingConflicts(String rootModuleConf, ModuleId mid) {
+ Collection resolved = (Collection) pendingConflicts.get(new ModuleIdConf(mid,
+ rootModuleConf));
+ Set ret = new HashSet();
+ if (resolved != null) {
+ for (Iterator iter = resolved.iterator(); iter.hasNext();) {
+ IvyNode node = (IvyNode) iter.next();
+ ret.add(node.getRealNode());
+ }
+ }
+ return ret;
+ }
+
+ public void setPendingConflicts(ModuleId moduleId, String rootModuleConf, Collection conflicts) {
+ ModuleIdConf moduleIdConf = new ModuleIdConf(moduleId, rootModuleConf);
+ pendingConflicts.put(moduleIdConf, new HashSet(conflicts));
+ }
+
+}
diff --git a/src/java/org/apache/ivy/core/resolve/IvyNodeUsage.java b/src/java/org/apache/ivy/core/resolve/IvyNodeUsage.java
new file mode 100644
index 0000000..5269558
--- /dev/null
+++ b/src/java/org/apache/ivy/core/resolve/IvyNodeUsage.java
@@ -0,0 +1,326 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.resolve;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.ivy.core.module.descriptor.DependencyArtifactDescriptor;
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.IncludeRule;
+
+/**
+ * Class collecting usage data for an IvyNode.
+ * <p>
+ * Usage data contains the configurations required by callers for each root module configuration,
+ * the configurations required by caller node and caller configuration, dependency artifacts
+ * descriptors declared by callers, include rules declared by callers, and blacklisted data by root
+ * module conf.
+ * </p>
+ */
+public class IvyNodeUsage {
+
+ private static final class NodeConf {
+ private IvyNode node;
+
+ private String conf;
+
+ public NodeConf(IvyNode node, String conf) {
+ if (node == null) {
+ throw new NullPointerException("node must not null");
+ }
+ if (conf == null) {
+ throw new NullPointerException("conf must not null");
+ }
+ this.node = node;
+ this.conf = conf;
+ }
+
+ public final String getConf() {
+ return conf;
+ }
+
+ public final IvyNode getNode() {
+ return node;
+ }
+
+ public boolean equals(Object obj) {
+ if (!(obj instanceof NodeConf)) {
+ return false;
+ }
+ return getNode().equals(((NodeConf) obj).getNode())
+ && getConf().equals(((NodeConf) obj).getConf());
+ }
+
+ public int hashCode() {
+ // CheckStyle:MagicNumber| OFF
+ int hash = 33;
+ hash += getNode().hashCode() * 17;
+ hash += getConf().hashCode() * 17;
+ // CheckStyle:MagicNumber| OFF
+ return hash;
+ }
+
+ public String toString() {
+ return "NodeConf(" + conf + ")";
+ }
+ }
+
+ private static final class Depender {
+ private DependencyDescriptor dd;
+
+ private String dependerConf;
+
+ public Depender(DependencyDescriptor dd, String dependerConf) {
+ this.dd = dd;
+ this.dependerConf = dependerConf;
+ }
+
+ public String toString() {
+ return dd + " [" + dependerConf + "]";
+ }
+
+ public boolean equals(Object obj) {
+ if (!(obj instanceof Depender)) {
+ return false;
+ }
+ Depender other = (Depender) obj;
+ return other.dd == dd && other.dependerConf.equals(dependerConf);
+ }
+
+ public int hashCode() {
+ int hash = 33;
+ hash += dd.hashCode() * 13;
+ hash += dependerConf.hashCode() * 13;
+ return hash;
+ }
+ }
+
+ private IvyNode node;
+
+ // Map (String rootConfName -> Set(String confName))
+ // used to know which configurations of the dependency are required
+ // for each root module configuration
+ private Map rootModuleConfs = new HashMap();
+
+ // Map (NodeConf in -> Set(String conf))
+ private Map requiredConfs = new HashMap();
+
+ private Map /* <String, Set<Depender>> */dependers = new HashMap();
+
+ // Map (String rootModuleConf -> IvyNodeBlacklist)
+ private Map blacklisted = new HashMap();
+
+ public IvyNodeUsage(IvyNode node) {
+ this.node = node;
+ }
+
+ protected Collection getRequiredConfigurations(IvyNode in, String inConf) {
+ return (Collection) requiredConfs.get(new NodeConf(in, inConf));
+ }
+
+ protected void setRequiredConfs(IvyNode parent, String parentConf, Collection confs) {
+ requiredConfs.put(new NodeConf(parent, parentConf), new HashSet(confs));
+ }
+
+ /**
+ * Returns the configurations of the dependency required in a given root module configuration.
+ *
+ * @param rootModuleConf
+ * @return
+ */
+ protected Set getConfigurations(String rootModuleConf) {
+ return (Set) rootModuleConfs.get(rootModuleConf);
+ }
+
+ protected Set addAndGetConfigurations(String rootModuleConf) {
+ Set depConfs = (Set) rootModuleConfs.get(rootModuleConf);
+ if (depConfs == null) {
+ depConfs = new HashSet();
+ rootModuleConfs.put(rootModuleConf, depConfs);
+ }
+ return depConfs;
+ }
+
+ protected Set /* <String> */getRootModuleConfigurations() {
+ return rootModuleConfs.keySet();
+ }
+
+ public void updateDataFrom(Collection/* <IvyNodeUsage> */usages, String rootModuleConf) {
+ for (Iterator iterator = usages.iterator(); iterator.hasNext();) {
+ IvyNodeUsage usage = (IvyNodeUsage) iterator.next();
+ updateDataFrom(usage, rootModuleConf);
+ }
+ }
+
+ private void updateDataFrom(IvyNodeUsage usage, String rootModuleConf) {
+ // update requiredConfs
+ updateMapOfSet(usage.requiredConfs, requiredConfs);
+
+ // update rootModuleConfs
+ updateMapOfSetForKey(usage.rootModuleConfs, rootModuleConfs, rootModuleConf);
+
+ // update dependencyArtifacts
+ updateMapOfSetForKey(usage.dependers, dependers, rootModuleConf);
+ }
+
+ private void updateMapOfSet(Map from, Map to) {
+ for (Iterator iter = from.keySet().iterator(); iter.hasNext();) {
+ Object key = iter.next();
+ updateMapOfSetForKey(from, to, key);
+ }
+ }
+
+ private void updateMapOfSetForKey(Map from, Map to, Object key) {
+ Set set = (Set) from.get(key);
+ if (set != null) {
+ Set toupdate = (Set) to.get(key);
+ if (toupdate != null) {
+ toupdate.addAll(set);
+ } else {
+ to.put(key, new HashSet(set));
+ }
+ }
+ }
+
+ // protected void addDependencyArtifacts(String rootModuleConf,
+ // DependencyArtifactDescriptor[] dependencyArtifacts) {
+ // addObjectsForConf(rootModuleConf, Arrays.asList(dependencyArtifacts),
+ // this.dependencyArtifacts);
+ // }
+ //
+ // protected void addDependencyIncludes(String rootModuleConf, IncludeRule[] rules) {
+ // addObjectsForConf(rootModuleConf, Arrays.asList(rules), dependencyIncludes);
+ // }
+ //
+ private void addObjectsForConf(String rootModuleConf, Object objectToAdd, Map map) {
+ Set set = (Set) map.get(rootModuleConf);
+ if (set == null) {
+ set = new HashSet();
+ map.put(rootModuleConf, set);
+ }
+ set.add(objectToAdd);
+ }
+
+ public void addUsage(String rootModuleConf, DependencyDescriptor dd, String parentConf) {
+ addObjectsForConf(rootModuleConf, new Depender(dd, parentConf), dependers);
+ }
+
+ protected Set getDependencyArtifactsSet(String rootModuleConf) {
+ Collection dependersInConf = (Collection) dependers.get(rootModuleConf);
+ if (dependersInConf == null) {
+ return null;
+ }
+ Set dependencyArtifacts = new HashSet();
+ for (Iterator iterator = dependersInConf.iterator(); iterator.hasNext();) {
+ Depender depender = (Depender) iterator.next();
+ DependencyArtifactDescriptor[] dads = depender.dd
+ .getDependencyArtifacts(depender.dependerConf);
+ dependencyArtifacts.addAll(Arrays.asList(dads));
+ }
+ return dependencyArtifacts;
+ }
+
+ protected Set getDependencyIncludesSet(String rootModuleConf) {
+ Collection dependersInConf = (Collection) dependers.get(rootModuleConf);
+ if (dependersInConf == null) {
+ return null;
+ }
+ Set dependencyIncludes = new HashSet();
+ for (Iterator iterator = dependersInConf.iterator(); iterator.hasNext();) {
+ Depender depender = (Depender) iterator.next();
+ IncludeRule[] rules = depender.dd.getIncludeRules(depender.dependerConf);
+ if (rules == null || rules.length == 0) {
+ // no include rule in at least one depender -> we must include everything,
+ // and so return no include rule at all
+ return null;
+ }
+ dependencyIncludes.addAll(Arrays.asList(rules));
+ }
+ return dependencyIncludes;
+ }
+
+ protected void removeRootModuleConf(String rootModuleConf) {
+ rootModuleConfs.remove(rootModuleConf);
+ }
+
+ protected void blacklist(IvyNodeBlacklist bdata) {
+ blacklisted.put(bdata.getRootModuleConf(), bdata);
+ }
+
+ /**
+ * Indicates if this node has been blacklisted in the given root module conf.
+ * <p>
+ * A blacklisted node should be considered as if it doesn't even exist on the repository.
+ * </p>
+ *
+ * @param rootModuleConf
+ * the root module conf for which we'd like to know if the node is blacklisted
+ *
+ * @return true if this node is blacklisted int he given root module conf, false otherwise
+ * @see #blacklist(String)
+ */
+ protected boolean isBlacklisted(String rootModuleConf) {
+ return blacklisted.containsKey(rootModuleConf);
+ }
+
+ /**
+ * Returns the blacklist data of this node in the given root module conf, or <code>null</code>
+ * if this node is not blacklisted in this root module conf.
+ *
+ * @param rootModuleConf
+ * the root module configuration to consider
+ * @return the blacklist data if any
+ */
+ protected IvyNodeBlacklist getBlacklistData(String rootModuleConf) {
+ return (IvyNodeBlacklist) blacklisted.get(rootModuleConf);
+ }
+
+ protected IvyNode getNode() {
+ return node;
+ }
+
+ /**
+ * Indicates if at least one depender has a transitive dependency descriptor for the given root
+ * module conf.
+ *
+ * @param rootModuleConf
+ * the root module conf to consider
+ * @return <code>true</code> if at least one depender has a transitive dependency descriptor for
+ * the given root module conf, <code>false</code> otherwise.
+ */
+ public boolean hasTransitiveDepender(String rootModuleConf) {
+ Set/* <Depender> */dependersSet = (Set) dependers.get(rootModuleConf);
+ if (dependersSet == null) {
+ return false;
+ }
+ for (Iterator iterator = dependersSet.iterator(); iterator.hasNext();) {
+ Depender depender = (Depender) iterator.next();
+ if (depender.dd.isTransitive()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/src/java/org/apache/ivy/core/resolve/ResolveData.java b/src/java/org/apache/ivy/core/resolve/ResolveData.java
new file mode 100644
index 0000000..54cb661
--- /dev/null
+++ b/src/java/org/apache/ivy/core/resolve/ResolveData.java
@@ -0,0 +1,351 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.resolve;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.ivy.core.event.EventManager;
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.report.ConfigurationResolveReport;
+import org.apache.ivy.util.Message;
+
+public class ResolveData {
+ private ResolveEngine engine;
+
+ private Map visitData; // shared map of all visit data: Map (ModuleRevisionId -> VisitData)
+
+ private ConfigurationResolveReport report;
+
+ private ResolveOptions options;
+
+ private VisitNode currentVisitNode = null;
+
+ private ResolvedModuleRevision currentResolvedModuleRevision;
+
+ public ResolveData(ResolveData data, boolean validate) {
+ this(data.engine, new ResolveOptions(data.options).setValidate(validate), data.report,
+ data.visitData);
+ setCurrentVisitNode(data.currentVisitNode);
+ setCurrentResolvedModuleRevision(data.currentResolvedModuleRevision);
+ }
+
+ public ResolveData(ResolveEngine engine, ResolveOptions options) {
+ this(engine, options, null, new LinkedHashMap());
+ }
+
+ public ResolveData(ResolveEngine engine, ResolveOptions options,
+ ConfigurationResolveReport report) {
+ this(engine, options, report, new LinkedHashMap());
+ }
+
+ public ResolveData(ResolveEngine engine, ResolveOptions options,
+ ConfigurationResolveReport report, Map visitData) {
+ this.engine = engine;
+ this.report = report;
+ this.visitData = visitData;
+ this.options = options;
+ }
+
+ public ConfigurationResolveReport getReport() {
+ return report;
+ }
+
+ public IvyNode getNode(ModuleRevisionId mrid) {
+ VisitData visitData = getVisitData(mrid);
+ return visitData == null ? null : visitData.getNode();
+ }
+
+ public Collection getNodes() {
+ Collection nodes = new ArrayList();
+ for (Iterator iter = visitData.values().iterator(); iter.hasNext();) {
+ VisitData vdata = (VisitData) iter.next();
+ nodes.add(vdata.getNode());
+ }
+ return nodes;
+ }
+
+ public Collection getNodeIds() {
+ return visitData.keySet();
+ }
+
+ public VisitData getVisitData(ModuleRevisionId mrid) {
+ VisitData result = (VisitData) visitData.get(mrid);
+
+ if (result == null) {
+ // search again, now ignore the missing extra attributes
+ for (Iterator it = visitData.entrySet().iterator(); it.hasNext();) {
+ Map.Entry entry = (Entry) it.next();
+ ModuleRevisionId current = (ModuleRevisionId) entry.getKey();
+
+ if (isSubMap(mrid.getAttributes(), current.getAttributes())) {
+ result = (VisitData) entry.getValue();
+ break;
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Checks whether one map is a sub-map of the other.
+ */
+ private static boolean isSubMap(Map map1, Map map2) {
+ int map1Size = map1.size();
+ int map2Size = map2.size();
+
+ if (map1Size == map2Size) {
+ return map1.equals(map2);
+ }
+
+ Map smallest = map1Size < map2Size ? map1 : map2;
+ Map largest = map1Size < map2Size ? map2 : map1;
+
+ for (Iterator it = smallest.entrySet().iterator(); it.hasNext();) {
+ Map.Entry entry = (Entry) it.next();
+
+ if (!largest.containsKey(entry.getKey())) {
+ return false;
+ }
+
+ Object map1Value = smallest.get(entry.getKey());
+ Object map2Value = largest.get(entry.getKey());
+ if (!isEqual(map1Value, map2Value)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private static boolean isEqual(Object obj1, Object obj2) {
+ if (obj1 == obj2) {
+ return true;
+ }
+
+ if (obj1 == null) {
+ return obj2 == null;
+ }
+
+ if (obj2 == null) {
+ return obj1 == null;
+ }
+
+ return obj1.equals(obj2);
+ }
+
+ /**
+ * Returns the VisitNode currently visited, or <code>null</code> if there is no node currently
+ * visited in this context.
+ *
+ * @return the VisitNode currently visited
+ */
+ public VisitNode getCurrentVisitNode() {
+ return currentVisitNode;
+ }
+
+ /**
+ * Sets the currently visited node. WARNING: This should only be called by Ivy core
+ * ResolveEngine!
+ *
+ * @param currentVisitNode
+ */
+ void setCurrentVisitNode(VisitNode currentVisitNode) {
+ this.currentVisitNode = currentVisitNode;
+ }
+
+ public void register(VisitNode node) {
+ register(node.getId(), node);
+ }
+
+ public void register(ModuleRevisionId mrid, VisitNode node) {
+ VisitData visitData = getVisitData(mrid);
+ if (visitData == null) {
+ visitData = new VisitData(node.getNode());
+ visitData.addVisitNode(node);
+ this.visitData.put(mrid, visitData);
+ } else {
+ visitData.setNode(node.getNode());
+ visitData.addVisitNode(node);
+ }
+ }
+
+ /**
+ * Updates the visit data currently associated with the given mrid with the given node and the
+ * visit nodes of the old visitData for the given rootModuleConf
+ *
+ * @param mrid
+ * the module revision id for which the update should be done
+ * @param node
+ * the IvyNode to associate with the visit data to update
+ * @param rootModuleConf
+ * the root module configuration in which the update is made
+ */
+ void replaceNode(ModuleRevisionId mrid, IvyNode node, String rootModuleConf) {
+ VisitData visitData = getVisitData(mrid);
+ if (visitData == null) {
+ throw new IllegalArgumentException("impossible to replace node for id " + mrid
+ + ". No registered node found.");
+ }
+ VisitData keptVisitData = getVisitData(node.getId());
+ if (keptVisitData == null) {
+ throw new IllegalArgumentException("impossible to replace node with " + node
+ + ". No registered node found for " + node.getId() + ".");
+ }
+ // replace visit data in Map (discards old one)
+ this.visitData.put(mrid, keptVisitData);
+ // update visit data with discarde visit nodes
+ keptVisitData.addVisitNodes(rootModuleConf, visitData.getVisitNodes(rootModuleConf));
+
+ report.updateDependency(mrid, node);
+ }
+
+ public void setReport(ConfigurationResolveReport report) {
+ this.report = report;
+ }
+
+ public Date getDate() {
+ return options.getDate();
+ }
+
+ public boolean isValidate() {
+ return options.isValidate();
+ }
+
+ public boolean isTransitive() {
+ return options.isTransitive();
+ }
+
+ public ResolveOptions getOptions() {
+ return options;
+ }
+
+ public ResolveEngineSettings getSettings() {
+ return engine.getSettings();
+ }
+
+ public EventManager getEventManager() {
+ return engine.getEventManager();
+ }
+
+ public ResolveEngine getEngine() {
+ return engine;
+ }
+
+ void blacklist(IvyNode node) {
+ for (Iterator iter = visitData.entrySet().iterator(); iter.hasNext();) {
+ Entry entry = (Entry) iter.next();
+ VisitData vdata = (VisitData) entry.getValue();
+ if (vdata.getNode() == node && !node.getResolvedId().equals(entry.getKey())) {
+ // this visit data was associated with the blacklisted node,
+ // we discard this association
+ iter.remove();
+ }
+ }
+ }
+
+ public boolean isBlacklisted(String rootModuleConf, ModuleRevisionId mrid) {
+ IvyNode node = getNode(mrid);
+
+ // if (node == null) {
+ // // search again, now ignore the extra attributes
+ // // TODO: maybe we should search the node that has at least the
+ // // same attributes as mrid
+ // for (Iterator it = visitData.entrySet().iterator(); it.hasNext();) {
+ // Map.Entry entry = (Entry) it.next();
+ // ModuleRevisionId current = (ModuleRevisionId) entry.getKey();
+ // if (current.getModuleId().equals(mrid.getModuleId())
+ // && current.getRevision().equals(mrid.getRevision())) {
+ // VisitData data = (VisitData) entry.getValue();
+ // node = data.getNode();
+ // break;
+ // }
+ // }
+ // }
+ //
+ return node != null && node.isBlacklisted(rootModuleConf);
+ }
+
+ public DependencyDescriptor mediate(DependencyDescriptor dd) {
+ DependencyDescriptor originalDD = dd;
+ dd = getEngine().mediate(dd, getOptions());
+
+ VisitNode current = getCurrentVisitNode();
+ if (current != null) {
+ // mediating dd through dependers stack
+ List dependers = new ArrayList(current.getPath());
+ // the returned path contains the currently visited node, we are only interested in
+ // the dependers, so we remove the currently visted node from the end
+ dependers.remove(dependers.size() - 1);
+ // we want to apply mediation going up in the dependers stack, not the opposite
+ Collections.reverse(dependers);
+ for (Iterator iterator = dependers.iterator(); iterator.hasNext();) {
+ VisitNode n = (VisitNode) iterator.next();
+ ModuleDescriptor md = n.getDescriptor();
+ if (md != null) {
+ dd = md.mediate(dd);
+ }
+ }
+ }
+
+ if (originalDD != dd) {
+ Message.verbose("dependency descriptor has been mediated: " + originalDD + " => " + dd);
+ }
+
+ return dd;
+ }
+
+ /**
+ * Sets the last {@link ResolvedModuleRevision} which has been currently resolved.
+ * <p>
+ * This can be used especially in dependency resolvers, to know if another dependency resolver
+ * has already resolved the requested dependency, to take a decision if the resolver should try
+ * to resolve it by itself or not. Indeed, the dependency resolver is responsible for taking
+ * this decision, even when included in a chain. The chain responsibility is only to set this
+ * current resolved module revision to enable the resolver to take the decision.
+ * </p>
+ *
+ * @param mr
+ * the last {@link ResolvedModuleRevision} which has been currently resolved.
+ */
+ public void setCurrentResolvedModuleRevision(ResolvedModuleRevision mr) {
+ this.currentResolvedModuleRevision = mr;
+ }
+
+ /**
+ * Returns the last {@link ResolvedModuleRevision} which has been currently resolved.
+ * <p>
+ * It can be <code>null</code>.
+ * </p>
+ *
+ * @return the last {@link ResolvedModuleRevision} which has been currently resolved.
+ */
+ public ResolvedModuleRevision getCurrentResolvedModuleRevision() {
+ return currentResolvedModuleRevision;
+ }
+}
diff --git a/src/java/org/apache/ivy/core/resolve/ResolveEngine.java b/src/java/org/apache/ivy/core/resolve/ResolveEngine.java
new file mode 100644
index 0000000..bb3bc95
--- /dev/null
+++ b/src/java/org/apache/ivy/core/resolve/ResolveEngine.java
@@ -0,0 +1,1219 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.resolve;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.net.URL;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+
+import org.apache.ivy.Ivy;
+import org.apache.ivy.core.IvyContext;
+import org.apache.ivy.core.LogOptions;
+import org.apache.ivy.core.cache.ArtifactOrigin;
+import org.apache.ivy.core.cache.ResolutionCacheManager;
+import org.apache.ivy.core.event.EventManager;
+import org.apache.ivy.core.event.download.PrepareDownloadEvent;
+import org.apache.ivy.core.event.resolve.EndResolveEvent;
+import org.apache.ivy.core.event.resolve.StartResolveEvent;
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.Configuration;
+import org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleId;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.report.ArtifactDownloadReport;
+import org.apache.ivy.core.report.ConfigurationResolveReport;
+import org.apache.ivy.core.report.DownloadReport;
+import org.apache.ivy.core.report.DownloadStatus;
+import org.apache.ivy.core.report.ResolveReport;
+import org.apache.ivy.core.resolve.IvyNodeEviction.EvictionData;
+import org.apache.ivy.core.settings.IvySettings;
+import org.apache.ivy.core.sort.SortEngine;
+import org.apache.ivy.core.sort.SortOptions;
+import org.apache.ivy.plugins.conflict.ConflictManager;
+import org.apache.ivy.plugins.parser.ModuleDescriptorParser;
+import org.apache.ivy.plugins.parser.ModuleDescriptorParserRegistry;
+import org.apache.ivy.plugins.repository.url.URLResource;
+import org.apache.ivy.plugins.resolver.DependencyResolver;
+import org.apache.ivy.plugins.version.VersionMatcher;
+import org.apache.ivy.util.Message;
+import org.apache.ivy.util.filter.Filter;
+
+/**
+ * The resolve engine which is the core of the dependency resolution mechanism used in Ivy. It
+ * features several resolve methods, some very simple, like {@link #resolve(File)} and
+ * {@link #resolve(URL)} which allow to simply resolve dependencies of a single module descriptor,
+ * or more complete one, like the {@link #resolve(ModuleDescriptor, ResolveOptions)} which allows to
+ * provide options to the resolution engine.
+ *
+ * @see ResolveOptions
+ */
+public class ResolveEngine {
+ private ResolveEngineSettings settings;
+
+ private EventManager eventManager;
+
+ private SortEngine sortEngine;
+
+ private Set fetchedSet = new HashSet();
+
+ private DependencyResolver dictatorResolver;
+
+ /**
+ * Constructs a ResolveEngine.
+ *
+ * @param settings
+ * the settings to use to configure the engine. Must not be null.
+ * @param eventManager
+ * the event manager to use to send events about the resolution process. Must not be
+ * null.
+ * @param sortEngine
+ * the sort engine to use to sort modules before producing the dependency resolution
+ * report. Must not be null.
+ */
+ public ResolveEngine(ResolveEngineSettings settings, EventManager eventManager,
+ SortEngine sortEngine) {
+ this.settings = settings;
+ this.eventManager = eventManager;
+ this.sortEngine = sortEngine;
+ }
+
+ /**
+ * Returns the currently configured dictator resolver, which when non null is used in place of
+ * any specified resolver in the {@link IvySettings}
+ *
+ * @return the currently configured dictator resolver, may be null.
+ */
+ public DependencyResolver getDictatorResolver() {
+ return dictatorResolver;
+ }
+
+ /**
+ * Sets a dictator resolver, which is used in place of regular dependency resolver for
+ * subsequent dependency resolution by this engine.
+ *
+ * @param dictatorResolver
+ * the dictator resolver to use in this engine, null if regular settings should used
+ */
+ public void setDictatorResolver(DependencyResolver dictatorResolver) {
+ this.dictatorResolver = dictatorResolver;
+ settings.setDictatorResolver(dictatorResolver);
+ }
+
+ public ResolveReport resolve(File ivySource) throws ParseException, IOException {
+ return resolve(ivySource.toURI().toURL());
+ }
+
+ public ResolveReport resolve(URL ivySource) throws ParseException, IOException {
+ return resolve(ivySource, new ResolveOptions());
+ }
+
+ /**
+ * Resolves the module identified by the given mrid with its dependencies if transitive is set
+ * to true.
+ */
+ public ResolveReport resolve(final ModuleRevisionId mrid, ResolveOptions options,
+ boolean changing) throws ParseException, IOException {
+ DefaultModuleDescriptor md;
+ ResolveOptions optionsToUse = new ResolveOptions(options);
+
+ if (options.useSpecialConfs()) {
+ // create new resolve options because this is a different resolve than the real resolve
+ // (which will be a resolve of a newCallerInstance module)
+ ResolvedModuleRevision rmr = findModule(mrid, new ResolveOptions(options));
+ if (rmr == null) {
+ Message.verbose("module not found " + mrid);
+
+ // we will continue the resolve anyway to get a nice error message back
+ // to the user, however reduce the amount of logging in this case
+ optionsToUse.setLog(LogOptions.LOG_DOWNLOAD_ONLY);
+ md = DefaultModuleDescriptor.newCallerInstance(mrid, new String[] {"default"},
+ options.isTransitive(), changing);
+ } else {
+ String[] confs = options.getConfs(rmr.getDescriptor());
+ md = DefaultModuleDescriptor.newCallerInstance(
+ ModuleRevisionId.newInstance(mrid, rmr.getId().getRevision()), confs,
+ options.isTransitive(), changing);
+ }
+ } else {
+ md = DefaultModuleDescriptor.newCallerInstance(mrid, options.getConfs(),
+ options.isTransitive(), changing);
+ }
+
+ return resolve(md, optionsToUse);
+ }
+
+ /**
+ * Resolve dependencies of a module described by an ivy file.
+ */
+ public ResolveReport resolve(URL ivySource, ResolveOptions options) throws ParseException,
+ IOException {
+ URLResource res = new URLResource(ivySource);
+ ModuleDescriptorParser parser = ModuleDescriptorParserRegistry.getInstance().getParser(res);
+ Message.verbose("using " + parser + " to parse " + ivySource);
+ ModuleDescriptor md = parser.parseDescriptor(settings, ivySource, options.isValidate());
+ String revision = options.getRevision();
+ if (revision == null && md.getResolvedModuleRevisionId().getRevision() == null) {
+ revision = Ivy.getWorkingRevision();
+ }
+ if (revision != null) {
+ md.setResolvedModuleRevisionId(ModuleRevisionId.newInstance(md.getModuleRevisionId(),
+ revision));
+ }
+
+ return resolve(md, options);
+ }
+
+ /**
+ * Resolve dependencies of a module described by a module descriptor.
+ */
+ public ResolveReport resolve(ModuleDescriptor md, ResolveOptions options)
+ throws ParseException, IOException {
+ DependencyResolver oldDictator = getDictatorResolver();
+ IvyContext context = IvyContext.getContext();
+ try {
+ String[] confs = options.getConfs(md);
+ options.setConfs(confs);
+
+ if (options.getResolveId() == null) {
+ options.setResolveId(ResolveOptions.getDefaultResolveId(md));
+ }
+
+ eventManager.fireIvyEvent(new StartResolveEvent(md, confs));
+
+ long start = System.currentTimeMillis();
+ if (ResolveOptions.LOG_DEFAULT.equals(options.getLog())) {
+ Message.info(":: resolving dependencies :: " + md.getResolvedModuleRevisionId()
+ + (options.isTransitive() ? "" : " [not transitive]"));
+ Message.info("\tconfs: " + Arrays.asList(confs));
+ } else {
+ Message.verbose(":: resolving dependencies :: " + md.getResolvedModuleRevisionId()
+ + (options.isTransitive() ? "" : " [not transitive]"));
+ Message.verbose("\tconfs: " + Arrays.asList(confs));
+ }
+ Message.verbose("\tvalidate = " + options.isValidate());
+ Message.verbose("\trefresh = " + options.isRefresh());
+
+ ResolveReport report = new ResolveReport(md, options.getResolveId());
+
+ ResolveData data = new ResolveData(this, options);
+ context.setResolveData(data);
+
+ // resolve dependencies
+ IvyNode[] dependencies = getDependencies(md, options, report);
+ report.setDependencies(Arrays.asList(dependencies), options.getArtifactFilter());
+
+ if (options.getCheckIfChanged()) {
+ report.checkIfChanged();
+ }
+
+ // produce resolved ivy file and ivy properties in cache
+ ResolutionCacheManager cacheManager = settings.getResolutionCacheManager();
+ cacheManager.saveResolvedModuleDescriptor(md);
+
+ // we store the resolved dependencies revisions and statuses per asked dependency
+ // revision id, for direct dependencies only.
+ // this is used by the deliver task to resolve dynamic revisions to static ones
+ File ivyPropertiesInCache = cacheManager.getResolvedIvyPropertiesInCache(md
+ .getResolvedModuleRevisionId());
+ Properties props = new Properties();
+ if (dependencies.length > 0) {
+ Map forcedRevisions = new HashMap();
+ for (int i = 0; i < dependencies.length; i++) {
+ if (dependencies[i].getModuleRevision() != null
+ && dependencies[i].getModuleRevision().isForce()) {
+ forcedRevisions.put(dependencies[i].getModuleId(),
+ dependencies[i].getResolvedId());
+ }
+ }
+
+ IvyNode root = dependencies[0].getRoot();
+
+ // <ModuleId,IvyNode>();
+ Map topLevelDeps = new HashMap(); //
+ for (int i = 0; i < dependencies.length; i++) {
+ if (!dependencies[i].hasProblem()) {
+ DependencyDescriptor dd = dependencies[i].getDependencyDescriptor(root);
+ if (dd != null) {
+ ModuleId orgMod = dependencies[i].getModuleId();
+ topLevelDeps.put(orgMod, dependencies[i]);
+ }
+ }
+ }
+
+ for (int i = 0; i < dependencies.length; i++) {
+ if (!dependencies[i].hasProblem() && !dependencies[i].isCompletelyEvicted()) {
+ DependencyDescriptor dd = dependencies[i].getDependencyDescriptor(root);
+ if (dd == null) {
+ ModuleId mid = dependencies[i].getModuleId();
+ IvyNode tlDep = (IvyNode) topLevelDeps.get(mid);
+ if (tlDep != null) {
+ dd = tlDep.getDependencyDescriptor(root);
+ }
+ }
+ if (dd != null) {
+ ModuleRevisionId depResolvedId = dependencies[i].getResolvedId();
+ ModuleDescriptor depDescriptor = dependencies[i].getDescriptor();
+ ModuleRevisionId depRevisionId = dd.getDependencyRevisionId();
+ ModuleRevisionId forcedRevisionId = (ModuleRevisionId) forcedRevisions
+ .get(dependencies[i].getModuleId());
+
+ if (dependencies[i].getModuleRevision() != null
+ && dependencies[i].getModuleRevision().isForce()
+ && !depResolvedId.equals(depRevisionId)
+ && !settings.getVersionMatcher().isDynamic(depRevisionId)) {
+ // if we were forced to this revision and we
+ // are not a dynamic revision, reset to the
+ // asked revision
+ depResolvedId = depRevisionId;
+ depDescriptor = null;
+ }
+
+ if (depResolvedId == null) {
+ throw new NullPointerException("getResolvedId() is null for "
+ + dependencies[i].toString());
+ }
+ if (depRevisionId == null) {
+ throw new NullPointerException("getDependencyRevisionId() "
+ + "is null for " + dd.toString());
+ }
+ String rev = depResolvedId.getRevision();
+ String forcedRev = forcedRevisionId == null ? rev : forcedRevisionId
+ .getRevision();
+
+ // The evicted modules have no description, so we can't put the status
+ String status = depDescriptor == null ? "?" : depDescriptor.getStatus();
+ Message.debug("storing dependency " + depResolvedId + " in props");
+ props.put(depRevisionId.encodeToString(), rev + " " + status + " "
+ + forcedRev + " " + depResolvedId.getBranch());
+ }
+ }
+ }
+ }
+ FileOutputStream out = new FileOutputStream(ivyPropertiesInCache);
+ props.store(out, md.getResolvedModuleRevisionId() + " resolved revisions");
+ out.close();
+ Message.verbose("\tresolved ivy file produced in cache");
+
+ report.setResolveTime(System.currentTimeMillis() - start);
+
+ if (options.isDownload()) {
+ Message.verbose(":: downloading artifacts ::");
+
+ DownloadOptions downloadOptions = new DownloadOptions();
+ downloadOptions.setLog(options.getLog());
+ downloadArtifacts(report, options.getArtifactFilter(), downloadOptions);
+ }
+
+ if (options.isOutputReport()) {
+ outputReport(report, cacheManager, options);
+ }
+
+ Message.verbose("\tresolve done (" + report.getResolveTime() + "ms resolve - "
+ + report.getDownloadTime() + "ms download)");
+ Message.sumupProblems();
+
+ eventManager.fireIvyEvent(new EndResolveEvent(md, confs, report));
+ return report;
+ } catch (RuntimeException ex) {
+ Message.debug(ex);
+ Message.error(ex.getMessage());
+ Message.sumupProblems();
+ throw ex;
+ } finally {
+ context.setResolveData(null);
+ setDictatorResolver(oldDictator);
+ }
+ }
+
+ public void outputReport(ResolveReport report, ResolutionCacheManager cacheMgr,
+ ResolveOptions options) throws IOException {
+ if (ResolveOptions.LOG_DEFAULT.equals(options.getLog())) {
+ Message.info(":: resolution report :: resolve " + report.getResolveTime() + "ms"
+ + " :: artifacts dl " + report.getDownloadTime() + "ms");
+ } else {
+ Message.verbose(":: resolution report :: resolve " + report.getResolveTime() + "ms"
+ + " :: artifacts dl " + report.getDownloadTime() + "ms");
+ }
+ report.setProblemMessages(Message.getProblems());
+ // output report
+ report.output(settings.getReportOutputters(), cacheMgr, options);
+ }
+
+ public void downloadArtifacts(ResolveReport report, Filter artifactFilter,
+ DownloadOptions options) {
+ long start = System.currentTimeMillis();
+ IvyNode[] dependencies = (IvyNode[]) report.getDependencies().toArray(
+ new IvyNode[report.getDependencies().size()]);
+
+ eventManager.fireIvyEvent(new PrepareDownloadEvent((Artifact[]) report.getArtifacts()
+ .toArray(new Artifact[report.getArtifacts().size()])));
+
+ long totalSize = 0;
+ for (int i = 0; i < dependencies.length; i++) {
+ checkInterrupted();
+ // download artifacts required in all asked configurations
+ if (!dependencies[i].isCompletelyEvicted() && !dependencies[i].hasProblem()
+ && dependencies[i].getModuleRevision() != null) {
+ DependencyResolver resolver = dependencies[i].getModuleRevision()
+ .getArtifactResolver();
+ Artifact[] selectedArtifacts = dependencies[i].getSelectedArtifacts(artifactFilter);
+ DownloadReport dReport = resolver.download(selectedArtifacts, options);
+ ArtifactDownloadReport[] adrs = dReport.getArtifactsReports();
+ for (int j = 0; j < adrs.length; j++) {
+ if (adrs[j].getDownloadStatus() == DownloadStatus.FAILED) {
+ if (adrs[j].getArtifact().getExtraAttribute("ivy:merged") != null) {
+ Message.warn("\tmerged artifact not found: " + adrs[j].getArtifact()
+ + ". It was required in "
+ + adrs[j].getArtifact().getExtraAttribute("ivy:merged"));
+ } else {
+ Message.warn("\t" + adrs[j]);
+ resolver.reportFailure(adrs[j].getArtifact());
+ }
+ } else if (adrs[j].getDownloadStatus() == DownloadStatus.SUCCESSFUL) {
+ totalSize += adrs[j].getSize();
+ }
+ }
+ // update concerned reports
+ String[] dconfs = dependencies[i].getRootModuleConfigurations();
+ for (int j = 0; j < dconfs.length; j++) {
+ // the report itself is responsible to take into account only
+ // artifacts required in its corresponding configuration
+ // (as described by the Dependency object)
+ if (dependencies[i].isEvicted(dconfs[j])
+ || dependencies[i].isBlacklisted(dconfs[j])) {
+ report.getConfigurationReport(dconfs[j]).addDependency(dependencies[i]);
+ } else {
+ report.getConfigurationReport(dconfs[j]).addDependency(dependencies[i],
+ dReport);
+ }
+ }
+ }
+ }
+ report.setDownloadTime(System.currentTimeMillis() - start);
+ report.setDownloadSize(totalSize);
+ }
+
+ /**
+ * Download an artifact to the cache. Not used internally, useful especially for IDE plugins
+ * needing to download artifact one by one (for source or javadoc artifact, for instance).
+ * <p>
+ * Downloaded artifact file can be accessed using {@link ArtifactDownloadReport#getLocalFile()}.
+ * </p>
+ * <p>
+ * It is possible to track the progression of the download using classical ivy progress
+ * monitoring feature (see addTransferListener).
+ * </p>
+ *
+ * @param artifact
+ * the artifact to download
+ * @return a report concerning the download
+ * @see #download(ArtifactOrigin, DownloadOptions)
+ */
+ public ArtifactDownloadReport download(Artifact artifact, DownloadOptions options) {
+ DependencyResolver resolver = settings.getResolver(artifact.getModuleRevisionId());
+ DownloadReport r = resolver.download(new Artifact[] {artifact}, options);
+ return r.getArtifactReport(artifact);
+ }
+
+ /**
+ * Locates an artifact in dependency resolvers, and return its location if it can be located and
+ * actually exists, or an unknown {@link ArtifactOrigin} in other cases.
+ *
+ * @param artifact
+ * the artifact to locate.
+ * @return the artifact location, should be tested with
+ * {@link ArtifactOrigin#isUnknown(ArtifactOrigin)} to check if the artifact has
+ * actually been located.
+ */
+ public ArtifactOrigin locate(Artifact artifact) {
+ DependencyResolver resolver = settings.getResolver(artifact.getModuleRevisionId());
+ return resolver.locate(artifact);
+ }
+
+ /**
+ * Materialize an artifact already located.
+ * <p>
+ * Not used internally, useful especially for IDE plugins needing to download artifact one by
+ * one (for source or javadoc artifact, for instance).
+ * </p>
+ * <p>
+ * Materialized artifact file can be accessed using
+ * {@link ArtifactDownloadReport#getLocalFile()}.
+ * </p>
+ * <p>
+ * It is possible to track the progression of the download using classical ivy progress
+ * monitoring feature (see addTransferListener).
+ * </p>
+ *
+ * @param origin
+ * the artifact origin to materialize
+ * @return a report concerning the download
+ * @see #download(Artifact, DownloadOptions)
+ * @see #locate(Artifact)
+ */
+ public ArtifactDownloadReport download(ArtifactOrigin origin, DownloadOptions options) {
+ DependencyResolver resolver = settings.getResolver(origin.getArtifact()
+ .getModuleRevisionId());
+ return resolver.download(origin, options);
+ }
+
+ /**
+ * Resolve the dependencies of a module without downloading corresponding artifacts. The module
+ * to resolve is given by its ivy file URL. This method requires appropriate configuration of
+ * the ivy instance, especially resolvers.
+ *
+ * @param ivySource
+ * url of the ivy file to use for dependency resolving
+ * @param confs
+ * an array of configuration names to resolve - must not be null nor empty
+ * @param getCache
+ * the cache to use - default cache is used if null
+ * @param date
+ * the date to which resolution must be done - may be null
+ * @return an array of the resolved dependencies
+ * @throws ParseException
+ * if a parsing problem occurred in the ivy file
+ * @throws IOException
+ * if an IO problem was raised during ivy file parsing
+ */
+ public IvyNode[] getDependencies(URL ivySource, ResolveOptions options) throws ParseException,
+ IOException {
+ return getDependencies(
+ ModuleDescriptorParserRegistry.getInstance().parseDescriptor(settings, ivySource,
+ options.isValidate()), options, null);
+ }
+
+ /**
+ * Resolve the dependencies of a module without downloading corresponding artifacts. The module
+ * to resolve is given by its module descriptor. This method requires appropriate configuration
+ * of the ivy instance, especially resolvers.
+ * <p>
+ * The <code>IvyNode</code>s are ordered from the most dependent to the less dependent, so that
+ * an IvyNode is always found in the list after all IvyNode depending directly on it.
+ *
+ * @param md
+ * the descriptor of the module for which we want to get dependencies - must not be
+ * null
+ * @param options
+ * the resolve options to use to resolve the dependencies
+ * @param report
+ * a resolve report to fill during resolution - may be null
+ * @return an array of the resolved Dependencies
+ */
+ public IvyNode[] getDependencies(ModuleDescriptor md, ResolveOptions options,
+ ResolveReport report) {
+ // check parameters
+ if (md == null) {
+ throw new NullPointerException("module descriptor must not be null");
+ }
+ String[] confs = options.getConfs(md);
+ Collection missingConfs = new ArrayList();
+ for (int i = 0; i < confs.length; i++) {
+ if (confs[i] == null) {
+ throw new NullPointerException("null conf not allowed: confs where: "
+ + Arrays.asList(confs));
+ }
+
+ if (md.getConfiguration(confs[i]) == null) {
+ missingConfs.add(" '" + confs[i] + "' ");
+ }
+ }
+ if (!missingConfs.isEmpty()) {
+ throw new IllegalArgumentException("requested configuration"
+ + (missingConfs.size() > 1 ? "s" : "") + " not found in "
+ + md.getModuleRevisionId() + ": " + missingConfs);
+ }
+
+ IvyContext context = IvyContext.pushNewCopyContext();
+ try {
+ options.setConfs(confs);
+
+ Date reportDate = new Date();
+ ResolveData data = context.getResolveData();
+ if (data == null) {
+ data = new ResolveData(this, options);
+ context.setResolveData(data);
+ }
+ IvyNode rootNode = new IvyNode(data, md);
+
+ for (int i = 0; i < confs.length; i++) {
+ Message.verbose("resolving dependencies for configuration '" + confs[i] + "'");
+ // for each configuration we clear the cache of what's been fetched
+ fetchedSet.clear();
+
+ ConfigurationResolveReport confReport = null;
+ if (report != null) {
+ confReport = report.getConfigurationReport(confs[i]);
+ if (confReport == null) {
+ confReport = new ConfigurationResolveReport(this, md, confs[i], reportDate,
+ options);
+ report.addReport(confs[i], confReport);
+ }
+ }
+ // we reuse the same resolve data with a new report for each conf
+ data.setReport(confReport);
+
+ // update the root module conf we are about to fetch
+ VisitNode root = new VisitNode(data, rootNode, null, confs[i], null);
+ root.setRequestedConf(confs[i]);
+ rootNode.updateConfsToFetch(Collections.singleton(confs[i]));
+
+ // go fetch !
+ boolean fetched = false;
+ while (!fetched) {
+ try {
+ fetchDependencies(root, confs[i], false);
+ fetched = true;
+ } catch (RestartResolveProcess restart) {
+ Message.verbose("====================================================");
+ Message.verbose("= RESTARTING RESOLVE PROCESS");
+ Message.verbose("= " + restart.getMessage());
+ Message.verbose("====================================================");
+ fetchedSet.clear();
+ }
+ }
+
+ // clean data
+ for (Iterator iter = data.getNodes().iterator(); iter.hasNext();) {
+ IvyNode dep = (IvyNode) iter.next();
+ dep.clean();
+ }
+ }
+
+ // prune and reverse sort fectched dependencies
+ Collection nodes = data.getNodes();
+ // use a Set to avoid duplicates, linked to preserve order
+ Collection dependencies = new LinkedHashSet(nodes.size());
+ for (Iterator iter = nodes.iterator(); iter.hasNext();) {
+ IvyNode node = (IvyNode) iter.next();
+ if (node != null && !node.isRoot() && !node.isCompletelyBlacklisted()) {
+ dependencies.add(node);
+ }
+ }
+ List sortedDependencies = sortEngine.sortNodes(dependencies, SortOptions.SILENT);
+ Collections.reverse(sortedDependencies);
+
+ handleTransiviteEviction(md, confs, data, sortedDependencies);
+
+ return (IvyNode[]) dependencies.toArray(new IvyNode[dependencies.size()]);
+ } finally {
+ IvyContext.popContext();
+ }
+ }
+
+ private void handleTransiviteEviction(ModuleDescriptor md, String[] confs, ResolveData data,
+ List sortedDependencies) {
+ // handle transitive eviction now:
+ // if a module has been evicted then all its dependencies required only by it should be
+ // evicted too. Since nodes are now sorted from the more dependent to the less one, we
+ // can traverse the list and check only the direct parent and not all the ancestors
+ for (ListIterator iter = sortedDependencies.listIterator(); iter.hasNext();) {
+ IvyNode node = (IvyNode) iter.next();
+ if (!node.isCompletelyEvicted()) {
+ for (int i = 0; i < confs.length; i++) {
+ IvyNodeCallers.Caller[] callers = node.getCallers(confs[i]);
+ if (settings.debugConflictResolution()) {
+ Message.debug("checking if " + node.getId()
+ + " is transitively evicted in " + confs[i]);
+ }
+ boolean allEvicted = callers.length > 0;
+ for (int j = 0; j < callers.length; j++) {
+ if (callers[j].getModuleRevisionId().equals(md.getModuleRevisionId())) {
+ // the caller is the root module itself, it can't be evicted
+ allEvicted = false;
+ break;
+ } else {
+ IvyNode callerNode = data.getNode(callers[j].getModuleRevisionId());
+ if (callerNode == null) {
+ Message.warn("ivy internal error: no node found for "
+ + callers[j].getModuleRevisionId() + ": looked in "
+ + data.getNodeIds() + " and root module id was "
+ + md.getModuleRevisionId());
+ } else if (!callerNode.isEvicted(confs[i])) {
+ allEvicted = false;
+ break;
+ } else {
+ if (settings.debugConflictResolution()) {
+ Message.debug("caller " + callerNode.getId() + " of "
+ + node.getId() + " is evicted");
+ }
+ }
+ }
+ }
+ if (allEvicted) {
+ Message.verbose("all callers are evicted for " + node + ": evicting too");
+ node.markEvicted(confs[i], null, null, null);
+ } else {
+ if (settings.debugConflictResolution()) {
+ Message.debug(node.getId()
+ + " isn't transitively evicted, at least one caller was"
+ + " not evicted");
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private void fetchDependencies(VisitNode node, String conf, boolean shouldBePublic) {
+ checkInterrupted();
+ long start = System.currentTimeMillis();
+ if (node.getParent() != null) {
+ Message.verbose("== resolving dependencies " + node.getParent().getId() + "->"
+ + node.getId() + " [" + node.getParentConf() + "->" + conf + "]");
+ } else {
+ Message.verbose("== resolving dependencies for " + node.getId() + " [" + conf + "]");
+ }
+ ResolveData data = node.getNode().getData();
+ VisitNode parentVisitNode = data.getCurrentVisitNode();
+
+ data.setCurrentVisitNode(node);
+ DependencyDescriptor dd = node.getDependencyDescriptor();
+ VersionMatcher versionMatcher = node.getNode().getData().getSettings().getVersionMatcher();
+ if (dd != null
+ && !(node.getRoot() == node.getParent() && versionMatcher.isDynamic(dd
+ .getDependencyRevisionId()))) {
+ /*
+ * we don't resolve conflicts before loading data for direct dependencies on dynamic
+ * revisions, so that direct dynamic revisions are always resolved, which is mandatory
+ * for proper replacement of dynamic revisions during 'deliver'
+ */
+ resolveConflict(node, conf);
+ }
+
+ if (node.loadData(conf, shouldBePublic)) {
+ // we resolve conflict again now that we have all information loaded
+ // indeed in some cases conflict manager need more information than just asked
+ // dependency to take the decision
+ resolveConflict(node, conf);
+ if (!node.isEvicted() && !node.isCircular()) {
+ String[] confs = node.getRealConfs(conf);
+ for (int i = 0; i < confs.length; i++) {
+ doFetchDependencies(node, confs[i]);
+ }
+ }
+ } else if (!node.hasProblem()) {
+ // the node has not been loaded but hasn't problem: it was already loaded
+ // => we just have to update its dependencies data
+ if (!node.isEvicted() && !node.isCircular()) {
+ String[] confs = node.getRealConfs(conf);
+ for (int i = 0; i < confs.length; i++) {
+ doFetchDependencies(node, confs[i]);
+ }
+ }
+ }
+ if (node.isEvicted()) {
+ // update selected nodes with confs asked in evicted one
+ EvictionData ed = node.getEvictedData();
+ if (ed.getSelected() != null) {
+ for (Iterator iter = ed.getSelected().iterator(); iter.hasNext();) {
+ IvyNode selected = (IvyNode) iter.next();
+ if (!selected.isLoaded()) {
+ // the node is not yet loaded, we can simply update its set of
+ // configurations to fetch
+ selected.updateConfsToFetch(Collections.singleton(conf));
+ } else {
+ // the node has already been loaded, we must fetch its dependencies in the
+ // required conf
+ fetchDependencies(node.gotoNode(selected), conf, true);
+ }
+ }
+ }
+ }
+ if (settings.debugConflictResolution()) {
+ Message.debug(node.getId() + " => dependencies resolved in " + conf + " ("
+ + (System.currentTimeMillis() - start) + "ms)");
+ }
+ data.setCurrentVisitNode(parentVisitNode);
+ }
+
+ private void doFetchDependencies(VisitNode node, String conf) {
+ Configuration c = node.getConfiguration(conf);
+ if (c == null) {
+ if (!node.isConfRequiredByMergedUsageOnly(conf)) {
+ Message.warn("configuration not found '" + conf + "' in " + node.getResolvedId()
+ + ": ignoring");
+ if (node.getParent() != null) {
+ Message.warn("it was required from " + node.getParent().getResolvedId());
+ }
+ }
+ return;
+ }
+ // we handle the case where the asked configuration extends others:
+ // we have to first fetch the extended configurations
+
+ // first we check if this is the actual requested conf (not an extended one)
+ boolean requestedConfSet = false;
+ if (node.getRequestedConf() == null) {
+ node.setRequestedConf(conf);
+ requestedConfSet = true;
+ }
+ // now let's recurse in extended confs
+ String[] extendedConfs = c.getExtends();
+ if (extendedConfs.length > 0) {
+ node.updateConfsToFetch(Arrays.asList(extendedConfs));
+ }
+ for (int i = 0; i < extendedConfs.length; i++) {
+ fetchDependencies(node, extendedConfs[i], false);
+ }
+
+ // now we can actually resolve this configuration dependencies
+ if (!isDependenciesFetched(node.getNode(), conf) && node.isTransitive()) {
+ Collection/* <VisitNode> */dependencies = node.getDependencies(conf);
+ for (Iterator iter = dependencies.iterator(); iter.hasNext();) {
+ VisitNode dep = (VisitNode) iter.next();
+ dep.useRealNode(); // the node may have been resolved to another real one while
+ // resolving other deps
+ String[] confs = dep.getRequiredConfigurations(node, conf);
+ for (int i = 0; i < confs.length; i++) {
+ fetchDependencies(dep, confs[i], true);
+ }
+ if (!dep.isEvicted() && !dep.hasProblem()) {
+ // if there are still confs to fetch (usually because they have
+ // been updated when evicting another module), we fetch them now
+ confs = dep.getConfsToFetch();
+ for (int i = 0; i < confs.length; i++) {
+ // shouldBeFixed=false to because some of those dependencies might
+ // be private when they were actually extending public conf.
+ // Should we keep two list of confs to fetch (private&public)?
+ // I don't think, visibility is already checked, and a change in the
+ // configuration between version might anyway have worse problems.
+ fetchDependencies(dep, confs[i], false);
+ }
+ }
+ }
+ markDependenciesFetched(node.getNode(), conf);
+ }
+ // we have finiched with this configuration, if it was the original requested conf
+ // we can clean it now
+ if (requestedConfSet) {
+ node.setRequestedConf(null);
+ }
+
+ }
+
+ /**
+ * Returns true if we've already fetched the dependencies for this node and configuration
+ *
+ * @param node
+ * node to check
+ * @param conf
+ * configuration to check
+ * @return true if we've already fetched this dependency
+ */
+ private boolean isDependenciesFetched(IvyNode node, String conf) {
+ String key = getDependenciesFetchedKey(node, conf);
+ return fetchedSet.contains(key);
+ }
+
+ private void markDependenciesFetched(IvyNode node, String conf) {
+ String key = getDependenciesFetchedKey(node, conf);
+ fetchedSet.add(key);
+ }
+
+ private String getDependenciesFetchedKey(IvyNode node, String conf) {
+ ModuleRevisionId moduleRevisionId = node.getResolvedId();
+ String key = moduleRevisionId.getOrganisation() + "|" + moduleRevisionId.getName() + "|"
+ + moduleRevisionId.getRevision() + "|" + conf;
+ return key;
+ }
+
+ private void resolveConflict(VisitNode node, String conf) {
+ resolveConflict(node, node.getParent(), conf, Collections.EMPTY_SET);
+ }
+
+ /**
+ * Resolves conflict for the given node in the given ancestor. This method do conflict
+ * resolution in ancestor parents recursively, unless not necessary.
+ *
+ * @param node
+ * the node for which conflict resolution should be done
+ * @param ancestor
+ * the ancestor in which the conflict resolution should be done
+ * @param toevict
+ * a collection of IvyNode to evict (as computed by conflict resolution in
+ * descendants of ancestor)
+ * @return true if conflict resolution has been done, false it can't be done yet
+ */
+ private boolean resolveConflict(VisitNode node, VisitNode ancestor, String conf,
+ Collection toevict) {
+ if (ancestor == null || node == ancestor) {
+ return true;
+ }
+ // check if job is not already done
+ if (checkConflictSolvedEvicted(node, ancestor)) {
+ // job is done and node is evicted, nothing to do
+ return true;
+ }
+ boolean debugConflictResolution = settings.debugConflictResolution();
+ if (checkConflictSolvedSelected(node, ancestor)) {
+ // job is done and node is selected, nothing to do for this ancestor, but we still have
+ // to check higher levels, for which conflict resolution might have been impossible
+ // before
+ if (resolveConflict(node, ancestor.getParent(), conf, toevict)) {
+ // now that conflict resolution is ok in ancestors
+ // we just have to check if the node wasn't previously evicted in root ancestor
+ EvictionData evictionData = node.getEvictionDataInRoot(node.getRootModuleConf(),
+ ancestor);
+ if (evictionData != null) {
+ // node has been previously evicted in an ancestor: we mark it as evicted
+ if (debugConflictResolution) {
+ Message.debug(node + " was previously evicted in root module conf "
+ + node.getRootModuleConf());
+ }
+ node.markEvicted(evictionData);
+ if (debugConflictResolution) {
+ Message.debug("evicting " + node + " by " + evictionData);
+ }
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ // compute conflicts
+ Set resolvedNodes = ancestor.getNode().getResolvedNodes(node.getModuleId(),
+ node.getRootModuleConf());
+ resolvedNodes.addAll(ancestor.getNode().getPendingConflicts(node.getRootModuleConf(),
+ node.getModuleId()));
+ Collection conflicts = computeConflicts(node, ancestor, conf, toevict, resolvedNodes);
+
+ ConflictManager conflictManager = ancestor.getNode().getConflictManager(node.getModuleId());
+
+ Collection resolved = resolveConflicts(node, ancestor, conflicts, conflictManager);
+
+ if (resolved == null) {
+ if (debugConflictResolution) {
+ Message.debug("impossible to resolve conflicts for " + node + " in " + ancestor
+ + " yet");
+ Message.debug("setting all nodes as pending conflicts for later conflict"
+ + " resolution: " + conflicts);
+ }
+ ancestor.getNode().setPendingConflicts(node.getModuleId(), node.getRootModuleConf(),
+ conflicts);
+ return false;
+ }
+
+ if (debugConflictResolution) {
+ Message.debug("selected revisions for " + node + " in " + ancestor + ": " + resolved);
+ }
+ if (resolved.contains(node.getNode())) {
+ // node has been selected for the current parent
+
+ // handle previously selected nodes that are now evicted by this new node
+ toevict = resolvedNodes;
+ toevict.removeAll(resolved);
+
+ for (Iterator iter = toevict.iterator(); iter.hasNext();) {
+ IvyNode te = (IvyNode) iter.next();
+ te.markEvicted(node.getRootModuleConf(), ancestor.getNode(), conflictManager,
+ resolved);
+
+ if (debugConflictResolution) {
+ Message.debug("evicting " + te + " by "
+ + te.getEvictedData(node.getRootModuleConf()));
+ }
+ }
+
+ // it's very important to update resolved and evicted nodes BEFORE recompute parent call
+ // to allow it to recompute its resolved collection with correct data
+ // if necessary
+ ancestor.getNode().setResolvedNodes(node.getModuleId(), node.getRootModuleConf(),
+ resolved);
+
+ Collection evicted = new HashSet(ancestor.getNode().getEvictedNodes(node.getModuleId(),
+ node.getRootModuleConf()));
+ evicted.removeAll(resolved);
+ evicted.addAll(toevict);
+ ancestor.getNode().setEvictedNodes(node.getModuleId(), node.getRootModuleConf(),
+ evicted);
+ ancestor.getNode().setPendingConflicts(node.getModuleId(), node.getRootModuleConf(),
+ Collections.EMPTY_SET);
+
+ return resolveConflict(node, ancestor.getParent(), conf, toevict);
+ } else {
+ // node has been evicted for the current parent
+ if (resolved.isEmpty()) {
+ if (debugConflictResolution) {
+ Message.verbose("conflict manager '" + conflictManager
+ + "' evicted all revisions among " + conflicts);
+ }
+ }
+
+ // it's time to update parent resolved and evicted with what was found
+
+ Collection evicted = new HashSet(ancestor.getNode().getEvictedNodes(node.getModuleId(),
+ node.getRootModuleConf()));
+ toevict.removeAll(resolved);
+ evicted.removeAll(resolved);
+ evicted.addAll(toevict);
+ evicted.add(node.getNode());
+ ancestor.getNode().setEvictedNodes(node.getModuleId(), node.getRootModuleConf(),
+ evicted);
+ ancestor.getNode().setPendingConflicts(node.getModuleId(), node.getRootModuleConf(),
+ Collections.EMPTY_SET);
+
+ node.markEvicted(ancestor, conflictManager, resolved);
+ if (debugConflictResolution) {
+ Message.debug("evicting " + node + " by " + node.getEvictedData());
+ }
+
+ // if resolved changed we have to go up in the graph
+ Collection prevResolved = ancestor.getNode().getResolvedNodes(node.getModuleId(),
+ node.getRootModuleConf());
+ boolean solved = true;
+ if (!prevResolved.equals(resolved)) {
+ ancestor.getNode().setResolvedNodes(node.getModuleId(), node.getRootModuleConf(),
+ resolved);
+ for (Iterator iter = resolved.iterator(); iter.hasNext();) {
+ IvyNode sel = (IvyNode) iter.next();
+ if (!prevResolved.contains(sel)) {
+ solved &= resolveConflict(node.gotoNode(sel), ancestor.getParent(), conf,
+ toevict);
+ }
+ }
+ }
+ return solved;
+ }
+ }
+
+ private Collection resolveConflicts(VisitNode node, VisitNode ancestor, Collection conflicts,
+ ConflictManager conflictManager) {
+ if (node.getParent() != ancestor
+ // we are not handling the direct parent
+
+ && conflictManager == settings.getConflictManager(node.getModuleId())
+ // the conflict manager is the default one
+
+ && node.getParent().getNode()
+ .getResolvedNodes(node.getModuleId(), node.getRootModuleConf())
+ .equals(conflicts)
+ // there is no new conflict in this ancestor
+
+ ) {
+ // IVY-465 case
+ if (settings.debugConflictResolution()) {
+ Message.debug("no new conflicting revisions for " + node + " in " + ancestor + ": "
+ + conflicts);
+ }
+
+ return conflicts;
+ } else {
+ if (settings.debugConflictResolution()) {
+ Message.debug("found conflicting revisions for " + node + " in " + ancestor + ": "
+ + conflicts);
+ }
+
+ return conflictManager.resolveConflicts(ancestor.getNode(), conflicts);
+ }
+ }
+
+ /**
+ * Compute possible conflicts for a node, in the context of an ancestor (a node which has a
+ * dependency - direct or indirect - on the node for which conflicts should be computed.
+ *
+ * @param node
+ * the node for which conflicts should be computed
+ * @param ancestor
+ * the ancestor in which conflicts should be computed
+ * @param conf
+ * the configuration of the node in which conflicts should be computed
+ * @param toevict
+ * a collection of nodes which have been evicted during conflict resolution at lower
+ * level. It may be empty if no conflict resolution has occurred for this node yet,
+ * or if no node has been evicted.
+ * @param selectedNodes
+ * a collection of nodes selected during previous conflict resolution for the given
+ * node and ancestor. This collection is updated by this call, removing nodes which
+ * should be evicted.
+ * @return a collection of IvyNode which may be in conflict with the given node in the given
+ * ancestor. This collection always contain at least the given node.
+ */
+ private Collection computeConflicts(VisitNode node, VisitNode ancestor, String conf,
+ Collection toevict, Collection selectedNodes) {
+ Collection conflicts = new LinkedHashSet();
+ conflicts.add(node.getNode());
+ /*
+ * We first try to remove all evicted nodes from the collection of selected nodes to update
+ * this collection. If the collection changes, it means that it contained evicted nodes, and
+ * thus is not up to date.
+ */
+ boolean evictedInSelected = selectedNodes.removeAll(toevict);
+ /*
+ * Another case where we need to deeply compute selected nodes is when selectedNodes is
+ * empty (not computed yet) and we aren't in the context of the direct parent of the node.
+ */
+ if (evictedInSelected
+ || (selectedNodes.isEmpty() && !node.getParent().getNode()
+ .equals(ancestor.getNode()))) {
+ IvyContext context = IvyContext.getContext();
+ ResolveData data = context.getResolveData();
+ VisitNode oldVisitNode = data.getCurrentVisitNode();
+ data.setCurrentVisitNode(ancestor);
+ try {
+ // In this case we need to compute selected nodes again.
+ Collection deps = ancestor.getNode().getDependencies(node.getRootModuleConf(),
+ ancestor.getNode().getConfigurations(node.getRootModuleConf()),
+ ancestor.getRequestedConf());
+ for (Iterator iter = deps.iterator(); iter.hasNext();) {
+ IvyNode dep = (IvyNode) iter.next();
+ if (dep.getModuleId().equals(node.getModuleId())) {
+ conflicts.add(dep);
+ }
+ conflicts.addAll(dep.getResolvedNodes(node.getModuleId(),
+ node.getRootModuleConf()));
+ }
+ } finally {
+ data.setCurrentVisitNode(oldVisitNode);
+ }
+ } else if (selectedNodes.isEmpty()) {
+ /*
+ * No selected nodes at all yet, and we are in the context of the direct parent
+ * (otherwise previous block would have been reached). We can compute conflicts based on
+ * the parent direct dependencies in current root module conf.
+ */
+ VisitNode parent = node.getParent();
+ Collection parentDepIvyNodes = parent.getNode().getDependencies(
+ node.getRootModuleConf(),
+ parent.getNode().getConfigurations(node.getRootModuleConf()),
+ parent.getRequestedConf());
+ for (Iterator it = parentDepIvyNodes.iterator(); it.hasNext();) {
+ IvyNode parentDep = (IvyNode) it.next();
+ if (parentDep.getModuleId().equals(node.getModuleId())) {
+ conflicts.add(parentDep);
+ }
+ }
+ } else {
+ conflicts.addAll(selectedNodes);
+ }
+ return conflicts;
+ }
+
+ private boolean checkConflictSolvedSelected(VisitNode node, VisitNode ancestor) {
+ if (ancestor.getResolvedRevisions(node.getModuleId()).contains(node.getResolvedId())) {
+ // resolve conflict has already be done with node with the same id
+ if (settings.debugConflictResolution()) {
+ Message.debug("conflict resolution already done for " + node + " in " + ancestor);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ private boolean checkConflictSolvedEvicted(VisitNode node, VisitNode ancestor) {
+ if (ancestor.getEvictedRevisions(node.getModuleId()).contains(node.getResolvedId())) {
+ // resolve conflict has already be done with node with the same id
+ if (settings.debugConflictResolution()) {
+ Message.debug("conflict resolution already done for " + node + " in " + ancestor);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ public ResolvedModuleRevision findModule(ModuleRevisionId id, ResolveOptions options) {
+ DependencyResolver r = settings.getResolver(id);
+ if (r == null) {
+ throw new IllegalStateException("no resolver found for " + id.getModuleId());
+ }
+ DefaultModuleDescriptor md = DefaultModuleDescriptor.newCallerInstance(id,
+ new String[] {"*"}, false, false);
+
+ if (options.getResolveId() == null) {
+ options.setResolveId(ResolveOptions.getDefaultResolveId(md));
+ }
+
+ try {
+ return r.getDependency(new DefaultDependencyDescriptor(id, true), new ResolveData(this,
+ options, new ConfigurationResolveReport(this, md, "default", null, options)));
+ } catch (ParseException e) {
+ throw new RuntimeException("problem while parsing repository module descriptor for "
+ + id + ": " + e, e);
+ }
+ }
+
+ /**
+ * Mediates the given dependency descriptor according to given options.
+ * <p>
+ * The mediated dependency descriptor must return the actually requested module revision id when
+ * the method {@link DependencyDescriptor#getDependencyRevisionId()} is called.
+ * </p>
+ *
+ * @param dd
+ * the dependency descriptor for which the requested module revision id should be
+ * returned
+ * @param options
+ * the resolve options to use
+ * @return the mediated {@link DependencyDescriptor}.
+ */
+ public DependencyDescriptor mediate(DependencyDescriptor dd, ResolveOptions options) {
+ if (dd == null) {
+ return null;
+ }
+ String resolveMode = options.getResolveMode() == null ? settings.getResolveMode(dd
+ .getDependencyId()) : options.getResolveMode();
+ if (ResolveOptions.RESOLVEMODE_DYNAMIC.equals(resolveMode)
+ && !dd.getDynamicConstraintDependencyRevisionId().equals(
+ dd.getDependencyRevisionId())) {
+ // the dynamicRevId can contain a null branch, so make sure this
+ // has been replaced by the default branch (if any!)
+ return dd.clone(ModuleRevisionId.newInstance(dd
+ .getDynamicConstraintDependencyRevisionId(), dd
+ .getDynamicConstraintDependencyRevisionId().getRevision()));
+ } else {
+ return dd;
+ }
+ }
+
+ public EventManager getEventManager() {
+ return eventManager;
+ }
+
+ public ResolveEngineSettings getSettings() {
+ return settings;
+ }
+
+ public SortEngine getSortEngine() {
+ return sortEngine;
+ }
+
+ private void checkInterrupted() {
+ IvyContext.getContext().getIvy().checkInterrupted();
+ }
+
+}
diff --git a/src/java/org/apache/ivy/core/resolve/ResolveEngineSettings.java b/src/java/org/apache/ivy/core/resolve/ResolveEngineSettings.java
new file mode 100644
index 0000000..34ff9fa
--- /dev/null
+++ b/src/java/org/apache/ivy/core/resolve/ResolveEngineSettings.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.resolve;
+
+import org.apache.ivy.core.module.id.ModuleId;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.plugins.conflict.ConflictManager;
+import org.apache.ivy.plugins.report.ReportOutputter;
+import org.apache.ivy.plugins.resolver.DependencyResolver;
+import org.apache.ivy.plugins.resolver.ResolverSettings;
+
+public interface ResolveEngineSettings extends ResolverSettings {
+
+ void setDictatorResolver(DependencyResolver dictatorResolver);
+
+ boolean debugConflictResolution();
+
+ ReportOutputter[] getReportOutputters();
+
+ String getResolverName(ModuleRevisionId mid);
+
+ boolean logNotConvertedExclusionRule();
+
+ ConflictManager getConflictManager(ModuleId mid);
+
+ boolean logModuleWhenFound();
+
+ boolean logResolvedRevision();
+
+}
diff --git a/src/java/org/apache/ivy/core/resolve/ResolveOptions.java b/src/java/org/apache/ivy/core/resolve/ResolveOptions.java
new file mode 100644
index 0000000..9ffe43f
--- /dev/null
+++ b/src/java/org/apache/ivy/core/resolve/ResolveOptions.java
@@ -0,0 +1,312 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.resolve;
+
+import java.util.Date;
+
+import org.apache.ivy.core.LogOptions;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleId;
+import org.apache.ivy.util.ConfigurationUtils;
+import org.apache.ivy.util.filter.Filter;
+import org.apache.ivy.util.filter.FilterHelper;
+
+/**
+ * A set of options used during resolve related tasks
+ *
+ * @see ResolveEngine
+ */
+public class ResolveOptions extends LogOptions {
+
+ /**
+ * Default resolve mode, using default revision constraints in dependency descriptors.
+ */
+ public static final String RESOLVEMODE_DEFAULT = "default";
+
+ /**
+ * Dynamic resolve mode, using dynamic revision constraints in dependency descriptors.
+ */
+ public static final String RESOLVEMODE_DYNAMIC = "dynamic";
+
+ /**
+ * Array of all available resolve modes.
+ */
+ public static final String[] RESOLVEMODES = new String[] {RESOLVEMODE_DEFAULT,
+ RESOLVEMODE_DYNAMIC};
+
+ /**
+ * an array of configuration names to resolve - must not be null nor empty
+ */
+ private String[] confs = new String[] {"*"};
+
+ /**
+ * the revision of the module for which dependencies should be resolved. This revision is
+ * considered as the resolved revision of the module, unless it is null. If it is null, then a
+ * default revision is given if necessary (no revision found in ivy file)
+ */
+ private String revision = null;
+
+ /**
+ * the date for which the dependencies should be resolved. All obtained artifacts should have a
+ * publication date which is before or equal to the given date. The date can be null, in which
+ * case all artifacts will be considered
+ */
+ private Date date = null;
+
+ /**
+ * True if validation of module descriptors should done, false otherwise
+ */
+ private boolean validate = true;
+
+ /**
+ * True if only the cache should be used for resolve, false if a real resolve with dependency
+ * resolvers should be done
+ */
+ private boolean useCacheOnly = false;
+
+ /**
+ * True if the dependencies should be resolved transitively, false if only direct dependencies
+ * should be resolved
+ */
+ private boolean transitive = true;
+
+ /**
+ * True if the resolve should also download artifacts, false if only dependency resolution with
+ * module descriptors should be done
+ */
+ private boolean download = true;
+
+ /**
+ * True if a report of the resolve process should be output at the end of the process, false
+ * otherwise
+ */
+ private boolean outputReport = true;
+
+ /**
+ * A filter to use to avoid downloading all artifacts.
+ */
+ private Filter artifactFilter = FilterHelper.NO_FILTER;
+
+ /**
+ * The resolve mode to use. Should be one of {@link #RESOLVEMODES}, or <code>null</code> to use
+ * settings configured resolve mode.
+ */
+ private String resolveMode;
+
+ /**
+ * The id used to store the resolve information.
+ */
+ private String resolveId;
+
+ private boolean refresh;
+
+ /**
+ * True if the resolve should compare the new resolution against the previous report
+ **/
+ private boolean checkIfChanged = false;
+
+ public ResolveOptions() {
+ }
+
+ public ResolveOptions(ResolveOptions options) {
+ super(options);
+ confs = options.confs;
+ revision = options.revision;
+ date = options.date;
+ validate = options.validate;
+ refresh = options.refresh;
+ useCacheOnly = options.useCacheOnly;
+ transitive = options.transitive;
+ download = options.download;
+ outputReport = options.outputReport;
+ resolveMode = options.resolveMode;
+ artifactFilter = options.artifactFilter;
+ resolveId = options.resolveId;
+ checkIfChanged = options.checkIfChanged;
+ }
+
+ public Filter getArtifactFilter() {
+ return artifactFilter;
+ }
+
+ public ResolveOptions setArtifactFilter(Filter artifactFilter) {
+ this.artifactFilter = artifactFilter;
+ return this;
+ }
+
+ /**
+ * Returns the resolve mode to use, or <code>null</code> to use settings configured resolve
+ * mode.
+ *
+ * @return the resolve mode to use.
+ */
+ public String getResolveMode() {
+ return resolveMode;
+ }
+
+ public ResolveOptions setResolveMode(String resolveMode) {
+ this.resolveMode = resolveMode;
+ return this;
+ }
+
+ /**
+ * Indicates if the configurations use a special configuration * , *(private) or *(public). When
+ * special configurations are used, you must have the module descriptor in order to get the list
+ * of configurations.
+ *
+ * @see #getConfs()
+ * @see #getConfs(ModuleDescriptor)
+ */
+ public boolean useSpecialConfs() {
+ for (int i = 0; confs != null && i < confs.length; i++) {
+ if (confs[0].startsWith("*")) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @pre can only be called if useSpecialConfs()==false. When it is true, you have to provide a
+ * module desciptor so that configurations can be resolved.
+ * @see #getConfs(ModuleDescriptor)
+ */
+ public String[] getConfs() {
+ if (useSpecialConfs()) {
+ throw new AssertionError("ResolveOptions.getConfs() "
+ + "can not be used for options used special confs.");
+ }
+ return confs;
+ }
+
+ /**
+ * Get the aksed confs. Special confs (like *) use the moduleDescriptor to find the values *
+ *
+ * @param md
+ * Used to get the exact values for special confs.
+ * */
+ public String[] getConfs(ModuleDescriptor md) {
+ // TODO add isInline, in that case, replace * by *(public).
+ return ConfigurationUtils.replaceWildcards(confs, md);
+ }
+
+ public ResolveOptions setConfs(String[] confs) {
+ this.confs = confs;
+ return this;
+ }
+
+ public Date getDate() {
+ return date;
+ }
+
+ public ResolveOptions setDate(Date date) {
+ this.date = date;
+ return this;
+ }
+
+ public boolean isDownload() {
+ return download;
+ }
+
+ public ResolveOptions setDownload(boolean download) {
+ this.download = download;
+ return this;
+ }
+
+ public boolean isOutputReport() {
+ return outputReport;
+ }
+
+ public ResolveOptions setOutputReport(boolean outputReport) {
+ this.outputReport = outputReport;
+ return this;
+ }
+
+ public boolean isTransitive() {
+ return transitive;
+ }
+
+ public ResolveOptions setTransitive(boolean transitive) {
+ this.transitive = transitive;
+ return this;
+ }
+
+ public boolean isUseCacheOnly() {
+ return useCacheOnly;
+ }
+
+ public ResolveOptions setUseCacheOnly(boolean useCacheOnly) {
+ this.useCacheOnly = useCacheOnly;
+ return this;
+ }
+
+ public boolean isValidate() {
+ return validate;
+ }
+
+ public ResolveOptions setValidate(boolean validate) {
+ this.validate = validate;
+ return this;
+ }
+
+ public String getRevision() {
+ return revision;
+ }
+
+ public ResolveOptions setRevision(String revision) {
+ this.revision = revision;
+ return this;
+ }
+
+ public String getResolveId() {
+ return resolveId;
+ }
+
+ public ResolveOptions setResolveId(String resolveId) {
+ this.resolveId = resolveId;
+ return this;
+ }
+
+ public ResolveOptions setRefresh(boolean refresh) {
+ this.refresh = refresh;
+ return this;
+ }
+
+ public boolean isRefresh() {
+ return refresh;
+ }
+
+ public ResolveOptions setCheckIfChanged(boolean checkIfChanged) {
+ this.checkIfChanged = checkIfChanged;
+ return this;
+ }
+
+ public boolean getCheckIfChanged() {
+ return checkIfChanged;
+ }
+
+ public static String getDefaultResolveId(ModuleDescriptor md) {
+ ModuleId module = md.getModuleRevisionId().getModuleId();
+ return getDefaultResolveId(module);
+ }
+
+ public static String getDefaultResolveId(ModuleId moduleId) {
+ return moduleId.getOrganisation() + "-" + moduleId.getName();
+ }
+
+}
diff --git a/src/java/org/apache/ivy/core/resolve/ResolveProcessException.java b/src/java/org/apache/ivy/core/resolve/ResolveProcessException.java
new file mode 100644
index 0000000..07ccea8
--- /dev/null
+++ b/src/java/org/apache/ivy/core/resolve/ResolveProcessException.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.resolve;
+
+/**
+ * ResolveProcessException is an exception which is used to control the resolve process.
+ * <p>
+ * All {@link ResolveProcessException} subclasses have the ability to interrupt the resolve process
+ * when thrown while resolving dependencies, instead of only marking the module with a problem and
+ * continuing the resolve process as part of the best effort strategy during resolve process.
+ * </p>
+ * Some subclasses have even a stronger power over the resolve process, like
+ * {@link RestartResolveProcess} which orders to restart the resolve process at the start.
+ */
+public class ResolveProcessException extends RuntimeException {
+
+ public ResolveProcessException() {
+ }
+
+ public ResolveProcessException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ public ResolveProcessException(String message) {
+ super(message);
+ }
+
+ public ResolveProcessException(Throwable cause) {
+ super(cause);
+ }
+}
diff --git a/src/java/org/apache/ivy/core/resolve/ResolvedModuleRevision.java b/src/java/org/apache/ivy/core/resolve/ResolvedModuleRevision.java
new file mode 100644
index 0000000..e25b21f
--- /dev/null
+++ b/src/java/org/apache/ivy/core/resolve/ResolvedModuleRevision.java
@@ -0,0 +1,143 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.resolve;
+
+import java.util.Date;
+
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.report.MetadataArtifactDownloadReport;
+import org.apache.ivy.plugins.resolver.DependencyResolver;
+
+/**
+ * Represents a module revision provisioned on the local filesystem.
+ */
+public class ResolvedModuleRevision {
+
+ private DependencyResolver resolver;
+
+ private DependencyResolver artifactResolver;
+
+ private ModuleDescriptor descriptor;
+
+ private MetadataArtifactDownloadReport report;
+
+ private boolean force = false;
+
+ public ResolvedModuleRevision(DependencyResolver resolver, DependencyResolver artifactResolver,
+ ModuleDescriptor descriptor, MetadataArtifactDownloadReport report) {
+ this.resolver = resolver;
+ this.artifactResolver = artifactResolver;
+ this.descriptor = descriptor;
+ this.report = report;
+ }
+
+ public ResolvedModuleRevision(DependencyResolver resolver, DependencyResolver artifactResolver,
+ ModuleDescriptor descriptor, MetadataArtifactDownloadReport report, boolean force) {
+ this.resolver = resolver;
+ this.artifactResolver = artifactResolver;
+ this.descriptor = descriptor;
+ this.report = report;
+ this.force = force;
+ }
+
+ /**
+ * Returns the identifier of the resolved module.
+ *
+ * @return the identifier of the resolved module.
+ */
+ public ModuleRevisionId getId() {
+ return descriptor.getResolvedModuleRevisionId();
+ }
+
+ /**
+ * Returns the date of publication of the resolved module.
+ *
+ * @return the date of publication of the resolved module.
+ */
+ public Date getPublicationDate() {
+ return descriptor.getResolvedPublicationDate();
+ }
+
+ /**
+ * Returns the descriptor of the resolved module.
+ *
+ * @return the descriptor of the resolved module.
+ */
+ public ModuleDescriptor getDescriptor() {
+ return descriptor;
+ }
+
+ /**
+ * The resolver which resolved this ResolvedModuleRevision
+ *
+ * @return The resolver which resolved this ResolvedModuleRevision
+ */
+ public DependencyResolver getResolver() {
+ return resolver;
+ }
+
+ /**
+ * The resolver to use to download artifacts
+ *
+ * @return The resolver to use to download artifacts
+ */
+ public DependencyResolver getArtifactResolver() {
+ return artifactResolver;
+ }
+
+ /**
+ * Returns a report of the resolved module metadata artifact provisioning.
+ *
+ * @return a report of the resolved module metadata artifact provisioning.
+ */
+ public MetadataArtifactDownloadReport getReport() {
+ return report;
+ }
+
+ /**
+ * Returns <code>true</code> if this resolved module revision should be forced as the one being
+ * returned.
+ * <p>
+ * This is used as an indication for CompositeResolver, to know if they should continue to look
+ * for a better ResolvedModuleRevision if possible, or stop with this instance.
+ * </p>
+ *
+ * @return <code>true</code> if this resolved module revision should be forced as the one being
+ * returned.
+ */
+ public boolean isForce() {
+ return force;
+ }
+
+ public boolean equals(Object obj) {
+ if (!(obj instanceof ResolvedModuleRevision)) {
+ return false;
+ }
+ return ((ResolvedModuleRevision) obj).getId().equals(getId());
+ }
+
+ public int hashCode() {
+ return getId().hashCode();
+ }
+
+ public String toString() {
+ return getId().toString();
+ }
+
+}
diff --git a/src/java/org/apache/ivy/core/resolve/RestartResolveProcess.java b/src/java/org/apache/ivy/core/resolve/RestartResolveProcess.java
new file mode 100644
index 0000000..c074067
--- /dev/null
+++ b/src/java/org/apache/ivy/core/resolve/RestartResolveProcess.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.resolve;
+
+/**
+ * This RuntimeException is used during the resolve process to ask the engine to restart the resolve
+ * process.
+ * <p>
+ * Users of this feature should be very careful to make sure they handle a termination condition,
+ * since the resolve engine itself won't check the same exception is not thrown ad libitum
+ * </p>
+ */
+public class RestartResolveProcess extends ResolveProcessException {
+
+ public RestartResolveProcess(String message) {
+ super(message);
+ }
+
+}
diff --git a/src/java/org/apache/ivy/core/resolve/VisitData.java b/src/java/org/apache/ivy/core/resolve/VisitData.java
new file mode 100644
index 0000000..16a10ab
--- /dev/null
+++ b/src/java/org/apache/ivy/core/resolve/VisitData.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.resolve;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * This class is used to store data related to one node of the dependency graph visit. It stores
+ * both an {@link IvyNode} and related {@link VisitNode} objects. Indeed, during the visit of the
+ * graph, the algorithm can visit the same node from several parents, thus requiring several
+ * VisitNode.
+ */
+public class VisitData {
+ /**
+ * A node in the graph of module dependencies resolution
+ */
+ private IvyNode node;
+
+ /**
+ * The associated visit nodes, per rootModuleConf Note that the value is a List, because a node
+ * can be visited from several parents during the resolution process
+ */
+ private Map visitNodes = new HashMap(); // Map (String rootModuleConf -> List(VisitNode))
+
+ public VisitData(IvyNode node) {
+ this.node = node;
+ }
+
+ public void addVisitNode(VisitNode node) {
+ String rootModuleConf = node.getRootModuleConf();
+ getVisitNodes(rootModuleConf).add(node);
+ }
+
+ public List getVisitNodes(String rootModuleConf) {
+ List visits = (List) visitNodes.get(rootModuleConf);
+ if (visits == null) {
+ visits = new ArrayList();
+ visitNodes.put(rootModuleConf, visits);
+ }
+ return visits;
+ }
+
+ public IvyNode getNode() {
+ return node;
+ }
+
+ public void setNode(IvyNode node) {
+ this.node = node;
+ }
+
+ public void addVisitNodes(String rootModuleConf, List visitNodes) {
+ getVisitNodes(rootModuleConf).addAll(visitNodes);
+ }
+}
diff --git a/src/java/org/apache/ivy/core/resolve/VisitNode.java b/src/java/org/apache/ivy/core/resolve/VisitNode.java
new file mode 100644
index 0000000..1547a40
--- /dev/null
+++ b/src/java/org/apache/ivy/core/resolve/VisitNode.java
@@ -0,0 +1,519 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.resolve;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
+
+import org.apache.ivy.core.IvyContext;
+import org.apache.ivy.core.module.descriptor.Configuration;
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleId;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.resolve.IvyNodeEviction.EvictionData;
+import org.apache.ivy.plugins.conflict.ConflictManager;
+import org.apache.ivy.util.Checks;
+
+/**
+ * A visit node is an object used to represent one visit from one parent on an {@link IvyNode} of
+ * the dependency graph. During dependency resolution, the {@link ResolveEngine} visits nodes of the
+ * depency graph following the dependencies, thus the same node can be visited several times, if it
+ * is requested from several module. In this case you will have one VisitNode per parent and per
+ * root module configuration. Thus VisitNode stores data specific to the visit:
+ * <ul>
+ * <li>parent</li>
+ * the node from which the visit is occuring
+ * <li>parentConf</li>
+ * the configuration of the parent in which this node is visited
+ * <li>rootModuleConf</li>
+ * the configuration of the root module which is currently resolved
+ * </ul>
+ */
+public class VisitNode {
+ /**
+ * The node which is currently visited
+ */
+ private IvyNode node;
+
+ /**
+ * Represents the current parent of the node during ivy visit of dependency graph.
+ */
+ private VisitNode parent = null;
+
+ /**
+ * The root node of the current visit It is null until it is required, see getRoot
+ */
+ private VisitNode root = null;
+
+ /**
+ * Direct path from root to this node. Note that the colleciton is ordered but is not a list
+ * implementation This collection is null until it is required, see getPath
+ */
+ private Collection path = null; // Collection(VisitNode)
+
+ /**
+ * The configuration of the parent module in the current visit
+ */
+ private String parentConf = null;
+
+ /**
+ * The configuration requested by the parent Note that this is the actual conf requested by the
+ * parent, not a configuration extended by the requested conf which actually trigger the node
+ * visit
+ */
+ private String requestedConf;
+
+ /**
+ * The root configuration which is currently visited
+ */
+ private String rootModuleConf;
+
+ /**
+ * Shared ResolveData instance, which can be used to get info on the current resolve process
+ */
+ private ResolveData data;
+
+ /**
+ * Boolean.TRUE if a node with a same module id as the one visited has already been visited in
+ * the current path. null if not computed yet Boolean.FALSE otherwise
+ */
+ private Boolean isCircular;
+
+ /**
+ * IvyNode usage information to update when visiting the underlying IvyNode. This is usually the
+ * main IvyNodeUsage of the underlying node, except when we are visiting it coming from an
+ * evicted node replaced by the other one.
+ */
+ private IvyNodeUsage usage;
+
+ public VisitNode(ResolveData data, IvyNode node, VisitNode parent, String rootModuleConf,
+ String parentConf) {
+ this(data, node, parent, rootModuleConf, parentConf, null);
+ }
+
+ public VisitNode(ResolveData data, IvyNode node, VisitNode parent, String rootModuleConf,
+ String parentConf, IvyNodeUsage usage) {
+ Checks.checkNotNull(data, "data");
+ Checks.checkNotNull(node, "node");
+ Checks.checkNotNull(rootModuleConf, "rootModuleConf");
+
+ this.data = data;
+ this.node = node;
+ this.parent = parent;
+ this.rootModuleConf = rootModuleConf;
+ this.parentConf = parentConf;
+ this.usage = usage;
+
+ this.data.register(this);
+ }
+
+ public IvyNode getNode() {
+ return node;
+ }
+
+ /**
+ * @return Returns the configuration requested by the parent
+ */
+ public String getRequestedConf() {
+ return requestedConf;
+ }
+
+ public void setRequestedConf(String requestedConf) {
+ this.requestedConf = requestedConf;
+ }
+
+ public VisitNode getParent() {
+ return parent;
+ }
+
+ public VisitNode getRoot() {
+ if (root == null) {
+ root = computeRoot();
+ }
+ return root;
+ }
+
+ /**
+ * Get an ordered collection with the nodes from the root to this node
+ *
+ * @return
+ */
+ public Collection/* <VisitNode> */getPath() {
+ if (path == null) {
+ path = computePath();
+ }
+ return path;
+ }
+
+ private Collection/* <VisitNode> */computePath() {
+ if (parent != null) {
+ Collection p = new LinkedHashSet(parent.getPath());
+ p.add(this);
+ return p;
+ } else {
+ return Collections.singletonList(this);
+ }
+ }
+
+ private VisitNode computeRoot() {
+ if (node.isRoot()) {
+ return this;
+ } else if (parent != null) {
+ return parent.getRoot();
+ } else {
+ return null;
+ }
+ }
+
+ public String getParentConf() {
+ return parentConf;
+ }
+
+ public void setParentConf(String parentConf) {
+ this.parentConf = parentConf;
+ }
+
+ public String getRootModuleConf() {
+ return rootModuleConf;
+ }
+
+ public static VisitNode getRoot(VisitNode parent) {
+ VisitNode root = parent;
+ Collection path = new HashSet();
+ path.add(root);
+ while (root.getParent() != null && !root.getNode().isRoot()) {
+ if (path.contains(root.getParent())) {
+ return root;
+ }
+ root = root.getParent();
+ path.add(root);
+ }
+ return root;
+ }
+
+ /**
+ * Returns true if the current dependency descriptor is transitive and the parent configuration
+ * is transitive. Otherwise returns false.
+ *
+ * @return true if current node is transitive and the parent configuration is transitive.
+ */
+ public boolean isTransitive() {
+ if (node.isRoot()) {
+ // the root node is always considered transitive!
+ return true;
+ }
+
+ if (!data.isTransitive()) {
+ return false;
+ }
+
+ if (!isParentConfTransitive()) {
+ return false;
+ }
+
+ DependencyDescriptor dd = node.getDependencyDescriptor(getParentNode());
+ if ((dd != null) && dd.isTransitive()) {
+ return true;
+ }
+
+ return node.hasAnyMergedUsageWithTransitiveDependency(rootModuleConf);
+ }
+
+ /**
+ * Checks if the current node's parent configuration is transitive.
+ *
+ * @param node
+ * current node
+ * @return true if the node's parent configuration is transitive
+ */
+ protected boolean isParentConfTransitive() {
+ String conf = getParent().getRequestedConf();
+ if (conf == null) {
+ return true;
+ }
+ Configuration parentConf = getParentNode().getConfiguration(conf);
+ return parentConf.isTransitive();
+
+ }
+
+ /**
+ * Returns the 'real' node currently visited. 'Real' means that if we are visiting a node
+ * created originally with only a version constraint, and if this version constraint has been
+ * resolved to an existing node in the graph, we will return the existing node, and not the one
+ * originally used which is about to be discarded, since it's not possible to have in the graph
+ * two nodes for the same ModuleRevisionId
+ *
+ * @return the 'real' node currently visited.
+ */
+ public IvyNode getRealNode() {
+ IvyNode node = this.node.getRealNode();
+ if (node != null) {
+ return node;
+ } else {
+ return this.node;
+ }
+ }
+
+ /**
+ * Ask to the current visited node to use a real node only, if one exist. See getRealNode for
+ * details about what a 'real' node is.
+ */
+ public void useRealNode() {
+ if (parent != null) { // use real node make sense only for non root module
+ IvyNode node = data.getNode(this.node.getId());
+ if (node != null && node != this.node) {
+ this.node = node;
+ }
+ }
+ }
+
+ public boolean loadData(String conf, boolean shouldBePublic) {
+ boolean loaded = node.loadData(rootModuleConf, getParentNode(), parentConf, conf,
+ shouldBePublic, getUsage());
+ if (loaded) {
+ useRealNode();
+
+ // if the loaded revision is different from original one
+ // we now register this node on the new resolved id
+ // this includes two cases:
+ // - the id refers to a dynamic revision, which has been resolved by loadData
+ // - the loaded module descriptor has extra attributes in his info tag which are not
+ // used when declaring the dependency
+ if (data.getNode(node.getResolvedId()) == null
+ || !data.getNode(node.getResolvedId()).getId().equals(node.getResolvedId())) {
+ data.register(node.getResolvedId(), this);
+ }
+ }
+
+ return loaded;
+ }
+
+ public Collection/* <VisitNode> */getDependencies(String conf) {
+ Collection/* <IvyNode> */deps = node.getDependencies(rootModuleConf, conf, requestedConf);
+ Collection/* <VisitNode> */ret = new ArrayList(deps.size());
+ for (Iterator iter = deps.iterator(); iter.hasNext();) {
+ IvyNode depNode = (IvyNode) iter.next();
+ ret.add(traverseChild(conf, depNode));
+ }
+ return ret;
+ }
+
+ /**
+ * Returns a VisitNode for the given node. The given node must be a representation of the same
+ * module (usually in another revision) as the one visited by this node.
+ *
+ * @param node
+ * the node to visit
+ * @return a VisitNode for the given node
+ */
+ VisitNode gotoNode(IvyNode node) {
+ if (!getModuleId().equals(node.getModuleId())) {
+ throw new IllegalArgumentException(
+ "You can't use gotoNode for a node which does not represent the same Module "
+ + "as the one represented by this node.\nCurrent node module id="
+ + getModuleId() + " Given node module id=" + node.getModuleId());
+ }
+ VisitData visitData = data.getVisitData(node.getId());
+ if (visitData != null) {
+ List visitNodes = visitData.getVisitNodes(rootModuleConf);
+ for (Iterator iter = visitNodes.iterator(); iter.hasNext();) {
+ VisitNode vnode = (VisitNode) iter.next();
+ if ((parent == null && vnode.getParent() == null)
+ || (parent != null && parent.getId().equals(vnode.getParent().getId()))) {
+ vnode.parentConf = parentConf;
+ vnode.usage = getUsage();
+ return vnode;
+ }
+ }
+ }
+ // the node has not yet been visited from the current parent, we create a new visit node
+ return traverse(parent, parentConf, node, getUsage());
+ }
+
+ private IvyNodeUsage getUsage() {
+ return usage == null ? node.getMainUsage() : usage;
+ }
+
+ private VisitNode traverseChild(String parentConf, IvyNode child) {
+ VisitNode parent = this;
+ return traverse(parent, parentConf, child, null);
+ }
+
+ private VisitNode traverse(VisitNode parent, String parentConf, IvyNode node, IvyNodeUsage usage) {
+ if (getPath().contains(node)) {
+ IvyContext.getContext().getCircularDependencyStrategy()
+ .handleCircularDependency(toMrids(getPath(), node.getId()));
+ // we do not use the new parent, but the first one, to always be able to go up to the
+ // root
+ // parent = getVisitNode(depNode).getParent();
+ }
+ return new VisitNode(data, node, parent, rootModuleConf, parentConf, usage);
+ }
+
+ private ModuleRevisionId[] toMrids(Collection path, ModuleRevisionId last) {
+ ModuleRevisionId[] ret = new ModuleRevisionId[path.size() + 1];
+ int i = 0;
+ for (Iterator iter = path.iterator(); iter.hasNext(); i++) {
+ VisitNode node = (VisitNode) iter.next();
+ ret[i] = node.getNode().getId();
+ }
+ ret[ret.length - 1] = last;
+ return ret;
+ }
+
+ public ModuleRevisionId getResolvedId() {
+ return node.getResolvedId();
+ }
+
+ public void updateConfsToFetch(Collection confs) {
+ node.updateConfsToFetch(confs);
+ }
+
+ public ModuleRevisionId getId() {
+ return node.getId();
+ }
+
+ public boolean isEvicted() {
+ return node.isEvicted(rootModuleConf);
+ }
+
+ public String[] getRealConfs(String conf) {
+ return node.getRealConfs(conf);
+ }
+
+ public boolean hasProblem() {
+ return node.hasProblem();
+ }
+
+ public Configuration getConfiguration(String conf) {
+ return node.getConfiguration(conf);
+ }
+
+ public EvictionData getEvictedData() {
+ return node.getEvictedData(rootModuleConf);
+ }
+
+ public DependencyDescriptor getDependencyDescriptor() {
+ return node.getDependencyDescriptor(getParentNode());
+ }
+
+ private IvyNode getParentNode() {
+ return parent == null ? null : parent.getNode();
+ }
+
+ /**
+ * Returns true if this node can already be found in the path
+ *
+ * @return
+ */
+ public boolean isCircular() {
+ if (isCircular == null) {
+ if (parent != null) {
+ isCircular = Boolean.FALSE; // asumme it's false, and see if it isn't by checking
+ // the parent path
+ for (Iterator iter = parent.getPath().iterator(); iter.hasNext();) {
+ VisitNode ancestor = (VisitNode) iter.next();
+ if (getId().getModuleId().equals(ancestor.getId().getModuleId())) {
+ isCircular = Boolean.TRUE;
+ break;
+ }
+ }
+ } else {
+ isCircular = Boolean.FALSE;
+ }
+ }
+ return isCircular.booleanValue();
+ }
+
+ public String[] getConfsToFetch() {
+ return node.getConfsToFetch();
+ }
+
+ public String[] getRequiredConfigurations(VisitNode in, String inConf) {
+ return node.getRequiredConfigurations(in.getNode(), inConf);
+ }
+
+ public ModuleId getModuleId() {
+ return node.getModuleId();
+ }
+
+ public Collection getResolvedRevisions(ModuleId mid) {
+ return node.getResolvedRevisions(mid, rootModuleConf);
+ }
+
+ public void markEvicted(EvictionData evictionData) {
+ node.markEvicted(evictionData);
+ }
+
+ public String[] getRequiredConfigurations() {
+ return node.getRequiredConfigurations();
+ }
+
+ /**
+ * Marks the current node as evicted by the the given selected IvyNodes, in the given parent and
+ * root module configuration, with the given {@link ConflictManager}
+ *
+ * @param parent
+ * the VisitNode in which eviction has been made
+ * @param conflictMgr
+ * the conflict manager responsible for the eviction
+ * @param selected
+ * a Collection of {@link IvyNode} which have been selected
+ */
+ public void markEvicted(VisitNode parent, ConflictManager conflictMgr, Collection selected) {
+ node.markEvicted(rootModuleConf, parent.getNode(), conflictMgr, selected);
+ }
+
+ public ModuleDescriptor getDescriptor() {
+ return node.getDescriptor();
+ }
+
+ public EvictionData getEvictionDataInRoot(String rootModuleConf, VisitNode ancestor) {
+ return node.getEvictionDataInRoot(rootModuleConf, ancestor.getNode());
+ }
+
+ public Collection getEvictedRevisions(ModuleId moduleId) {
+ return node.getEvictedRevisions(moduleId, rootModuleConf);
+ }
+
+ // public void setRootModuleConf(String rootModuleConf) {
+ // if (rootModuleConf != null && !rootModuleConf.equals(rootModuleConf)) {
+ // _confsToFetch.clear(); // we change of root module conf => we discard all confs to fetch
+ // }
+ // if (rootModuleConf != null && rootModuleConf.equals(rootModuleConf)) {
+ // _selectedDeps.put(new ModuleIdConf(_id.getModuleId(), rootModuleConf),
+ // Collections.singleton(this));
+ // }
+ // rootModuleConf = rootModuleConf;
+ // }
+
+ public String toString() {
+ return node.toString();
+ }
+
+ public boolean isConfRequiredByMergedUsageOnly(String conf) {
+ return node.isConfRequiredByMergedUsageOnly(rootModuleConf, conf);
+ }
+
+}
diff --git a/src/java/org/apache/ivy/core/retrieve/FileNameMapper.java b/src/java/org/apache/ivy/core/retrieve/FileNameMapper.java
new file mode 100644
index 0000000..0e39a21
--- /dev/null
+++ b/src/java/org/apache/ivy/core/retrieve/FileNameMapper.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.retrieve;
+
+public interface FileNameMapper {
+
+ String[] mapFileName(String fileName);
+
+}
diff --git a/src/java/org/apache/ivy/core/retrieve/RetrieveEngine.java b/src/java/org/apache/ivy/core/retrieve/RetrieveEngine.java
new file mode 100644
index 0000000..d827f78
--- /dev/null
+++ b/src/java/org/apache/ivy/core/retrieve/RetrieveEngine.java
@@ -0,0 +1,496 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.retrieve;
+
+import java.io.File;
+import java.io.IOException;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.ivy.core.IvyContext;
+import org.apache.ivy.core.IvyPatternHelper;
+import org.apache.ivy.core.LogOptions;
+import org.apache.ivy.core.cache.ResolutionCacheManager;
+import org.apache.ivy.core.event.EventManager;
+import org.apache.ivy.core.event.retrieve.EndRetrieveArtifactEvent;
+import org.apache.ivy.core.event.retrieve.EndRetrieveEvent;
+import org.apache.ivy.core.event.retrieve.StartRetrieveArtifactEvent;
+import org.apache.ivy.core.event.retrieve.StartRetrieveEvent;
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleId;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.report.ArtifactDownloadReport;
+import org.apache.ivy.core.resolve.ResolveOptions;
+import org.apache.ivy.plugins.report.XmlReportParser;
+import org.apache.ivy.util.FileUtil;
+import org.apache.ivy.util.Message;
+
+public class RetrieveEngine {
+ private static final int KILO = 1024;
+
+ private RetrieveEngineSettings settings;
+
+ private EventManager eventManager;
+
+ public RetrieveEngine(RetrieveEngineSettings settings, EventManager eventManager) {
+ this.settings = settings;
+ this.eventManager = eventManager;
+ }
+
+ /**
+ * example of destFilePattern : - lib/[organisation]/[module]/[artifact]-[revision].[type] -
+ * lib/[artifact].[type] : flatten with no revision moduleId is used with confs and
+ * localCacheDirectory to determine an ivy report file, used as input for the copy If such a
+ * file does not exist for any conf (resolve has not been called before ?) then an
+ * IllegalStateException is thrown and nothing is copied.
+ *
+ * @deprecated Use
+ * {@link #retrieve(org.apache.ivy.core.module.id.ModuleRevisionId, RetrieveOptions)}
+ * instead
+ */
+ @Deprecated
+ public int retrieve(ModuleRevisionId mrid, String destFilePattern, RetrieveOptions options)
+ throws IOException {
+ RetrieveOptions retieveOptions = new RetrieveOptions(options);
+ retieveOptions.setDestArtifactPattern(destFilePattern);
+
+ RetrieveReport result = retrieve(mrid, retieveOptions);
+ return result.getNbrArtifactsCopied();
+ }
+
+ public RetrieveReport retrieve(ModuleRevisionId mrid, RetrieveOptions options)
+ throws IOException {
+ RetrieveReport report = new RetrieveReport();
+
+ ModuleId moduleId = mrid.getModuleId();
+ if (LogOptions.LOG_DEFAULT.equals(options.getLog())) {
+ Message.info(":: retrieving :: " + moduleId + (options.isSync() ? " [sync]" : ""));
+ } else {
+ Message.verbose(":: retrieving :: " + moduleId + (options.isSync() ? " [sync]" : ""));
+ }
+ Message.verbose("\tcheckUpToDate=" + settings.isCheckUpToDate());
+ long start = System.currentTimeMillis();
+
+ String destFilePattern = IvyPatternHelper.substituteVariables(
+ options.getDestArtifactPattern(), settings.getVariables());
+ String destIvyPattern = IvyPatternHelper.substituteVariables(options.getDestIvyPattern(),
+ settings.getVariables());
+
+ String[] confs = getConfs(mrid, options);
+ if (LogOptions.LOG_DEFAULT.equals(options.getLog())) {
+ Message.info("\tconfs: " + Arrays.asList(confs));
+ } else {
+ Message.verbose("\tconfs: " + Arrays.asList(confs));
+ }
+ if (this.eventManager != null) {
+ this.eventManager.fireIvyEvent(new StartRetrieveEvent(mrid, confs, options));
+ }
+
+ try {
+ Map/* <File, File> */destToSrcMap = null;
+ // Map<ArtifactDownloadReport, Set<String>>
+ Map artifactsToCopy = determineArtifactsToCopy(mrid, destFilePattern, options);
+ File fileRetrieveRoot = settings.resolveFile(IvyPatternHelper
+ .getTokenRoot(destFilePattern));
+ report.setRetrieveRoot(fileRetrieveRoot);
+ File ivyRetrieveRoot = destIvyPattern == null ? null : settings
+ .resolveFile(IvyPatternHelper.getTokenRoot(destIvyPattern));
+ Collection targetArtifactsStructure = new HashSet(); // Set(File) set of all paths
+ // which should be present at
+ // then end of retrieve (useful
+ // for sync)
+ Collection targetIvysStructure = new HashSet(); // same for ivy files
+
+ if (options.isMakeSymlinksInMass()) {
+ // The HashMap is of "destToSrc" because src could go two places, but dest can only
+ // come from one
+ destToSrcMap = new HashMap();
+ }
+
+ // do retrieve
+ long totalCopiedSize = 0;
+ for (Iterator iter = artifactsToCopy.keySet().iterator(); iter.hasNext();) {
+ ArtifactDownloadReport artifact = (ArtifactDownloadReport) iter.next();
+ File archive = artifact.getLocalFile();
+ if (artifact.getUnpackedLocalFile() != null) {
+ archive = artifact.getUnpackedLocalFile();
+ }
+ if (archive == null) {
+ Message.verbose("\tno local file available for " + artifact + ": skipping");
+ continue;
+ }
+ Set dest = (Set) artifactsToCopy.get(artifact);
+ Message.verbose("\tretrieving " + archive);
+ for (Iterator it2 = dest.iterator(); it2.hasNext();) {
+ IvyContext.getContext().checkInterrupted();
+ File destFile = settings.resolveFile((String) it2.next());
+ if (!settings.isCheckUpToDate() || !upToDate(archive, destFile, options)) {
+ Message.verbose("\t\tto " + destFile);
+ if (this.eventManager != null) {
+ // There is no unitary event for the mass sym linking.
+ // skip the event declaration.
+ if (!options.isMakeSymlinksInMass()) {
+ this.eventManager.fireIvyEvent(new StartRetrieveArtifactEvent(
+ artifact, destFile));
+ }
+ }
+ if (options.isMakeSymlinksInMass()) {
+ if (FileUtil.prepareCopy(archive, destFile, true)) {
+ destToSrcMap.put(destFile, archive);
+ }
+ } else if (options.isMakeSymlinks()) {
+ FileUtil.symlink(archive, destFile, null, true);
+ } else {
+ FileUtil.copy(archive, destFile, null, true);
+ }
+ if (this.eventManager != null) {
+ // There is no unitary event for the mass sym linking.
+ // skip the event declaration.
+ if (!options.isMakeSymlinksInMass()) {
+ this.eventManager.fireIvyEvent(new EndRetrieveArtifactEvent(
+ artifact, destFile));
+ }
+ }
+ totalCopiedSize += FileUtil.getFileLength(destFile);
+ report.addCopiedFile(destFile, artifact);
+ } else {
+ Message.verbose("\t\tto " + destFile + " [NOT REQUIRED]");
+ report.addUpToDateFile(destFile, artifact);
+ }
+
+ if ("ivy".equals(artifact.getType())) {
+ targetIvysStructure
+ .addAll(FileUtil.getPathFiles(ivyRetrieveRoot, destFile));
+ } else {
+ Iterator destFiles = FileUtil.listAll(destFile, Collections.EMPTY_LIST)
+ .iterator();
+ while (destFiles.hasNext()) {
+ targetArtifactsStructure.addAll(FileUtil.getPathFiles(fileRetrieveRoot,
+ (File) destFiles.next()));
+ }
+ }
+ }
+ }
+
+ if (options.isMakeSymlinksInMass()) {
+ Message.verbose("\tMass symlinking " + destToSrcMap.size() + " files");
+ FileUtil.symlinkInMass(destToSrcMap, true);
+ }
+
+ if (options.isSync()) {
+ Message.verbose("\tsyncing...");
+
+ String[] ignorableFilenames = settings.getIgnorableFilenames();
+ Collection ignoreList = Arrays.asList(ignorableFilenames);
+
+ Collection existingArtifacts = FileUtil.listAll(fileRetrieveRoot, ignoreList);
+ Collection existingIvys = ivyRetrieveRoot == null ? null : FileUtil.listAll(
+ ivyRetrieveRoot, ignoreList);
+
+ if (fileRetrieveRoot.equals(ivyRetrieveRoot)) {
+ Collection target = targetArtifactsStructure;
+ target.addAll(targetIvysStructure);
+ Collection existing = existingArtifacts;
+ existing.addAll(existingIvys);
+ sync(target, existing);
+ } else {
+ sync(targetArtifactsStructure, existingArtifacts);
+ if (existingIvys != null) {
+ sync(targetIvysStructure, existingIvys);
+ }
+ }
+ }
+ long elapsedTime = System.currentTimeMillis() - start;
+ String msg = "\t"
+ + report.getNbrArtifactsCopied()
+ + " artifacts copied"
+ + (settings.isCheckUpToDate() ? (", " + report.getNbrArtifactsUpToDate() + " already retrieved")
+ : "") + " (" + (totalCopiedSize / KILO) + "kB/" + elapsedTime + "ms)";
+ if (LogOptions.LOG_DEFAULT.equals(options.getLog())) {
+ Message.info(msg);
+ } else {
+ Message.verbose(msg);
+ }
+ Message.verbose("\tretrieve done (" + (elapsedTime) + "ms)");
+ if (this.eventManager != null) {
+ this.eventManager.fireIvyEvent(new EndRetrieveEvent(mrid, confs, elapsedTime,
+ report.getNbrArtifactsCopied(), report.getNbrArtifactsUpToDate(),
+ totalCopiedSize, options));
+ }
+
+ return report;
+ } catch (Exception ex) {
+ throw new RuntimeException("problem during retrieve of " + moduleId + ": " + ex, ex);
+ }
+ }
+
+ private String[] getConfs(ModuleRevisionId mrid, RetrieveOptions options) throws IOException {
+ String[] confs = options.getConfs();
+ if (confs == null || (confs.length == 1 && "*".equals(confs[0]))) {
+ try {
+ ModuleDescriptor md = getCache().getResolvedModuleDescriptor(mrid);
+ Message.verbose("no explicit confs given for retrieve, using ivy file: "
+ + md.getResource().getName());
+ confs = md.getConfigurationsNames();
+ options.setConfs(confs);
+ } catch (IOException e) {
+ throw e;
+ } catch (Exception e) {
+ IOException ioex = new IOException(e.getMessage());
+ ioex.initCause(e);
+ throw ioex;
+ }
+ }
+ return confs;
+ }
+
+ private ResolutionCacheManager getCache() {
+ return settings.getResolutionCacheManager();
+ }
+
+ private void sync(Collection target, Collection existing) {
+ Collection toRemove = new HashSet();
+ for (Iterator iter = existing.iterator(); iter.hasNext();) {
+ File file = (File) iter.next();
+ toRemove.add(file.getAbsoluteFile());
+ }
+ for (Iterator iter = target.iterator(); iter.hasNext();) {
+ File file = (File) iter.next();
+ toRemove.remove(file.getAbsoluteFile());
+ }
+ for (Iterator iter = toRemove.iterator(); iter.hasNext();) {
+ File file = (File) iter.next();
+ if (file.exists()) {
+ Message.verbose("\t\tdeleting " + file);
+ FileUtil.forceDelete(file);
+ }
+ }
+ }
+
+ /**
+ * @return Map<ArtifactDownloadReport, Set<String>>
+ */
+ public Map determineArtifactsToCopy(ModuleRevisionId mrid, String destFilePattern,
+ RetrieveOptions options) throws ParseException, IOException {
+ ModuleId moduleId = mrid.getModuleId();
+
+ if (options.getResolveId() == null) {
+ options.setResolveId(ResolveOptions.getDefaultResolveId(moduleId));
+ }
+
+ ResolutionCacheManager cacheManager = getCache();
+ String[] confs = getConfs(mrid, options);
+ String destIvyPattern = IvyPatternHelper.substituteVariables(options.getDestIvyPattern(),
+ settings.getVariables());
+
+ // find what we must retrieve where
+
+ // ArtifactDownloadReport source -> Set (String copyDestAbsolutePath)
+ final Map artifactsToCopy = new HashMap();
+ // String copyDestAbsolutePath -> Set (ArtifactRevisionId source)
+ final Map conflictsMap = new HashMap();
+ // String copyDestAbsolutePath -> Set (ArtifactDownloadReport source)
+ final Map conflictsReportsMap = new HashMap();
+ // String copyDestAbsolutePath -> Set (String conf)
+ final Map conflictsConfMap = new HashMap();
+
+ XmlReportParser parser = new XmlReportParser();
+ for (int i = 0; i < confs.length; i++) {
+ final String conf = confs[i];
+
+ File report = cacheManager.getConfigurationResolveReportInCache(options.getResolveId(),
+ conf);
+ parser.parse(report);
+
+ Collection artifacts = new ArrayList(Arrays.asList(parser.getArtifactReports()));
+ if (destIvyPattern != null) {
+ ModuleRevisionId[] mrids = parser.getRealDependencyRevisionIds();
+ for (int j = 0; j < mrids.length; j++) {
+ artifacts.add(parser.getMetadataArtifactReport(mrids[j]));
+ }
+ }
+ for (Iterator iter = artifacts.iterator(); iter.hasNext();) {
+ ArtifactDownloadReport adr = (ArtifactDownloadReport) iter.next();
+
+ Artifact artifact = adr.getArtifact();
+ String ext = artifact.getExt();
+ if (adr.getUnpackedLocalFile() != null) {
+ ext = "";
+ }
+
+ String destPattern = "ivy".equals(adr.getType()) ? destIvyPattern : destFilePattern;
+
+ if (!"ivy".equals(adr.getType())
+ && !options.getArtifactFilter().accept(adr.getArtifact())) {
+ continue; // skip this artifact, the filter didn't accept it!
+ }
+
+ ModuleRevisionId aMrid = artifact.getModuleRevisionId();
+ String destFileName = IvyPatternHelper.substitute(destPattern,
+ aMrid.getOrganisation(), aMrid.getName(), aMrid.getBranch(),
+ aMrid.getRevision(), artifact.getName(), artifact.getType(), ext, conf,
+ adr.getArtifactOrigin(), aMrid.getQualifiedExtraAttributes(),
+ artifact.getQualifiedExtraAttributes());
+ Set dest = (Set) artifactsToCopy.get(adr);
+ if (dest == null) {
+ dest = new HashSet();
+ artifactsToCopy.put(adr, dest);
+ }
+ String copyDest = settings.resolveFile(destFileName).getAbsolutePath();
+
+ String[] destinations = new String[] {copyDest};
+ if (options.getMapper() != null) {
+ destinations = options.getMapper().mapFileName(copyDest);
+ }
+
+ for (int j = 0; j < destinations.length; j++) {
+ dest.add(destinations[j]);
+
+ Set conflicts = (Set) conflictsMap.get(destinations[j]);
+ Set conflictsReports = (Set) conflictsReportsMap.get(destinations[j]);
+ Set conflictsConf = (Set) conflictsConfMap.get(destinations[j]);
+ if (conflicts == null) {
+ conflicts = new HashSet();
+ conflictsMap.put(destinations[j], conflicts);
+ }
+ if (conflictsReports == null) {
+ conflictsReports = new HashSet();
+ conflictsReportsMap.put(destinations[j], conflictsReports);
+ }
+ if (conflictsConf == null) {
+ conflictsConf = new HashSet();
+ conflictsConfMap.put(destinations[j], conflictsConf);
+ }
+ if (conflicts.add(artifact.getId())) {
+ conflictsReports.add(adr);
+ conflictsConf.add(conf);
+ }
+ }
+ }
+ }
+
+ // resolve conflicts if any
+ for (Iterator iter = conflictsMap.keySet().iterator(); iter.hasNext();) {
+ String copyDest = (String) iter.next();
+ Set artifacts = (Set) conflictsMap.get(copyDest);
+ Set conflictsConfs = (Set) conflictsConfMap.get(copyDest);
+ if (artifacts.size() > 1) {
+ List artifactsList = new ArrayList((Collection) conflictsReportsMap.get(copyDest));
+ // conflicts battle is resolved by a sort using a conflict resolving policy
+ // comparator which consider as greater a winning artifact
+ Collections.sort(artifactsList, getConflictResolvingPolicy());
+
+ // after the sort, the winning artifact is the greatest one, i.e. the last one
+ // we fail if different artifacts of the same module are mapped to the same file
+ ArtifactDownloadReport winner = (ArtifactDownloadReport) artifactsList
+ .get(artifactsList.size() - 1);
+ ModuleRevisionId winnerMD = winner.getArtifact().getModuleRevisionId();
+ for (int i = artifactsList.size() - 2; i >= 0; i--) {
+ ArtifactDownloadReport current = (ArtifactDownloadReport) artifactsList.get(i);
+ if (winnerMD.equals(current.getArtifact().getModuleRevisionId())) {
+ throw new RuntimeException("Multiple artifacts of the module " + winnerMD
+ + " are retrieved to the same file! Update the retrieve pattern "
+ + " to fix this error.");
+ }
+ }
+
+ Message.info("\tconflict on " + copyDest + " in " + conflictsConfs + ": "
+ + winnerMD.getRevision() + " won");
+
+ // we now iterate over the list beginning with the artifact preceding the winner,
+ // and going backward to the least artifact
+ for (int i = artifactsList.size() - 2; i >= 0; i--) {
+ ArtifactDownloadReport looser = (ArtifactDownloadReport) artifactsList.get(i);
+ Message.verbose("\t\tremoving conflict looser artifact: "
+ + looser.getArtifact());
+ // for each loser, we remove the pair (loser - copyDest) in the artifactsToCopy
+ // map
+ Set dest = (Set) artifactsToCopy.get(looser);
+ dest.remove(copyDest);
+ if (dest.isEmpty()) {
+ artifactsToCopy.remove(looser);
+ }
+ }
+ }
+ }
+ return artifactsToCopy;
+ }
+
+ private boolean upToDate(File source, File target, RetrieveOptions options) {
+ if (!target.exists()) {
+ return false;
+ }
+
+ String overwriteMode = options.getOverwriteMode();
+ if (RetrieveOptions.OVERWRITEMODE_ALWAYS.equals(overwriteMode)) {
+ return false;
+ }
+
+ if (RetrieveOptions.OVERWRITEMODE_NEVER.equals(overwriteMode)) {
+ return true;
+ }
+
+ if (RetrieveOptions.OVERWRITEMODE_NEWER.equals(overwriteMode)) {
+ return source.lastModified() <= target.lastModified();
+ }
+
+ if (RetrieveOptions.OVERWRITEMODE_DIFFERENT.equals(overwriteMode)) {
+ return source.lastModified() == target.lastModified();
+ }
+
+ // unknown, so just to be sure
+ return false;
+ }
+
+ /**
+ * The returned comparator should consider greater the artifact which gains the conflict battle.
+ * This is used only during retrieve... prefer resolve conflict manager to resolve conflicts.
+ *
+ * @return
+ */
+ private Comparator getConflictResolvingPolicy() {
+ return new Comparator() {
+ // younger conflict resolving policy
+ public int compare(Object o1, Object o2) {
+ Artifact a1 = ((ArtifactDownloadReport) o1).getArtifact();
+ Artifact a2 = ((ArtifactDownloadReport) o2).getArtifact();
+ if (a1.getPublicationDate().after(a2.getPublicationDate())) {
+ // a1 is after a2 <=> a1 is younger than a2 <=> a1 wins the conflict battle
+ return +1;
+ } else if (a1.getPublicationDate().before(a2.getPublicationDate())) {
+ // a1 is before a2 <=> a2 is younger than a1 <=> a2 wins the conflict battle
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+ };
+ }
+
+}
diff --git a/src/java/org/apache/ivy/core/retrieve/RetrieveEngineSettings.java b/src/java/org/apache/ivy/core/retrieve/RetrieveEngineSettings.java
new file mode 100644
index 0000000..c50baee
--- /dev/null
+++ b/src/java/org/apache/ivy/core/retrieve/RetrieveEngineSettings.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.retrieve;
+
+import org.apache.ivy.core.settings.IvyVariableContainer;
+import org.apache.ivy.plugins.parser.ParserSettings;
+
+public interface RetrieveEngineSettings extends ParserSettings {
+
+ boolean isCheckUpToDate();
+
+ IvyVariableContainer getVariables();
+
+ String[] getIgnorableFilenames();
+
+}
diff --git a/src/java/org/apache/ivy/core/retrieve/RetrieveOptions.java b/src/java/org/apache/ivy/core/retrieve/RetrieveOptions.java
new file mode 100644
index 0000000..c80bb71
--- /dev/null
+++ b/src/java/org/apache/ivy/core/retrieve/RetrieveOptions.java
@@ -0,0 +1,210 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.retrieve;
+
+import org.apache.ivy.core.LogOptions;
+import org.apache.ivy.util.filter.Filter;
+import org.apache.ivy.util.filter.FilterHelper;
+
+/**
+ * A set of options used during retrieve related tasks
+ *
+ * @see RetrieveEngine
+ */
+public class RetrieveOptions extends LogOptions {
+ public static final String OVERWRITEMODE_NEVER = "never";
+
+ public static final String OVERWRITEMODE_ALWAYS = "always";
+
+ public static final String OVERWRITEMODE_NEWER = "newer";
+
+ public static final String OVERWRITEMODE_DIFFERENT = "different";
+
+ /**
+ * The names of configurations to retrieve. If the array consists only of '*', then all
+ * configurations of the module will be retrieved.
+ */
+ private String[] confs = new String[] {"*"};
+
+ /**
+ * The pattern to which ivy files should be retrieved. If destIvyPattern is null no ivy files
+ * will be copied.
+ */
+ private String destIvyPattern = null;
+
+ /**
+ * The pattern to which artifacts should be retrieved.
+ */
+ private String destArtifactPattern = null;
+
+ /**
+ * The filter to apply before retrieving artifacts.
+ */
+ private Filter artifactFilter = FilterHelper.NO_FILTER;
+
+ /**
+ * True if a synchronisation of the destination directory should be done, false if a simple copy
+ * is enough. Synchronisation means that after the retrieve only files which have been retrieved
+ * will be present in the destination directory, which means that some files may be deleted.
+ */
+ private boolean sync = false;
+
+ private String overwriteMode = OVERWRITEMODE_NEWER;
+
+ /**
+ * True if the original files should be used insteaad of their cache copy.
+ */
+ private boolean useOrigin = false;
+
+ /**
+ * True if symbolic links should be created instead of plain copy. Works only on OS supporting
+ * symbolic links.
+ */
+ private boolean makeSymlinks = false;
+
+ /**
+ * True if symbolic links should be created all at once, instead of one at a time. Works only on
+ * OS supporting with both "sh" (a shell) and "ln" (the link command).
+ */
+ private boolean makeSymlinksInMass = false;
+
+ /**
+ * The id used to store the resolve information.
+ */
+ private String resolveId;
+
+ private FileNameMapper mapper;
+
+ public RetrieveOptions() {
+ }
+
+ public RetrieveOptions(RetrieveOptions options) {
+ super(options);
+ this.confs = options.confs;
+ this.destIvyPattern = options.destIvyPattern;
+ this.destArtifactPattern = options.destArtifactPattern;
+ this.artifactFilter = options.artifactFilter;
+ this.sync = options.sync;
+ this.overwriteMode = options.overwriteMode;
+ this.useOrigin = options.useOrigin;
+ this.makeSymlinks = options.makeSymlinks;
+ this.makeSymlinksInMass = options.makeSymlinksInMass;
+ this.resolveId = options.resolveId;
+ this.mapper = options.mapper;
+ }
+
+ public String getDestArtifactPattern() {
+ return destArtifactPattern;
+ }
+
+ public RetrieveOptions setDestArtifactPattern(String destArtifactPattern) {
+ this.destArtifactPattern = destArtifactPattern;
+ return this;
+ }
+
+ public Filter getArtifactFilter() {
+ return artifactFilter;
+ }
+
+ public RetrieveOptions setArtifactFilter(Filter artifactFilter) {
+ this.artifactFilter = artifactFilter;
+ return this;
+ }
+
+ public String[] getConfs() {
+ return confs;
+ }
+
+ public RetrieveOptions setConfs(String[] confs) {
+ this.confs = confs;
+ return this;
+ }
+
+ public String getOverwriteMode() {
+ return overwriteMode == null ? OVERWRITEMODE_NEWER : overwriteMode;
+ }
+
+ public RetrieveOptions setOverwriteMode(String overwriteMode) {
+ this.overwriteMode = overwriteMode;
+ return this;
+ }
+
+ public String getDestIvyPattern() {
+ return destIvyPattern;
+ }
+
+ public RetrieveOptions setDestIvyPattern(String destIvyPattern) {
+ this.destIvyPattern = destIvyPattern;
+ return this;
+ }
+
+ public boolean isMakeSymlinks() {
+ return makeSymlinks;
+ }
+
+ public boolean isMakeSymlinksInMass() {
+ return makeSymlinksInMass;
+ }
+
+ public RetrieveOptions setMakeSymlinks(boolean makeSymlinks) {
+ this.makeSymlinks = makeSymlinks;
+ return this;
+ }
+
+ public RetrieveOptions setMakeSymlinksInMass(boolean makeSymlinksInMass) {
+ this.makeSymlinksInMass = makeSymlinksInMass;
+ return this;
+ }
+
+ public boolean isSync() {
+ return sync;
+ }
+
+ public RetrieveOptions setSync(boolean sync) {
+ this.sync = sync;
+ return this;
+ }
+
+ public boolean isUseOrigin() {
+ return useOrigin;
+ }
+
+ public RetrieveOptions setUseOrigin(boolean useOrigin) {
+ this.useOrigin = useOrigin;
+ return this;
+ }
+
+ public String getResolveId() {
+ return resolveId;
+ }
+
+ public RetrieveOptions setResolveId(String resolveId) {
+ this.resolveId = resolveId;
+ return this;
+ }
+
+ public FileNameMapper getMapper() {
+ return mapper;
+ }
+
+ public RetrieveOptions setMapper(FileNameMapper mapper) {
+ this.mapper = mapper;
+ return this;
+ }
+
+}
diff --git a/src/java/org/apache/ivy/core/retrieve/RetrieveReport.java b/src/java/org/apache/ivy/core/retrieve/RetrieveReport.java
new file mode 100644
index 0000000..42273d3
--- /dev/null
+++ b/src/java/org/apache/ivy/core/retrieve/RetrieveReport.java
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.retrieve;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+
+import org.apache.ivy.core.report.ArtifactDownloadReport;
+
+public class RetrieveReport {
+
+ private Collection/* <File> */upToDateFiles = new HashSet();
+
+ private Collection/* <File> */copiedFiles = new HashSet();
+
+ private Map/* <File, ArtifactDownloadReport> */downloadReport = new HashMap();
+
+ private File retrieveRoot;
+
+ /**
+ * Returns the root directory to where the artifacts are retrieved.
+ */
+ public File getRetrieveRoot() {
+ return retrieveRoot;
+ }
+
+ public void setRetrieveRoot(File retrieveRoot) {
+ this.retrieveRoot = retrieveRoot;
+ }
+
+ public int getNbrArtifactsCopied() {
+ return copiedFiles.size();
+ }
+
+ public int getNbrArtifactsUpToDate() {
+ return upToDateFiles.size();
+ }
+
+ public void addCopiedFile(File file, ArtifactDownloadReport report) {
+ copiedFiles.add(file);
+ downloadReport.put(file, report);
+ }
+
+ public void addUpToDateFile(File file, ArtifactDownloadReport report) {
+ upToDateFiles.add(file);
+ downloadReport.put(file, report);
+ }
+
+ /**
+ * Returns a collection of <tt>File</tt> objects who were actually copied during the retrieve
+ * process.
+ */
+ public Collection getCopiedFiles() {
+ return new ArrayList(copiedFiles);
+ }
+
+ /**
+ * Returns a collection of <tt>File</tt> objects who were actually copied during the retrieve
+ * process.
+ */
+ public Collection getUpToDateFiles() {
+ return new ArrayList(upToDateFiles);
+ }
+
+ /**
+ * Returns a collection of <tt>File</tt> objects who were retrieved during the retrieve process.
+ * This is the union of the files being copied and the files that were up-to-date.
+ */
+ public Collection getRetrievedFiles() {
+ Collection result = new ArrayList(upToDateFiles.size() + copiedFiles.size());
+ result.addAll(upToDateFiles);
+ result.addAll(copiedFiles);
+ return result;
+ }
+
+ /**
+ * Get the mapping between the copied files and their corresponding download report
+ */
+ public Map getDownloadReport() {
+ return downloadReport;
+ }
+}
diff --git a/src/java/org/apache/ivy/core/search/ModuleEntry.java b/src/java/org/apache/ivy/core/search/ModuleEntry.java
new file mode 100644
index 0000000..c3062c8
--- /dev/null
+++ b/src/java/org/apache/ivy/core/search/ModuleEntry.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.search;
+
+import org.apache.ivy.plugins.resolver.DependencyResolver;
+
+public class ModuleEntry {
+ private OrganisationEntry organisationEntry;
+
+ private String module;
+
+ public ModuleEntry(OrganisationEntry org, String name) {
+ organisationEntry = org;
+ module = name;
+ }
+
+ public String getOrganisation() {
+ return organisationEntry.getOrganisation();
+ }
+
+ public DependencyResolver getResolver() {
+ return organisationEntry.getResolver();
+ }
+
+ public String getModule() {
+ return module;
+ }
+
+ public OrganisationEntry getOrganisationEntry() {
+ return organisationEntry;
+ }
+
+ public String toString() {
+ return organisationEntry + "#" + module;
+ }
+}
diff --git a/src/java/org/apache/ivy/core/search/OrganisationEntry.java b/src/java/org/apache/ivy/core/search/OrganisationEntry.java
new file mode 100644
index 0000000..d173dd0
--- /dev/null
+++ b/src/java/org/apache/ivy/core/search/OrganisationEntry.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.search;
+
+import org.apache.ivy.plugins.resolver.DependencyResolver;
+
+public class OrganisationEntry {
+ private DependencyResolver resolver;
+
+ private String organisation;
+
+ public OrganisationEntry(DependencyResolver resolver, String organisation) {
+ this.resolver = resolver;
+ this.organisation = organisation;
+ }
+
+ public String getOrganisation() {
+ return organisation;
+ }
+
+ public DependencyResolver getResolver() {
+ return resolver;
+ }
+
+ public String toString() {
+ return organisation;
+ }
+}
diff --git a/src/java/org/apache/ivy/core/search/RevisionEntry.java b/src/java/org/apache/ivy/core/search/RevisionEntry.java
new file mode 100644
index 0000000..74c536d
--- /dev/null
+++ b/src/java/org/apache/ivy/core/search/RevisionEntry.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.search;
+
+import org.apache.ivy.plugins.resolver.DependencyResolver;
+
+public class RevisionEntry {
+ private ModuleEntry moduleEntry;
+
+ private String revision;
+
+ public RevisionEntry(ModuleEntry mod, String name) {
+ moduleEntry = mod;
+ revision = name;
+ }
+
+ public ModuleEntry getModuleEntry() {
+ return moduleEntry;
+ }
+
+ public String getRevision() {
+ return revision;
+ }
+
+ public String getModule() {
+ return moduleEntry.getModule();
+ }
+
+ public String getOrganisation() {
+ return moduleEntry.getOrganisation();
+ }
+
+ public OrganisationEntry getOrganisationEntry() {
+ return moduleEntry.getOrganisationEntry();
+ }
+
+ public DependencyResolver getResolver() {
+ return moduleEntry.getResolver();
+ }
+
+ public String toString() {
+ return moduleEntry + ";" + revision;
+ }
+}
diff --git a/src/java/org/apache/ivy/core/search/SearchEngine.java b/src/java/org/apache/ivy/core/search/SearchEngine.java
new file mode 100644
index 0000000..84c2443
--- /dev/null
+++ b/src/java/org/apache/ivy/core/search/SearchEngine.java
@@ -0,0 +1,437 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.search;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.apache.ivy.core.IvyPatternHelper;
+import org.apache.ivy.core.module.id.ModuleId;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.settings.IvySettings;
+import org.apache.ivy.plugins.matcher.Matcher;
+import org.apache.ivy.plugins.matcher.MatcherHelper;
+import org.apache.ivy.plugins.matcher.PatternMatcher;
+import org.apache.ivy.plugins.namespace.NameSpaceHelper;
+import org.apache.ivy.plugins.namespace.Namespace;
+import org.apache.ivy.plugins.resolver.AbstractResolver;
+import org.apache.ivy.plugins.resolver.DependencyResolver;
+import org.apache.ivy.util.Message;
+
+public class SearchEngine {
+ private IvySettings settings;
+
+ public SearchEngine(IvySettings settings) {
+ this.settings = settings;
+ }
+
+ /**
+ * Returns an empty array when no token values are found.
+ *
+ * @param token
+ * @param otherTokenValues
+ * @return
+ */
+ public String[] listTokenValues(String token, Map otherTokenValues) {
+ Set entries = new LinkedHashSet();
+
+ for (Iterator iter = settings.getResolvers().iterator(); iter.hasNext();) {
+ DependencyResolver resolver = (DependencyResolver) iter.next();
+ Map[] values = resolver.listTokenValues(new String[] {token}, otherTokenValues);
+ for (int i = 0; i < values.length; i++) {
+ entries.add(values[i].get(token));
+ }
+ }
+
+ return (String[]) entries.toArray(new String[entries.size()]);
+ }
+
+ public OrganisationEntry[] listOrganisationEntries() {
+ Set entries = new HashSet();
+
+ for (Iterator iter = settings.getResolvers().iterator(); iter.hasNext();) {
+ DependencyResolver resolver = (DependencyResolver) iter.next();
+ Map[] orgs = resolver.listTokenValues(new String[] {IvyPatternHelper.ORGANISATION_KEY},
+ new HashMap());
+ for (int i = 0; i < orgs.length; i++) {
+ String org = (String) orgs[i].get(IvyPatternHelper.ORGANISATION_KEY);
+ entries.add(new OrganisationEntry(resolver, org));
+ }
+ }
+
+ return (OrganisationEntry[]) entries.toArray(new OrganisationEntry[entries.size()]);
+ }
+
+ public String[] listOrganisations() {
+ Set entries = new HashSet();
+
+ for (Iterator iter = settings.getResolvers().iterator(); iter.hasNext();) {
+ DependencyResolver resolver = (DependencyResolver) iter.next();
+ Map[] orgs = resolver.listTokenValues(new String[] {IvyPatternHelper.ORGANISATION_KEY},
+ new HashMap());
+ for (int i = 0; i < orgs.length; i++) {
+ entries.add(orgs[i].get(IvyPatternHelper.ORGANISATION_KEY));
+ }
+ }
+
+ return (String[]) entries.toArray(new String[entries.size()]);
+ }
+
+ public ModuleEntry[] listModuleEntries(OrganisationEntry org) {
+ Set entries = new HashSet();
+
+ Map tokenValues = new HashMap();
+ tokenValues.put(IvyPatternHelper.ORGANISATION_KEY, org.getOrganisation());
+
+ for (Iterator iter = settings.getResolvers().iterator(); iter.hasNext();) {
+ DependencyResolver resolver = (DependencyResolver) iter.next();
+ Map[] modules = resolver.listTokenValues(new String[] {IvyPatternHelper.MODULE_KEY},
+ tokenValues);
+ for (int i = 0; i < modules.length; i++) {
+ String module = (String) modules[i].get(IvyPatternHelper.MODULE_KEY);
+ entries.add(new ModuleEntry(org, module));
+ }
+ }
+
+ return (ModuleEntry[]) entries.toArray(new ModuleEntry[entries.size()]);
+ }
+
+ public String[] listModules(String org) {
+ Set entries = new HashSet();
+
+ Map tokenValues = new HashMap();
+ tokenValues.put(IvyPatternHelper.ORGANISATION_KEY, org);
+
+ for (Iterator iter = settings.getResolvers().iterator(); iter.hasNext();) {
+ DependencyResolver resolver = (DependencyResolver) iter.next();
+ Map[] modules = resolver.listTokenValues(new String[] {IvyPatternHelper.MODULE_KEY},
+ tokenValues);
+ for (int i = 0; i < modules.length; i++) {
+ entries.add(modules[i].get(IvyPatternHelper.MODULE_KEY));
+ }
+ }
+
+ return (String[]) entries.toArray(new String[entries.size()]);
+ }
+
+ public RevisionEntry[] listRevisionEntries(ModuleEntry module) {
+ Set entries = new HashSet();
+
+ Map tokenValues = new HashMap();
+ tokenValues.put(IvyPatternHelper.ORGANISATION_KEY, module.getOrganisation());
+ tokenValues.put(IvyPatternHelper.MODULE_KEY, module.getModule());
+
+ for (Iterator iter = settings.getResolvers().iterator(); iter.hasNext();) {
+ DependencyResolver resolver = (DependencyResolver) iter.next();
+ Map[] revisions = resolver.listTokenValues(
+ new String[] {IvyPatternHelper.REVISION_KEY}, tokenValues);
+ for (int i = 0; i < revisions.length; i++) {
+ String revision = (String) revisions[i].get(IvyPatternHelper.REVISION_KEY);
+ entries.add(new RevisionEntry(module, revision));
+ }
+ }
+
+ return (RevisionEntry[]) entries.toArray(new RevisionEntry[entries.size()]);
+ }
+
+ public String[] listRevisions(String org, String module) {
+ Set entries = new HashSet();
+
+ Map tokenValues = new HashMap();
+ tokenValues.put(IvyPatternHelper.ORGANISATION_KEY, org);
+ tokenValues.put(IvyPatternHelper.MODULE_KEY, module);
+
+ for (Iterator iter = settings.getResolvers().iterator(); iter.hasNext();) {
+ DependencyResolver resolver = (DependencyResolver) iter.next();
+ Map[] revisions = resolver.listTokenValues(
+ new String[] {IvyPatternHelper.REVISION_KEY}, tokenValues);
+ for (int i = 0; i < revisions.length; i++) {
+ entries.add(revisions[i].get(IvyPatternHelper.REVISION_KEY));
+ }
+ }
+
+ return (String[]) entries.toArray(new String[entries.size()]);
+ }
+
+ /**
+ * List module ids of the module accessible through the current resolvers matching the given mid
+ * criteria according to the given matcher.
+ * <p>
+ * ModuleId are returned in the system namespace.
+ * </p>
+ *
+ * @param criteria
+ * @param matcher
+ * @return
+ */
+ public ModuleId[] listModules(ModuleId moduleCrit, PatternMatcher matcher) {
+ List ret = new ArrayList();
+
+ Map criteria = new HashMap();
+ addMatcher(matcher, moduleCrit.getOrganisation(), criteria,
+ IvyPatternHelper.ORGANISATION_KEY);
+ addMatcher(matcher, moduleCrit.getName(), criteria, IvyPatternHelper.MODULE_KEY);
+
+ String[] tokensToList = new String[] {IvyPatternHelper.ORGANISATION_KEY,
+ IvyPatternHelper.MODULE_KEY};
+
+ for (Iterator iter = settings.getResolvers().iterator(); iter.hasNext();) {
+ DependencyResolver resolver = (DependencyResolver) iter.next();
+ Map[] moduleIdAsMap = resolver.listTokenValues(tokensToList, criteria);
+ for (int i = 0; i < moduleIdAsMap.length; i++) {
+ String org = (String) moduleIdAsMap[i].get(IvyPatternHelper.ORGANISATION_KEY);
+ String name = (String) moduleIdAsMap[i].get(IvyPatternHelper.MODULE_KEY);
+ ModuleId modId = ModuleId.newInstance(org, name);
+ ret.add(NameSpaceHelper.transform(modId, resolver.getNamespace()
+ .getToSystemTransformer()));
+ }
+ }
+
+ return (ModuleId[]) ret.toArray(new ModuleId[ret.size()]);
+ }
+
+ /**
+ * List module revision ids of the module accessible through the current resolvers matching the
+ * given mrid criteria according to the given matcher.
+ * <p>
+ * ModuleRevisionId are returned in the system namespace.
+ * </p>
+ *
+ * @param criteria
+ * @param matcher
+ * @return
+ */
+ public ModuleRevisionId[] listModules(ModuleRevisionId moduleCrit, PatternMatcher matcher) {
+ List ret = new ArrayList();
+
+ Map criteria = new HashMap();
+ for (Iterator it = moduleCrit.getAttributes().entrySet().iterator(); it.hasNext();) {
+ Map.Entry entry = (Entry) it.next();
+ addMatcher(matcher, (String) entry.getValue(), criteria, (String) entry.getKey());
+ }
+
+ String[] tokensToList = (String[]) moduleCrit.getAttributes().keySet()
+ .toArray(new String[moduleCrit.getAttributes().size()]);
+
+ for (Iterator iter = settings.getResolvers().iterator(); iter.hasNext();) {
+ DependencyResolver resolver = (DependencyResolver) iter.next();
+ Map[] moduleIdAsMap = resolver.listTokenValues(tokensToList, criteria);
+ for (int i = 0; i < moduleIdAsMap.length; i++) {
+ String org = (String) moduleIdAsMap[i].get(IvyPatternHelper.ORGANISATION_KEY);
+ String name = (String) moduleIdAsMap[i].get(IvyPatternHelper.MODULE_KEY);
+ String branch = (String) moduleIdAsMap[i].get(IvyPatternHelper.BRANCH_KEY);
+ String rev = (String) moduleIdAsMap[i].get(IvyPatternHelper.REVISION_KEY);
+
+ Map foundExtraAtts = new HashMap();
+ Set qualAttributes = moduleCrit.getQualifiedExtraAttributes().keySet();
+ for (Iterator iter2 = qualAttributes.iterator(); iter2.hasNext();) {
+ String qualifiedKey = (String) iter2.next();
+ String value = null;
+ int colonIndex = qualifiedKey.indexOf(':');
+ if (colonIndex == -1) {
+ value = (String) moduleIdAsMap[i].get(qualifiedKey);
+ } else {
+ value = (String) moduleIdAsMap[i].get(qualifiedKey
+ .substring(colonIndex + 1));
+ }
+
+ if (value != null) {
+ foundExtraAtts.put(qualifiedKey, value);
+ }
+ }
+
+ ModuleRevisionId modRevId = ModuleRevisionId.newInstance(org, name, branch, rev,
+ foundExtraAtts);
+ ret.add(resolver.getNamespace().getToSystemTransformer().transform(modRevId));
+ }
+ }
+
+ return (ModuleRevisionId[]) ret.toArray(new ModuleRevisionId[ret.size()]);
+ }
+
+ /**
+ * List modules matching a given criteria, available in the given dependency resolver.
+ * <p>
+ * ModuleRevisionId are returned in the system namespace.
+ * </p>
+ *
+ * @param resolver
+ * the resolver in which modules should looked up
+ * @param moduleCrit
+ * the criteria to match
+ * @param matcher
+ * the matcher to use to match criteria
+ * @return an array of matching module revision ids
+ */
+ public ModuleRevisionId[] listModules(DependencyResolver resolver, ModuleRevisionId moduleCrit,
+ PatternMatcher matcher) {
+ Map criteria = new HashMap();
+ for (Iterator it = moduleCrit.getAttributes().entrySet().iterator(); it.hasNext();) {
+ Map.Entry entry = (Entry) it.next();
+ addMatcher(matcher, (String) entry.getValue(), criteria, (String) entry.getKey());
+ }
+
+ String[] tokensToList = (String[]) moduleCrit.getAttributes().keySet()
+ .toArray(new String[moduleCrit.getAttributes().size()]);
+
+ Map[] moduleIdAsMap = resolver.listTokenValues(tokensToList, criteria);
+ Set result = new LinkedHashSet(); // we use a Set to remove duplicates
+ for (int i = 0; i < moduleIdAsMap.length; i++) {
+ String org = (String) moduleIdAsMap[i].get(IvyPatternHelper.ORGANISATION_KEY);
+ String name = (String) moduleIdAsMap[i].get(IvyPatternHelper.MODULE_KEY);
+ String branch = (String) moduleIdAsMap[i].get(IvyPatternHelper.BRANCH_KEY);
+ String rev = (String) moduleIdAsMap[i].get(IvyPatternHelper.REVISION_KEY);
+
+ Map foundExtraAtts = new HashMap();
+ Set qualExtraAttributes = moduleCrit.getQualifiedExtraAttributes().keySet();
+ for (Iterator iter2 = qualExtraAttributes.iterator(); iter2.hasNext();) {
+ String qualifiedKey = (String) iter2.next();
+ String value = null;
+ int colonIndex = qualifiedKey.indexOf(':');
+ if (colonIndex == -1) {
+ value = (String) moduleIdAsMap[i].get(qualifiedKey);
+ } else {
+ value = (String) moduleIdAsMap[i].get(qualifiedKey.substring(colonIndex + 1));
+ }
+
+ if (value != null) {
+ foundExtraAtts.put(qualifiedKey, value);
+ }
+ }
+
+ ModuleRevisionId modRevId = ModuleRevisionId.newInstance(org, name, branch, rev,
+ foundExtraAtts);
+ result.add(resolver.getNamespace().getToSystemTransformer().transform(modRevId));
+ }
+
+ return (ModuleRevisionId[]) result.toArray(new ModuleRevisionId[result.size()]);
+ }
+
+ private void addMatcher(PatternMatcher patternMatcher, String expression, Map criteria,
+ String key) {
+ if (expression == null) {
+ return;
+ }
+
+ Matcher matcher = patternMatcher.getMatcher(expression);
+ if (matcher.isExact()) {
+ criteria.put(key, expression);
+ } else {
+ criteria.put(key, matcher);
+ }
+ }
+
+ public Collection findModuleRevisionIds(DependencyResolver resolver, ModuleRevisionId pattern,
+ PatternMatcher matcher) {
+ Collection mrids = new ArrayList();
+ String resolverName = resolver.getName();
+
+ Message.verbose("looking for modules matching " + pattern + " using " + matcher.getName());
+ Namespace fromNamespace = null;
+ if (resolver instanceof AbstractResolver) {
+ fromNamespace = ((AbstractResolver) resolver).getNamespace();
+ }
+
+ Collection modules = new ArrayList();
+
+ OrganisationEntry[] orgs = resolver.listOrganisations();
+ if (orgs == null || orgs.length == 0) {
+ // hack for resolvers which are not able to list organisation, we try to see if the
+ // asked organisation is not an exact one:
+ String org = pattern.getOrganisation();
+ if (fromNamespace != null) {
+ org = NameSpaceHelper.transform(pattern.getModuleId(),
+ fromNamespace.getFromSystemTransformer()).getOrganisation();
+ }
+ modules.addAll(Arrays.asList(resolver.listModules(new OrganisationEntry(resolver, org))));
+ } else {
+ Matcher orgMatcher = matcher.getMatcher(pattern.getOrganisation());
+ for (int i = 0; i < orgs.length; i++) {
+ String org = orgs[i].getOrganisation();
+ String systemOrg = org;
+ if (fromNamespace != null) {
+ systemOrg = NameSpaceHelper.transformOrganisation(org,
+ fromNamespace.getToSystemTransformer());
+ }
+ if (orgMatcher.matches(systemOrg)) {
+ modules.addAll(Arrays.asList(resolver.listModules(new OrganisationEntry(
+ resolver, org))));
+ }
+ }
+ }
+ Message.debug("found " + modules.size() + " modules for " + pattern.getOrganisation()
+ + " on " + resolverName);
+ boolean foundModule = false;
+ for (Iterator iter = modules.iterator(); iter.hasNext();) {
+ ModuleEntry mEntry = (ModuleEntry) iter.next();
+
+ ModuleId foundMid = new ModuleId(mEntry.getOrganisation(), mEntry.getModule());
+ ModuleId systemMid = foundMid;
+ if (fromNamespace != null) {
+ systemMid = NameSpaceHelper.transform(foundMid,
+ fromNamespace.getToSystemTransformer());
+ }
+
+ if (MatcherHelper.matches(matcher, pattern.getModuleId(), systemMid)) {
+ // The module corresponds to the searched module pattern
+ foundModule = true;
+ RevisionEntry[] rEntries = resolver.listRevisions(mEntry);
+ Message.debug("found " + rEntries.length + " revisions for ["
+ + mEntry.getOrganisation() + ", " + mEntry.getModule() + "] on "
+ + resolverName);
+
+ boolean foundRevision = false;
+ for (int j = 0; j < rEntries.length; j++) {
+ RevisionEntry rEntry = rEntries[j];
+
+ ModuleRevisionId foundMrid = ModuleRevisionId.newInstance(
+ mEntry.getOrganisation(), mEntry.getModule(), rEntry.getRevision());
+ ModuleRevisionId systemMrid = foundMrid;
+ if (fromNamespace != null) {
+ systemMrid = fromNamespace.getToSystemTransformer().transform(foundMrid);
+ }
+
+ if (MatcherHelper.matches(matcher, pattern, systemMrid)) {
+ // We have a matching module revision
+ foundRevision = true;
+ mrids.add(systemMrid);
+ }
+ }
+ if (!foundRevision) {
+ Message.debug("no revision found matching " + pattern + " in ["
+ + mEntry.getOrganisation() + "," + mEntry.getModule() + "] using "
+ + resolverName);
+ }
+ }
+ }
+ if (!foundModule) {
+ Message.debug("no module found matching " + pattern + " using " + resolverName);
+ }
+ return mrids;
+ }
+
+}
diff --git a/src/java/org/apache/ivy/core/settings/IvyPattern.java b/src/java/org/apache/ivy/core/settings/IvyPattern.java
new file mode 100644
index 0000000..1464714
--- /dev/null
+++ b/src/java/org/apache/ivy/core/settings/IvyPattern.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.settings;
+
+/**
+ *
+ */
+public class IvyPattern {
+ private String pattern;
+
+ public String getPattern() {
+ return pattern;
+ }
+
+ public void setPattern(String pattern) {
+ this.pattern = pattern;
+ }
+}
diff --git a/src/java/org/apache/ivy/core/settings/IvySettings.java b/src/java/org/apache/ivy/core/settings/IvySettings.java
new file mode 100644
index 0000000..1ef8dc7
--- /dev/null
+++ b/src/java/org/apache/ivy/core/settings/IvySettings.java
@@ -0,0 +1,1516 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.settings;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Field;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.security.AccessControlException;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.ivy.Ivy;
+import org.apache.ivy.core.IvyPatternHelper;
+import org.apache.ivy.core.NormalRelativeUrlResolver;
+import org.apache.ivy.core.RelativeUrlResolver;
+import org.apache.ivy.core.cache.CacheUtil;
+import org.apache.ivy.core.cache.DefaultRepositoryCacheManager;
+import org.apache.ivy.core.cache.DefaultResolutionCacheManager;
+import org.apache.ivy.core.cache.RepositoryCacheManager;
+import org.apache.ivy.core.cache.ResolutionCacheManager;
+import org.apache.ivy.core.check.CheckEngineSettings;
+import org.apache.ivy.core.deliver.DeliverEngineSettings;
+import org.apache.ivy.core.install.InstallEngineSettings;
+import org.apache.ivy.core.module.id.ModuleId;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.module.id.ModuleRules;
+import org.apache.ivy.core.module.status.StatusManager;
+import org.apache.ivy.core.pack.ArchivePacking;
+import org.apache.ivy.core.pack.PackingRegistry;
+import org.apache.ivy.core.publish.PublishEngineSettings;
+import org.apache.ivy.core.repository.RepositoryManagementEngineSettings;
+import org.apache.ivy.core.resolve.ResolveEngineSettings;
+import org.apache.ivy.core.resolve.ResolveOptions;
+import org.apache.ivy.core.retrieve.RetrieveEngineSettings;
+import org.apache.ivy.core.sort.SortEngineSettings;
+import org.apache.ivy.osgi.core.OsgiLatestStrategy;
+import org.apache.ivy.plugins.IvySettingsAware;
+import org.apache.ivy.plugins.circular.CircularDependencyStrategy;
+import org.apache.ivy.plugins.circular.ErrorCircularDependencyStrategy;
+import org.apache.ivy.plugins.circular.IgnoreCircularDependencyStrategy;
+import org.apache.ivy.plugins.circular.WarnCircularDependencyStrategy;
+import org.apache.ivy.plugins.conflict.ConflictManager;
+import org.apache.ivy.plugins.conflict.LatestCompatibleConflictManager;
+import org.apache.ivy.plugins.conflict.LatestConflictManager;
+import org.apache.ivy.plugins.conflict.NoConflictManager;
+import org.apache.ivy.plugins.conflict.StrictConflictManager;
+import org.apache.ivy.plugins.latest.LatestLexicographicStrategy;
+import org.apache.ivy.plugins.latest.LatestRevisionStrategy;
+import org.apache.ivy.plugins.latest.LatestStrategy;
+import org.apache.ivy.plugins.latest.LatestTimeStrategy;
+import org.apache.ivy.plugins.lock.CreateFileLockStrategy;
+import org.apache.ivy.plugins.lock.LockStrategy;
+import org.apache.ivy.plugins.lock.NIOFileLockStrategy;
+import org.apache.ivy.plugins.lock.NoLockStrategy;
+import org.apache.ivy.plugins.matcher.ExactOrRegexpPatternMatcher;
+import org.apache.ivy.plugins.matcher.ExactPatternMatcher;
+import org.apache.ivy.plugins.matcher.MapMatcher;
+import org.apache.ivy.plugins.matcher.PatternMatcher;
+import org.apache.ivy.plugins.matcher.RegexpPatternMatcher;
+import org.apache.ivy.plugins.namespace.Namespace;
+import org.apache.ivy.plugins.parser.ModuleDescriptorParser;
+import org.apache.ivy.plugins.parser.ModuleDescriptorParserRegistry;
+import org.apache.ivy.plugins.parser.ParserSettings;
+import org.apache.ivy.plugins.report.LogReportOutputter;
+import org.apache.ivy.plugins.report.ReportOutputter;
+import org.apache.ivy.plugins.report.XmlReportOutputter;
+import org.apache.ivy.plugins.resolver.ChainResolver;
+import org.apache.ivy.plugins.resolver.DependencyResolver;
+import org.apache.ivy.plugins.resolver.DualResolver;
+import org.apache.ivy.plugins.resolver.ResolverSettings;
+import org.apache.ivy.plugins.signer.SignatureGenerator;
+import org.apache.ivy.plugins.trigger.Trigger;
+import org.apache.ivy.plugins.version.ChainVersionMatcher;
+import org.apache.ivy.plugins.version.ExactVersionMatcher;
+import org.apache.ivy.plugins.version.LatestVersionMatcher;
+import org.apache.ivy.plugins.version.SubVersionMatcher;
+import org.apache.ivy.plugins.version.VersionMatcher;
+import org.apache.ivy.plugins.version.VersionRangeMatcher;
+import org.apache.ivy.util.Checks;
+import org.apache.ivy.util.FileUtil;
+import org.apache.ivy.util.Message;
+import org.apache.ivy.util.filter.Filter;
+import org.apache.ivy.util.url.URLHandlerRegistry;
+
+public class IvySettings implements SortEngineSettings, PublishEngineSettings, ParserSettings,
+ DeliverEngineSettings, CheckEngineSettings, InstallEngineSettings, ResolverSettings,
+ ResolveEngineSettings, RetrieveEngineSettings, RepositoryManagementEngineSettings {
+ private static final long INTERUPT_TIMEOUT = 2000;
+
+ private Map typeDefs = new HashMap();
+
+ private Map resolversMap = new HashMap();
+
+ private DependencyResolver defaultResolver;
+
+ private DependencyResolver dictatorResolver = null;
+
+ private String defaultResolverName;
+
+ private File defaultCache;
+
+ private String defaultBranch = null;
+
+ private boolean checkUpToDate = true;
+
+ private ModuleRules moduleSettings = new ModuleRules();
+
+ // Map (String conflictManagerName -> ConflictManager)
+ private Map conflictsManager = new HashMap();
+
+ // Map (String latestStrategyName -> LatestStrategy)
+ private Map latestStrategies = new HashMap();
+
+ // Map (String name -> LockStrategy)
+ private Map lockStrategies = new HashMap();
+
+ // Map (String namespaceName -> Namespace)
+ private Map namespaces = new HashMap();
+
+ // Map (String matcherName -> Matcher)
+ private Map matchers = new HashMap();
+
+ // Map (String outputterName -> ReportOutputter)
+ private Map reportOutputters = new HashMap();
+
+ // Map (String matcherName -> VersionMatcher)
+ private Map versionMatchers = new HashMap();
+
+ // Map (String name -> CircularDependencyStrategy)
+ private Map circularDependencyStrategies = new HashMap();
+
+ // Map (String name -> RepositoryCacheManager)
+ private Map repositoryCacheManagers = new HashMap();
+
+ // Map (String name -> SignatureGenerator)
+ private Map signatureGenerators = new HashMap();
+
+ // List (Trigger)
+ private List triggers = new ArrayList();
+
+ private IvyVariableContainer variableContainer = new IvyVariableContainerImpl();
+
+ private boolean validate = true;
+
+ private LatestStrategy defaultLatestStrategy = null;
+
+ private LockStrategy defaultLockStrategy = null;
+
+ private ConflictManager defaultConflictManager = null;
+
+ private CircularDependencyStrategy circularDependencyStrategy = null;
+
+ private RepositoryCacheManager defaultRepositoryCacheManager = null;
+
+ private ResolutionCacheManager resolutionCacheManager = null;
+
+ private List listingIgnore = new ArrayList();
+
+ private boolean repositoriesConfigured;
+
+ private boolean useRemoteConfig = false;
+
+ private File defaultUserDir;
+
+ private File baseDir = new File(".").getAbsoluteFile();
+
+ private List classpathURLs = new ArrayList();
+
+ private ClassLoader classloader;
+
+ private Boolean debugConflictResolution;
+
+ private boolean logNotConvertedExclusionRule;
+
+ private VersionMatcher versionMatcher;
+
+ private StatusManager statusManager;
+
+ private Boolean debugLocking;
+
+ private Boolean dumpMemoryUsage;
+
+ private String defaultCacheIvyPattern;
+
+ private String defaultCacheArtifactPattern;
+
+ private boolean defaultUseOrigin;
+
+ private String defaultResolveMode = ResolveOptions.RESOLVEMODE_DEFAULT;
+
+ private PackingRegistry packingRegistry = new PackingRegistry();
+
+ public IvySettings() {
+ this(new IvyVariableContainerImpl());
+ }
+
+ public IvySettings(IvyVariableContainer variableContainer) {
+ setVariableContainer(variableContainer);
+ setVariable("ivy.default.settings.dir", getDefaultSettingsDir(), true);
+ setVariable("ivy.basedir", getBaseDir().getAbsolutePath());
+ setDeprecatedVariable("ivy.default.conf.dir", "ivy.default.settings.dir");
+
+ String ivyTypeDefs = System.getProperty("ivy.typedef.files");
+ if (ivyTypeDefs != null) {
+ String[] files = ivyTypeDefs.split("\\,");
+ for (int i = 0; i < files.length; i++) {
+ try {
+ typeDefs(
+ new FileInputStream(Checks.checkAbsolute(files[i].trim(),
+ "ivy.typedef.files")), true);
+ } catch (FileNotFoundException e) {
+ Message.warn("typedefs file not found: " + files[i].trim());
+ } catch (IOException e) {
+ Message.warn("problem with typedef file: " + files[i].trim(), e);
+ }
+ }
+ } else {
+ try {
+ typeDefs(getSettingsURL("typedef.properties").openStream(), true);
+ } catch (IOException e) {
+ Message.warn("impossible to load default type defs", e);
+ }
+ }
+ LatestLexicographicStrategy latestLexicographicStrategy = new LatestLexicographicStrategy();
+ LatestRevisionStrategy latestRevisionStrategy = new LatestRevisionStrategy();
+ LatestTimeStrategy latestTimeStrategy = new LatestTimeStrategy();
+ OsgiLatestStrategy osgiLatestStrategy = new OsgiLatestStrategy();
+
+ addLatestStrategy("latest-revision", latestRevisionStrategy);
+ addLatestStrategy("latest-lexico", latestLexicographicStrategy);
+ addLatestStrategy("latest-time", latestTimeStrategy);
+ addLatestStrategy("latest-osgi", osgiLatestStrategy);
+
+ addLockStrategy("no-lock", new NoLockStrategy());
+ addLockStrategy("artifact-lock", new CreateFileLockStrategy(debugLocking()));
+ addLockStrategy("artifact-lock-nio", new NIOFileLockStrategy(debugLocking()));
+
+ addConflictManager("latest-revision", new LatestConflictManager("latest-revision",
+ latestRevisionStrategy));
+ addConflictManager("latest-compatible", new LatestCompatibleConflictManager(
+ "latest-compatible", latestRevisionStrategy));
+ addConflictManager("latest-time", new LatestConflictManager("latest-time",
+ latestTimeStrategy));
+ addConflictManager("all", new NoConflictManager());
+ addConflictManager("strict", new StrictConflictManager());
+
+ addMatcher(ExactPatternMatcher.INSTANCE);
+ addMatcher(RegexpPatternMatcher.INSTANCE);
+ addMatcher(ExactOrRegexpPatternMatcher.INSTANCE);
+
+ try {
+ // GlobPatternMatcher is optional. Only add it when available.
+ Class globClazz = IvySettings.class.getClassLoader().loadClass(
+ "org.apache.ivy.plugins.matcher.GlobPatternMatcher");
+ Field instanceField = globClazz.getField("INSTANCE");
+ addMatcher((PatternMatcher) instanceField.get(null));
+ } catch (Exception e) {
+ // ignore: the matcher isn't on the classpath
+ Message.info("impossible to define glob matcher: "
+ + "org.apache.ivy.plugins.matcher.GlobPatternMatcher was not found", e);
+ }
+
+ addReportOutputter(new LogReportOutputter());
+ addReportOutputter(new XmlReportOutputter());
+
+ configureDefaultCircularDependencyStrategies();
+
+ listingIgnore.add(".cvsignore");
+ listingIgnore.add("CVS");
+ listingIgnore.add(".svn");
+ listingIgnore.add("maven-metadata.xml");
+ listingIgnore.add("maven-metadata.xml.md5");
+ listingIgnore.add("maven-metadata.xml.sha1");
+
+ addSystemProperties();
+ }
+
+ private synchronized void addSystemProperties() {
+ try {
+ addAllVariables((Map) System.getProperties().clone());
+ } catch (AccessControlException ex) {
+ Message.verbose("access denied to getting all system properties: they won't be available as Ivy variables."
+ + "\nset " + ex.getPermission() + " permission if you want to access them");
+ }
+ }
+
+ /**
+ * Call this method to ask ivy to configure some variables using either a remote or a local
+ * properties file
+ */
+ public synchronized void configureRepositories(boolean remote) {
+ if (!repositoriesConfigured) {
+ Properties props = new Properties();
+ boolean configured = false;
+ if (useRemoteConfig && remote) {
+ try {
+ URL url = new URL("http://ant.apache.org/ivy/repository.properties");
+ Message.verbose("configuring repositories with " + url);
+ props.load(URLHandlerRegistry.getDefault().openStream(url));
+ configured = true;
+ } catch (Exception ex) {
+ Message.verbose("unable to use remote repository configuration", ex);
+ props = new Properties();
+ }
+ }
+ if (!configured) {
+ InputStream repositoryPropsStream = null;
+ try {
+ repositoryPropsStream = getSettingsURL("repository.properties").openStream();
+ props.load(repositoryPropsStream);
+ } catch (IOException e) {
+ Message.error("unable to use internal repository configuration", e);
+ if (repositoryPropsStream != null) {
+ try {
+ repositoryPropsStream.close();
+ } catch (Exception ex) {
+ // nothing to do
+ }
+ }
+ }
+ }
+ addAllVariables(props, false);
+ repositoriesConfigured = true;
+ }
+ }
+
+ public synchronized void typeDefs(InputStream stream) throws IOException {
+ typeDefs(stream, false);
+ }
+
+ public synchronized void typeDefs(InputStream stream, boolean silentFail) throws IOException {
+ try {
+ Properties p = new Properties();
+ p.load(stream);
+ typeDefs(p, silentFail);
+ } finally {
+ stream.close();
+ }
+ }
+
+ public synchronized void typeDefs(Properties p) {
+ typeDefs(p, false);
+ }
+
+ public synchronized void typeDefs(Properties p, boolean silentFail) {
+ for (Iterator iter = p.keySet().iterator(); iter.hasNext();) {
+ String name = (String) iter.next();
+ typeDef(name, p.getProperty(name), silentFail);
+ }
+ }
+
+ public synchronized void load(File settingsFile) throws ParseException, IOException {
+ Message.info(":: loading settings :: file = " + settingsFile);
+ long start = System.currentTimeMillis();
+ setSettingsVariables(settingsFile);
+ if (getVariable("ivy.default.ivy.user.dir") != null) {
+ setDefaultIvyUserDir(Checks.checkAbsolute(getVariable("ivy.default.ivy.user.dir"),
+ "ivy.default.ivy.user.dir"));
+ } else {
+ getDefaultIvyUserDir();
+ }
+
+ loadDefaultProperties();
+ try {
+ new XmlSettingsParser(this).parse(settingsFile.toURI().toURL());
+ } catch (MalformedURLException e) {
+ IllegalArgumentException iae = new IllegalArgumentException(
+ "given file cannot be transformed to url: " + settingsFile);
+ iae.initCause(e);
+ throw iae;
+ }
+ setVariable("ivy.default.ivy.user.dir", getDefaultIvyUserDir().getAbsolutePath(), false);
+ Message.verbose("settings loaded (" + (System.currentTimeMillis() - start) + "ms)");
+ dumpSettings();
+ }
+
+ public synchronized void load(URL settingsURL) throws ParseException, IOException {
+ Message.info(":: loading settings :: url = " + settingsURL);
+ long start = System.currentTimeMillis();
+ setSettingsVariables(settingsURL);
+ if (getVariable("ivy.default.ivy.user.dir") != null) {
+ setDefaultIvyUserDir(Checks.checkAbsolute(getVariable("ivy.default.ivy.user.dir"),
+ "ivy.default.ivy.user.dir"));
+ } else {
+ getDefaultIvyUserDir();
+ }
+
+ loadDefaultProperties();
+ new XmlSettingsParser(this).parse(settingsURL);
+ setVariable("ivy.default.ivy.user.dir", getDefaultIvyUserDir().getAbsolutePath(), false);
+ Message.verbose("settings loaded (" + (System.currentTimeMillis() - start) + "ms)");
+ dumpSettings();
+ }
+
+ /**
+ * Default initialization of settings, useful when you don't want to load your settings from a
+ * settings file or URL, but prefer to set them manually. By calling this method you will still
+ * have the basic initialization done when loading settings.
+ *
+ * @throws IOException
+ */
+ public synchronized void defaultInit() throws IOException {
+ if (getVariable("ivy.default.ivy.user.dir") != null) {
+ setDefaultIvyUserDir(Checks.checkAbsolute(getVariable("ivy.default.ivy.user.dir"),
+ "ivy.default.ivy.user.dir"));
+ } else {
+ getDefaultIvyUserDir();
+ }
+ getDefaultCache();
+
+ loadDefaultProperties();
+ setVariable("ivy.default.ivy.user.dir", getDefaultIvyUserDir().getAbsolutePath(), false);
+ dumpSettings();
+ }
+
+ public synchronized void loadDefault() throws ParseException, IOException {
+ load(getDefaultSettingsURL());
+ }
+
+ public synchronized void loadDefault14() throws ParseException, IOException {
+ load(getDefault14SettingsURL());
+ }
+
+ private void loadDefaultProperties() throws IOException {
+ loadProperties(getDefaultPropertiesURL(), false);
+ }
+
+ public static URL getDefaultPropertiesURL() {
+ return getSettingsURL("ivy.properties");
+ }
+
+ public static URL getDefaultSettingsURL() {
+ return getSettingsURL("ivysettings.xml");
+ }
+
+ public static URL getDefault14SettingsURL() {
+ return getSettingsURL("ivysettings-1.4.xml");
+ }
+
+ private String getDefaultSettingsDir() {
+ String ivysettingsLocation = getDefaultSettingsURL().toExternalForm();
+ return ivysettingsLocation.substring(0,
+ ivysettingsLocation.length() - "ivysettings.xml".length() - 1);
+ }
+
+ private static URL getSettingsURL(String file) {
+ return XmlSettingsParser.class.getResource(file);
+ }
+
+ public synchronized void setSettingsVariables(File settingsFile) {
+ try {
+ setVariable("ivy.settings.dir", new File(settingsFile.getAbsolutePath()).getParent());
+ setDeprecatedVariable("ivy.conf.dir", "ivy.settings.dir");
+ setVariable("ivy.settings.file", settingsFile.getAbsolutePath());
+ setDeprecatedVariable("ivy.conf.file", "ivy.settings.file");
+ setVariable("ivy.settings.url", settingsFile.toURI().toURL().toExternalForm());
+ setDeprecatedVariable("ivy.conf.url", "ivy.settings.url");
+ setVariable("ivy.settings.dir.url", new File(settingsFile.getAbsolutePath())
+ .getParentFile().toURI().toURL().toExternalForm());
+ } catch (MalformedURLException e) {
+ IllegalArgumentException iae = new IllegalArgumentException(
+ "given file cannot be transformed to url: " + settingsFile);
+ iae.initCause(e);
+ throw iae;
+ }
+ }
+
+ /**
+ * Sets a deprecated variable with the value of the new variable
+ *
+ * @param deprecatedKey
+ * the deprecated variable name
+ * @param newKey
+ * the new variable name
+ */
+ private void setDeprecatedVariable(String deprecatedKey, String newKey) {
+ setVariable(deprecatedKey, getVariable(newKey));
+ }
+
+ public synchronized void setSettingsVariables(URL settingsURL) {
+ String settingsURLStr = settingsURL.toExternalForm();
+ setVariable("ivy.settings.url", settingsURLStr);
+ setDeprecatedVariable("ivy.conf.url", "ivy.settings.url");
+ int slashIndex = settingsURLStr.lastIndexOf('/');
+ if (slashIndex != -1) {
+ String dirUrl = settingsURLStr.substring(0, slashIndex);
+ setVariable("ivy.settings.dir", dirUrl);
+ setVariable("ivy.settings.dir.url", dirUrl);
+ setDeprecatedVariable("ivy.conf.dir", "ivy.settings.dir");
+ } else {
+ Message.warn("settings url does not contain any slash (/): "
+ + "ivy.settings.dir variable not set");
+ }
+ }
+
+ private void dumpSettings() {
+ Message.verbose("\tdefault cache: " + getDefaultCache());
+ Message.verbose("\tdefault resolver: " + getDefaultResolver());
+ Message.debug("\tdefault latest strategy: " + getDefaultLatestStrategy());
+ Message.debug("\tdefault conflict manager: " + getDefaultConflictManager());
+ Message.debug("\tcircular dependency strategy: " + getCircularDependencyStrategy());
+ Message.debug("\tvalidate: " + doValidate());
+ Message.debug("\tcheck up2date: " + isCheckUpToDate());
+
+ if (!classpathURLs.isEmpty()) {
+ Message.verbose("\t-- " + classpathURLs.size() + " custom classpath urls:");
+ for (Iterator iter = classpathURLs.iterator(); iter.hasNext();) {
+ Message.debug("\t\t" + iter.next());
+ }
+ }
+ Message.verbose("\t-- " + resolversMap.size() + " resolvers:");
+ for (Iterator iter = resolversMap.values().iterator(); iter.hasNext();) {
+ DependencyResolver resolver = (DependencyResolver) iter.next();
+ resolver.dumpSettings();
+ }
+ Message.debug("\tmodule settings:");
+ moduleSettings.dump("\t\t");
+ }
+
+ public synchronized void loadProperties(URL url) throws IOException {
+ loadProperties(url, true);
+ }
+
+ public synchronized void loadProperties(URL url, boolean overwrite) throws IOException {
+ loadProperties(url.openStream(), overwrite);
+ }
+
+ public synchronized void loadProperties(File file) throws IOException {
+ loadProperties(file, true);
+ }
+
+ public synchronized void loadProperties(File file, boolean overwrite) throws IOException {
+ loadProperties(new FileInputStream(file), overwrite);
+ }
+
+ private void loadProperties(InputStream stream, boolean overwrite) throws IOException {
+ try {
+ Properties properties = new Properties();
+ properties.load(stream);
+ addAllVariables(properties, overwrite);
+ } finally {
+ if (stream != null) {
+ try {
+ stream.close();
+ } catch (IOException e) {
+ // nothing
+ }
+ }
+ }
+ }
+
+ public synchronized void setVariable(String varName, String value) {
+ setVariable(varName, value, true);
+ }
+
+ public synchronized void setVariable(String varName, String value, boolean overwrite) {
+ setVariable(varName, value, overwrite, null, null);
+ }
+
+ public synchronized void setVariable(String varName, String value, boolean overwrite,
+ String ifSetVar, String unlessSetVar) {
+ if (ifSetVar != null && variableContainer.getVariable(ifSetVar) == null) {
+ Message.verbose("Not setting '" + varName + "' to '" + value + "' since '" + ifSetVar
+ + "' is not set.");
+ return;
+ }
+ if (unlessSetVar != null && variableContainer.getVariable(unlessSetVar) != null) {
+ Message.verbose("Not setting '" + varName + "' to '" + value + "' since '"
+ + unlessSetVar + "' is set.");
+ return;
+ }
+ variableContainer.setVariable(varName, value, overwrite);
+ }
+
+ public synchronized void addAllVariables(Map variables) {
+ addAllVariables(variables, true);
+ }
+
+ public synchronized void addAllVariables(Map<?, ?> variables, boolean overwrite) {
+ for (Map.Entry<?, ?> entry : variables.entrySet()) {
+ String key = entry.getKey().toString();
+ Object val = entry.getValue();
+ if (val == null || val instanceof String) {
+ setVariable(key, (String) val, overwrite);
+ }
+ }
+ }
+
+ /**
+ * Substitute variables in the given string by their value found in the current set of variables
+ *
+ * @param str
+ * the string in which substitution should be made
+ * @return the string where all current ivy variables have been substituted by their value If
+ * the input str doesn't use any variable, the same object is returned
+ */
+ public synchronized String substitute(String str) {
+ return IvyPatternHelper.substituteVariables(str, variableContainer);
+ }
+
+ /**
+ * Substitute variables in the given map values by their value found in the current set of
+ * variables
+ *
+ * @param strings
+ * the map of strings in which substitution should be made
+ * @return a new map of strings in which all current ivy variables in values have been
+ * substituted by their value
+ */
+ public synchronized Map/* <String, String> */substitute(Map/* <String, String> */strings) {
+ Map substituted = new LinkedHashMap();
+ for (Iterator it = strings.entrySet().iterator(); it.hasNext();) {
+ Map.Entry entry = (Map.Entry) it.next();
+ substituted.put(entry.getKey(), substitute((String) entry.getValue()));
+ }
+ return substituted;
+ }
+
+ /**
+ * Returns the variables loaded in configuration file. Those variables may better be seen as ant
+ * properties
+ *
+ * @return
+ */
+ public synchronized IvyVariableContainer getVariables() {
+ return variableContainer;
+ }
+
+ public synchronized Class typeDef(String name, String className) {
+ return typeDef(name, className, false);
+ }
+
+ public synchronized Class typeDef(String name, String className, boolean silentFail) {
+ Class clazz = classForName(className, silentFail);
+ if (clazz != null) {
+ typeDefs.put(name, clazz);
+ }
+ return clazz;
+ }
+
+ private Class classForName(String className, boolean silentFail) {
+ try {
+ return getClassLoader().loadClass(className);
+ } catch (ClassNotFoundException e) {
+ if (silentFail) {
+ Message.info("impossible to define new type: class not found: " + className
+ + " in " + classpathURLs + " nor Ivy classloader");
+ return null;
+ } else {
+ throw new RuntimeException("impossible to define new type: class not found: "
+ + className + " in " + classpathURLs + " nor Ivy classloader");
+ }
+ }
+ }
+
+ private ClassLoader getClassLoader() {
+ if (classloader == null) {
+ if (classpathURLs.isEmpty()) {
+ classloader = Ivy.class.getClassLoader();
+ } else {
+ classloader = new URLClassLoader(
+ (URL[]) classpathURLs.toArray(new URL[classpathURLs.size()]),
+ Ivy.class.getClassLoader());
+ }
+ }
+ return classloader;
+ }
+
+ public synchronized void addClasspathURL(URL url) {
+ classpathURLs.add(url);
+ classloader = null;
+ }
+
+ public synchronized Map getTypeDefs() {
+ return typeDefs;
+ }
+
+ public synchronized Class getTypeDef(String name) {
+ return (Class) typeDefs.get(name);
+ }
+
+ // methods which match ivy conf method signature specs
+ public synchronized void addConfigured(DependencyResolver resolver) {
+ addResolver(resolver);
+ }
+
+ public synchronized void addConfigured(ModuleDescriptorParser parser) {
+ ModuleDescriptorParserRegistry.getInstance().addParser(parser);
+ }
+
+ public synchronized void addConfigured(SignatureGenerator generator) {
+ addSignatureGenerator(generator);
+ }
+
+ public synchronized void addSignatureGenerator(SignatureGenerator generator) {
+ init(generator);
+ signatureGenerators.put(generator.getName(), generator);
+ }
+
+ public synchronized SignatureGenerator getSignatureGenerator(String name) {
+ return (SignatureGenerator) signatureGenerators.get(name);
+ }
+
+ public synchronized void addResolver(DependencyResolver resolver) {
+ if (resolver == null) {
+ throw new NullPointerException("null resolver");
+ }
+ init(resolver);
+ resolversMap.put(resolver.getName(), resolver);
+ if (resolver instanceof ChainResolver) {
+ List subresolvers = ((ChainResolver) resolver).getResolvers();
+ for (Iterator iter = subresolvers.iterator(); iter.hasNext();) {
+ DependencyResolver dr = (DependencyResolver) iter.next();
+ addResolver(dr);
+ }
+ } else if (resolver instanceof DualResolver) {
+ DependencyResolver ivyResolver = ((DualResolver) resolver).getIvyResolver();
+ if (ivyResolver != null) {
+ addResolver(ivyResolver);
+ }
+ DependencyResolver artifactResolver = ((DualResolver) resolver).getArtifactResolver();
+ if (artifactResolver != null) {
+ addResolver(artifactResolver);
+ }
+ }
+ }
+
+ public synchronized void setDefaultCache(File cacheDirectory) {
+ setVariable("ivy.cache.dir", cacheDirectory.getAbsolutePath(), false);
+ defaultCache = cacheDirectory;
+ if (defaultRepositoryCacheManager != null) {
+ if ("default-cache".equals(defaultRepositoryCacheManager.getName())
+ && defaultRepositoryCacheManager instanceof DefaultRepositoryCacheManager) {
+ ((DefaultRepositoryCacheManager) defaultRepositoryCacheManager)
+ .setBasedir(defaultCache);
+ }
+ }
+ }
+
+ public synchronized void setDefaultResolver(String resolverName) {
+ checkResolverName(resolverName);
+ if (resolverName != null && !resolverName.equals(defaultResolverName)) {
+ defaultResolver = null;
+ }
+ defaultResolverName = resolverName;
+ }
+
+ private void checkResolverName(String resolverName) {
+ if (resolverName != null && !resolversMap.containsKey(resolverName)) {
+ throw new IllegalArgumentException("no resolver found called " + resolverName
+ + ": check your settings");
+ }
+ }
+
+ /**
+ * regular expressions as explained in Pattern class may be used in attributes
+ */
+ public synchronized void addModuleConfiguration(Map attributes, PatternMatcher matcher,
+ String resolverName, String branch, String conflictManager, String resolveMode) {
+ checkResolverName(resolverName);
+ moduleSettings.defineRule(new MapMatcher(attributes, matcher), new ModuleSettings(
+ resolverName, branch, conflictManager, resolveMode));
+ }
+
+ /**
+ * Return the canonical form of a filename.
+ * <p>
+ * If the specified file name is relative it is resolved with respect to the settings's base
+ * directory.
+ *
+ * @param fileName
+ * The name of the file to resolve. Must not be <code>null</code>.
+ *
+ * @return the resolved File.
+ *
+ */
+ public synchronized File resolveFile(String fileName) {
+ return FileUtil.resolveFile(baseDir, fileName);
+ }
+
+ public synchronized void setBaseDir(File baseDir) {
+ this.baseDir = baseDir.getAbsoluteFile();
+ setVariable("ivy.basedir", this.baseDir.getAbsolutePath());
+ setVariable("basedir", this.baseDir.getAbsolutePath(), false);
+ }
+
+ public synchronized File getBaseDir() {
+ return baseDir;
+ }
+
+ public synchronized File getDefaultIvyUserDir() {
+ if (defaultUserDir == null) {
+ if (getVariable("ivy.home") != null) {
+ setDefaultIvyUserDir(Checks.checkAbsolute(getVariable("ivy.home"), "ivy.home"));
+ Message.verbose("using ivy.default.ivy.user.dir variable for default ivy user dir: "
+ + defaultUserDir);
+ } else {
+ setDefaultIvyUserDir(new File(System.getProperty("user.home"), ".ivy2"));
+ Message.verbose("no default ivy user dir defined: set to " + defaultUserDir);
+ }
+ }
+ return defaultUserDir;
+ }
+
+ public synchronized void setDefaultIvyUserDir(File defaultUserDir) {
+ this.defaultUserDir = defaultUserDir;
+ setVariable("ivy.default.ivy.user.dir", this.defaultUserDir.getAbsolutePath());
+ setVariable("ivy.home", this.defaultUserDir.getAbsolutePath());
+ }
+
+ public synchronized File getDefaultCache() {
+ if (defaultCache == null) {
+ String cache = getVariable("ivy.cache.dir");
+ if (cache != null) {
+ defaultCache = Checks.checkAbsolute(cache, "ivy.cache.dir");
+ } else {
+ setDefaultCache(new File(getDefaultIvyUserDir(), "cache"));
+ Message.verbose("no default cache defined: set to " + defaultCache);
+ }
+ }
+ return defaultCache;
+ }
+
+ public synchronized void setDefaultRepositoryCacheBasedir(String repositoryCacheRoot) {
+ setVariable("ivy.cache.repository", repositoryCacheRoot, true);
+ if (defaultRepositoryCacheManager != null
+ && "default-cache".equals(defaultRepositoryCacheManager.getName())
+ && defaultRepositoryCacheManager instanceof DefaultRepositoryCacheManager) {
+ ((DefaultRepositoryCacheManager) defaultRepositoryCacheManager)
+ .setBasedir(getDefaultRepositoryCacheBasedir());
+ }
+ }
+
+ public synchronized void setDefaultResolutionCacheBasedir(String resolutionCacheRoot) {
+ setVariable("ivy.cache.resolution", resolutionCacheRoot, true);
+ if (resolutionCacheManager != null
+ && resolutionCacheManager instanceof DefaultResolutionCacheManager) {
+ ((DefaultResolutionCacheManager) resolutionCacheManager)
+ .setBasedir(getDefaultResolutionCacheBasedir());
+ }
+ }
+
+ public synchronized File getDefaultRepositoryCacheBasedir() {
+ String repositoryCacheRoot = getVariable("ivy.cache.repository");
+ if (repositoryCacheRoot != null) {
+ return Checks.checkAbsolute(repositoryCacheRoot, "ivy.cache.repository");
+ } else {
+ return getDefaultCache();
+ }
+ }
+
+ public synchronized File getDefaultResolutionCacheBasedir() {
+ String resolutionCacheRoot = getVariable("ivy.cache.resolution");
+ if (resolutionCacheRoot != null) {
+ return Checks.checkAbsolute(resolutionCacheRoot, "ivy.cache.resolution");
+ } else {
+ return getDefaultCache();
+ }
+ }
+
+ public synchronized void setDictatorResolver(DependencyResolver resolver) {
+ dictatorResolver = resolver;
+ }
+
+ public synchronized DependencyResolver getResolver(ModuleRevisionId mrid) {
+ if (dictatorResolver != null) {
+ return dictatorResolver;
+ }
+ String resolverName = getResolverName(mrid);
+ return getResolver(resolverName);
+ }
+
+ public synchronized boolean hasResolver(String resolverName) {
+ return resolversMap.containsKey(resolverName);
+ }
+
+ public synchronized DependencyResolver getResolver(String resolverName) {
+ if (dictatorResolver != null) {
+ return dictatorResolver;
+ }
+ DependencyResolver resolver = (DependencyResolver) resolversMap.get(resolverName);
+ if (resolver == null) {
+ Message.error("unknown resolver " + resolverName);
+ }
+ return resolver;
+ }
+
+ public synchronized DependencyResolver getDefaultResolver() {
+ if (dictatorResolver != null) {
+ return dictatorResolver;
+ }
+ if (defaultResolver == null) {
+ defaultResolver = (DependencyResolver) resolversMap.get(defaultResolverName);
+ }
+ return defaultResolver;
+ }
+
+ public synchronized String getResolverName(ModuleRevisionId mrid) {
+ ModuleSettings ms = (ModuleSettings) moduleSettings.getRule(mrid, new Filter() {
+ public boolean accept(Object o) {
+ return ((ModuleSettings) o).getResolverName() != null;
+ }
+ });
+ return ms == null ? defaultResolverName : ms.getResolverName();
+ }
+
+ public synchronized String getDefaultBranch(ModuleId moduleId) {
+ ModuleSettings ms = (ModuleSettings) moduleSettings.getRule(moduleId, new Filter() {
+ public boolean accept(Object o) {
+ return ((ModuleSettings) o).getBranch() != null;
+ }
+ });
+ return ms == null ? getDefaultBranch() : ms.getBranch();
+ }
+
+ public synchronized String getDefaultBranch() {
+ return defaultBranch;
+ }
+
+ public synchronized void setDefaultBranch(String defaultBranch) {
+ this.defaultBranch = defaultBranch;
+ }
+
+ public synchronized ConflictManager getConflictManager(ModuleId moduleId) {
+ ModuleSettings ms = (ModuleSettings) moduleSettings.getRule(moduleId, new Filter() {
+ public boolean accept(Object o) {
+ return ((ModuleSettings) o).getConflictManager() != null;
+ }
+ });
+ if (ms == null) {
+ return getDefaultConflictManager();
+ } else {
+ ConflictManager cm = getConflictManager(ms.getConflictManager());
+ if (cm == null) {
+ throw new IllegalStateException("ivy badly configured: unknown conflict manager "
+ + ms.getConflictManager());
+ }
+ return cm;
+ }
+ }
+
+ public synchronized String getResolveMode(ModuleId moduleId) {
+ ModuleSettings ms = (ModuleSettings) moduleSettings.getRule(moduleId, new Filter() {
+ public boolean accept(Object o) {
+ return ((ModuleSettings) o).getResolveMode() != null;
+ }
+ });
+ return ms == null ? getDefaultResolveMode() : ms.getResolveMode();
+ }
+
+ public synchronized String getDefaultResolveMode() {
+ return defaultResolveMode;
+ }
+
+ public synchronized void setDefaultResolveMode(String defaultResolveMode) {
+ this.defaultResolveMode = defaultResolveMode;
+ }
+
+ public synchronized void addConfigured(ConflictManager cm) {
+ addConflictManager(cm.getName(), cm);
+ }
+
+ public synchronized ConflictManager getConflictManager(String name) {
+ if ("default".equals(name)) {
+ return getDefaultConflictManager();
+ }
+ return (ConflictManager) conflictsManager.get(name);
+ }
+
+ public synchronized void addConflictManager(String name, ConflictManager cm) {
+ init(cm);
+ conflictsManager.put(name, cm);
+ }
+
+ public synchronized void addConfigured(LatestStrategy latest) {
+ addLatestStrategy(latest.getName(), latest);
+ }
+
+ public synchronized LatestStrategy getLatestStrategy(String name) {
+ if ("default".equals(name)) {
+ return getDefaultLatestStrategy();
+ }
+ return (LatestStrategy) latestStrategies.get(name);
+ }
+
+ public synchronized void addLatestStrategy(String name, LatestStrategy latest) {
+ init(latest);
+ latestStrategies.put(name, latest);
+ }
+
+ public synchronized void addConfigured(LockStrategy lockStrategy) {
+ addLockStrategy(lockStrategy.getName(), lockStrategy);
+ }
+
+ public synchronized LockStrategy getLockStrategy(String name) {
+ if ("default".equals(name)) {
+ return getDefaultLockStrategy();
+ }
+ return (LockStrategy) lockStrategies.get(name);
+ }
+
+ public synchronized void addLockStrategy(String name, LockStrategy lockStrategy) {
+ init(lockStrategy);
+ lockStrategies.put(name, lockStrategy);
+ }
+
+ public synchronized void addConfigured(Namespace ns) {
+ addNamespace(ns);
+ }
+
+ public synchronized Namespace getNamespace(String name) {
+ if ("system".equals(name)) {
+ return getSystemNamespace();
+ }
+ return (Namespace) namespaces.get(name);
+ }
+
+ public final Namespace getSystemNamespace() {
+ return Namespace.SYSTEM_NAMESPACE;
+ }
+
+ public synchronized void addNamespace(Namespace ns) {
+ init(ns);
+ namespaces.put(ns.getName(), ns);
+ }
+
+ public synchronized void addConfigured(PatternMatcher m) {
+ addMatcher(m);
+ }
+
+ public synchronized PatternMatcher getMatcher(String name) {
+ return (PatternMatcher) matchers.get(name);
+ }
+
+ public synchronized void addMatcher(PatternMatcher m) {
+ init(m);
+ matchers.put(m.getName(), m);
+ }
+
+ public synchronized void addConfigured(RepositoryCacheManager c) {
+ addRepositoryCacheManager(c);
+ }
+
+ public synchronized RepositoryCacheManager getRepositoryCacheManager(String name) {
+ return (RepositoryCacheManager) repositoryCacheManagers.get(name);
+ }
+
+ public synchronized void addRepositoryCacheManager(RepositoryCacheManager c) {
+ init(c);
+ repositoryCacheManagers.put(c.getName(), c);
+ }
+
+ public synchronized RepositoryCacheManager[] getRepositoryCacheManagers() {
+ return (RepositoryCacheManager[]) repositoryCacheManagers.values().toArray(
+ new RepositoryCacheManager[repositoryCacheManagers.size()]);
+ }
+
+ public synchronized void addConfigured(ReportOutputter outputter) {
+ addReportOutputter(outputter);
+ }
+
+ public synchronized ReportOutputter getReportOutputter(String name) {
+ return (ReportOutputter) reportOutputters.get(name);
+ }
+
+ public synchronized void addReportOutputter(ReportOutputter outputter) {
+ init(outputter);
+ reportOutputters.put(outputter.getName(), outputter);
+ }
+
+ public synchronized ReportOutputter[] getReportOutputters() {
+ return (ReportOutputter[]) reportOutputters.values().toArray(
+ new ReportOutputter[reportOutputters.size()]);
+ }
+
+ public synchronized void addConfigured(VersionMatcher vmatcher) {
+ addVersionMatcher(vmatcher);
+ }
+
+ public synchronized VersionMatcher getVersionMatcher(String name) {
+ return (VersionMatcher) versionMatchers.get(name);
+ }
+
+ public synchronized void addVersionMatcher(VersionMatcher vmatcher) {
+ init(vmatcher);
+ versionMatchers.put(vmatcher.getName(), vmatcher);
+
+ if (versionMatcher == null) {
+ versionMatcher = new ChainVersionMatcher();
+ addVersionMatcher(new ExactVersionMatcher());
+ }
+ if (versionMatcher instanceof ChainVersionMatcher) {
+ ChainVersionMatcher chain = (ChainVersionMatcher) versionMatcher;
+ chain.add(vmatcher);
+ }
+ }
+
+ public synchronized VersionMatcher[] getVersionMatchers() {
+ return (VersionMatcher[]) versionMatchers.values().toArray(
+ new VersionMatcher[versionMatchers.size()]);
+ }
+
+ public synchronized VersionMatcher getVersionMatcher() {
+ if (versionMatcher == null) {
+ configureDefaultVersionMatcher();
+ }
+ return versionMatcher;
+ }
+
+ public synchronized void configureDefaultVersionMatcher() {
+ addVersionMatcher(new LatestVersionMatcher());
+ addVersionMatcher(new SubVersionMatcher());
+ addVersionMatcher(new VersionRangeMatcher());
+ }
+
+ public synchronized CircularDependencyStrategy getCircularDependencyStrategy() {
+ if (circularDependencyStrategy == null) {
+ circularDependencyStrategy = getCircularDependencyStrategy("default");
+ }
+ return circularDependencyStrategy;
+ }
+
+ public synchronized CircularDependencyStrategy getCircularDependencyStrategy(String name) {
+ if ("default".equals(name)) {
+ name = "warn";
+ }
+ return (CircularDependencyStrategy) circularDependencyStrategies.get(name);
+ }
+
+ public synchronized void setCircularDependencyStrategy(CircularDependencyStrategy strategy) {
+ circularDependencyStrategy = strategy;
+ }
+
+ public synchronized void addConfigured(CircularDependencyStrategy strategy) {
+ addCircularDependencyStrategy(strategy);
+ }
+
+ private void addCircularDependencyStrategy(CircularDependencyStrategy strategy) {
+ circularDependencyStrategies.put(strategy.getName(), strategy);
+ }
+
+ private void configureDefaultCircularDependencyStrategies() {
+ addCircularDependencyStrategy(WarnCircularDependencyStrategy.getInstance());
+ addCircularDependencyStrategy(ErrorCircularDependencyStrategy.getInstance());
+ addCircularDependencyStrategy(IgnoreCircularDependencyStrategy.getInstance());
+ }
+
+ public synchronized StatusManager getStatusManager() {
+ if (statusManager == null) {
+ statusManager = StatusManager.newDefaultInstance();
+ }
+ return statusManager;
+ }
+
+ public void setStatusManager(StatusManager statusManager) {
+ this.statusManager = statusManager;
+ }
+
+ /**
+ * Returns the file names of the files that should be ignored when creating a file listing.
+ */
+ public synchronized String[] getIgnorableFilenames() {
+ return (String[]) listingIgnore.toArray(new String[listingIgnore.size()]);
+ }
+
+ /**
+ * Filters the names list by removing all names that should be ignored as defined by the listing
+ * ignore list
+ *
+ * @param names
+ */
+ public synchronized void filterIgnore(Collection names) {
+ names.removeAll(listingIgnore);
+ }
+
+ public synchronized boolean isCheckUpToDate() {
+ return checkUpToDate;
+ }
+
+ public synchronized void setCheckUpToDate(boolean checkUpToDate) {
+ this.checkUpToDate = checkUpToDate;
+ }
+
+ public synchronized boolean doValidate() {
+ return validate;
+ }
+
+ public synchronized void setValidate(boolean validate) {
+ this.validate = validate;
+ }
+
+ public synchronized String getVariable(String name) {
+ return variableContainer.getVariable(name);
+ }
+
+ public synchronized ConflictManager getDefaultConflictManager() {
+ if (defaultConflictManager == null) {
+ defaultConflictManager = new LatestConflictManager(getDefaultLatestStrategy());
+ ((LatestConflictManager) defaultConflictManager).setSettings(this);
+ }
+ return defaultConflictManager;
+ }
+
+ public synchronized void setDefaultConflictManager(ConflictManager defaultConflictManager) {
+ this.defaultConflictManager = defaultConflictManager;
+ }
+
+ public synchronized LatestStrategy getDefaultLatestStrategy() {
+ if (defaultLatestStrategy == null) {
+ defaultLatestStrategy = new LatestRevisionStrategy();
+ }
+ return defaultLatestStrategy;
+ }
+
+ public synchronized void setDefaultLatestStrategy(LatestStrategy defaultLatestStrategy) {
+ this.defaultLatestStrategy = defaultLatestStrategy;
+ }
+
+ public synchronized LockStrategy getDefaultLockStrategy() {
+ if (defaultLockStrategy == null) {
+ defaultLockStrategy = new NoLockStrategy();
+ }
+ return defaultLockStrategy;
+ }
+
+ public synchronized void setDefaultLockStrategy(LockStrategy defaultLockStrategy) {
+ this.defaultLockStrategy = defaultLockStrategy;
+ }
+
+ public synchronized RepositoryCacheManager getDefaultRepositoryCacheManager() {
+ if (defaultRepositoryCacheManager == null) {
+ defaultRepositoryCacheManager = new DefaultRepositoryCacheManager("default-cache",
+ this, getDefaultRepositoryCacheBasedir());
+ addRepositoryCacheManager(defaultRepositoryCacheManager);
+ }
+ return defaultRepositoryCacheManager;
+ }
+
+ public synchronized void setDefaultRepositoryCacheManager(RepositoryCacheManager cache) {
+ this.defaultRepositoryCacheManager = cache;
+ }
+
+ public synchronized ResolutionCacheManager getResolutionCacheManager() {
+ if (resolutionCacheManager == null) {
+ resolutionCacheManager = new DefaultResolutionCacheManager(
+ getDefaultResolutionCacheBasedir());
+ init(resolutionCacheManager);
+ }
+ return resolutionCacheManager;
+ }
+
+ public synchronized void setResolutionCacheManager(ResolutionCacheManager resolutionCacheManager) {
+ this.resolutionCacheManager = resolutionCacheManager;
+ }
+
+ public synchronized void addTrigger(Trigger trigger) {
+ init(trigger);
+ triggers.add(trigger);
+ }
+
+ public synchronized List getTriggers() {
+ return triggers;
+ }
+
+ public synchronized void addConfigured(Trigger trigger) {
+ addTrigger(trigger);
+ }
+
+ public synchronized boolean isUseRemoteConfig() {
+ return useRemoteConfig;
+ }
+
+ public synchronized void setUseRemoteConfig(boolean useRemoteConfig) {
+ this.useRemoteConfig = useRemoteConfig;
+ }
+
+ public synchronized boolean logModulesInUse() {
+ String var = getVariable("ivy.log.modules.in.use");
+ return var == null || Boolean.valueOf(var).booleanValue();
+ }
+
+ public synchronized boolean logModuleWhenFound() {
+ String var = getVariable("ivy.log.module.when.found");
+ return var == null || Boolean.valueOf(var).booleanValue();
+ }
+
+ public synchronized boolean logResolvedRevision() {
+ String var = getVariable("ivy.log.resolved.revision");
+ return var == null || Boolean.valueOf(var).booleanValue();
+ }
+
+ public synchronized boolean debugConflictResolution() {
+ if (debugConflictResolution == null) {
+ String var = getVariable("ivy.log.conflict.resolution");
+ debugConflictResolution = Boolean.valueOf(var != null
+ && Boolean.valueOf(var).booleanValue());
+ }
+ return debugConflictResolution.booleanValue();
+ }
+
+ public synchronized boolean debugLocking() {
+ if (debugLocking == null) {
+ String var = getVariable("ivy.log.locking");
+ debugLocking = Boolean.valueOf(var != null && Boolean.valueOf(var).booleanValue());
+ }
+ return debugLocking.booleanValue();
+ }
+
+ public synchronized boolean dumpMemoryUsage() {
+ if (dumpMemoryUsage == null) {
+ String var = getVariable("ivy.log.memory");
+ dumpMemoryUsage = Boolean.valueOf(var != null && Boolean.valueOf(var).booleanValue());
+ }
+ return dumpMemoryUsage.booleanValue();
+ }
+
+ public synchronized boolean logNotConvertedExclusionRule() {
+ return logNotConvertedExclusionRule;
+ }
+
+ public synchronized void setLogNotConvertedExclusionRule(boolean logNotConvertedExclusionRule) {
+ this.logNotConvertedExclusionRule = logNotConvertedExclusionRule;
+ }
+
+ private void init(Object obj) {
+ if (obj instanceof IvySettingsAware) {
+ ((IvySettingsAware) obj).setSettings(this);
+ } else if (obj instanceof DependencyResolver) {
+ ((DependencyResolver) obj).setSettings(this);
+ }
+ }
+
+ private static class ModuleSettings {
+ private String resolverName;
+
+ private String branch;
+
+ private String conflictManager;
+
+ private String resolveMode;
+
+ public ModuleSettings(String resolver, String branchName, String conflictMgr,
+ String resolveMode) {
+ this.resolverName = resolver;
+ this.branch = branchName;
+ this.conflictManager = conflictMgr;
+ this.resolveMode = resolveMode;
+ }
+
+ public String toString() {
+ return (resolverName != null ? "resolver: " + resolverName : "")
+ + (branch != null ? "branch: " + branch : "")
+ + (conflictManager != null ? "conflictManager: " + conflictManager : "")
+ + (resolveMode != null ? "resolveMode: " + resolveMode : "");
+ }
+
+ public String getBranch() {
+ return branch;
+ }
+
+ public String getResolverName() {
+ return resolverName;
+ }
+
+ public String getConflictManager() {
+ return conflictManager;
+ }
+
+ public String getResolveMode() {
+ return resolveMode;
+ }
+ }
+
+ public final long getInterruptTimeout() {
+ return INTERUPT_TIMEOUT;
+ }
+
+ public synchronized Collection getResolvers() {
+ return resolversMap.values();
+ }
+
+ public synchronized Collection getResolverNames() {
+ return resolversMap.keySet();
+ }
+
+ public synchronized Collection getMatcherNames() {
+ return matchers.keySet();
+ }
+
+ public synchronized IvyVariableContainer getVariableContainer() {
+ return variableContainer;
+ }
+
+ /**
+ * Use a different variable container.
+ *
+ * @param variables
+ */
+ public synchronized void setVariableContainer(IvyVariableContainer variables) {
+ variableContainer = variables;
+ }
+
+ public synchronized RelativeUrlResolver getRelativeUrlResolver() {
+ return new NormalRelativeUrlResolver();
+ }
+
+ public synchronized void setDefaultCacheIvyPattern(String defaultCacheIvyPattern) {
+ CacheUtil.checkCachePattern(defaultCacheIvyPattern);
+ this.defaultCacheIvyPattern = defaultCacheIvyPattern;
+ }
+
+ public synchronized String getDefaultCacheIvyPattern() {
+ return defaultCacheIvyPattern;
+ }
+
+ public synchronized void setDefaultCacheArtifactPattern(String defaultCacheArtifactPattern) {
+ CacheUtil.checkCachePattern(defaultCacheArtifactPattern);
+ this.defaultCacheArtifactPattern = defaultCacheArtifactPattern;
+ }
+
+ public synchronized String getDefaultCacheArtifactPattern() {
+ return defaultCacheArtifactPattern;
+ }
+
+ public synchronized void setDefaultUseOrigin(boolean useOrigin) {
+ defaultUseOrigin = useOrigin;
+ }
+
+ public synchronized boolean isDefaultUseOrigin() {
+ return defaultUseOrigin;
+ }
+
+ public synchronized void useDeprecatedUseOrigin() {
+ Message.deprecated("useOrigin option is deprecated when calling resolve, use useOrigin"
+ + " setting on the cache implementation instead");
+ setDefaultUseOrigin(true);
+
+ }
+
+ /**
+ * Validates the settings, throwing an {@link IllegalStateException} if the current state is not
+ * valid.
+ *
+ * @throws IllegalStateException
+ * if the settings is not valid.
+ */
+ public synchronized void validate() {
+ validateAll(resolversMap.values());
+ validateAll(conflictsManager.values());
+ validateAll(latestStrategies.values());
+ validateAll(lockStrategies.values());
+ validateAll(repositoryCacheManagers.values());
+ validateAll(reportOutputters.values());
+ validateAll(circularDependencyStrategies.values());
+ validateAll(versionMatchers.values());
+ validateAll(namespaces.values());
+ }
+
+ /**
+ * Validates all {@link Validatable} objects in the collection.
+ *
+ * @param objects
+ * the collection of objects to validate.
+ * @throws IllegalStateException
+ * if any of the objects is not valid.
+ */
+ private void validateAll(Collection values) {
+ for (Iterator iterator = values.iterator(); iterator.hasNext();) {
+ Object object = iterator.next();
+ if (object instanceof Validatable) {
+ ((Validatable) object).validate();
+ }
+ }
+ }
+
+ public Namespace getContextNamespace() {
+ return Namespace.SYSTEM_NAMESPACE;
+ }
+
+ public synchronized void addConfigured(ArchivePacking packing) {
+ init(packing);
+ packingRegistry.register(packing);
+ }
+
+ public PackingRegistry getPackingRegistry() {
+ return packingRegistry;
+ }
+}
diff --git a/src/java/org/apache/ivy/core/settings/IvyVariableContainer.java b/src/java/org/apache/ivy/core/settings/IvyVariableContainer.java
new file mode 100644
index 0000000..e3d15a4
--- /dev/null
+++ b/src/java/org/apache/ivy/core/settings/IvyVariableContainer.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.settings;
+
+/**
+ * Store and provide access to the ivy variables.
+ */
+public interface IvyVariableContainer extends Cloneable {
+
+ public void setVariable(String varName, String value, boolean overwrite);
+
+ public String getVariable(String name);
+
+ /**
+ * Specifies the prefix used to indicate a variable is an environment variable. If the prefix
+ * doesn't end with a '.', it will be added automatically.
+ *
+ * @param prefix
+ * the prefix to use for the environment variables
+ */
+ public void setEnvironmentPrefix(String prefix);
+
+ public Object clone();
+}
diff --git a/src/java/org/apache/ivy/core/settings/IvyVariableContainerImpl.java b/src/java/org/apache/ivy/core/settings/IvyVariableContainerImpl.java
new file mode 100644
index 0000000..8d89091
--- /dev/null
+++ b/src/java/org/apache/ivy/core/settings/IvyVariableContainerImpl.java
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.settings;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.ivy.core.IvyPatternHelper;
+import org.apache.ivy.util.Message;
+
+public class IvyVariableContainerImpl implements IvyVariableContainer {
+
+ private Map variables;
+
+ private String envPrefix;
+
+ public IvyVariableContainerImpl() {
+ this.variables = new HashMap();
+ }
+
+ public IvyVariableContainerImpl(Map variables) {
+ this.variables = variables;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.ivy.core.settings.IvyVariableContainer#setVariable(java.lang.String,
+ * java.lang.String, boolean)
+ */
+ public void setVariable(String varName, String value, boolean overwrite) {
+ if (overwrite || !variables.containsKey(varName)) {
+ Message.debug("setting '" + varName + "' to '" + value + "'");
+ variables.put(varName, substitute(value));
+ } else {
+ Message.debug("'" + varName + "' already set: discarding '" + value + "'");
+ }
+ }
+
+ public void setEnvironmentPrefix(String prefix) {
+ if ((prefix != null) && !prefix.endsWith(".")) {
+ this.envPrefix = prefix + ".";
+ } else {
+ this.envPrefix = prefix;
+ }
+ }
+
+ protected String substitute(String value) {
+ return IvyPatternHelper.substituteVariables(value, this);
+ }
+
+ protected Map getVariables() {
+ return variables;
+ }
+
+ protected String getEnvironmentPrefix() {
+ return envPrefix;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.ivy.core.settings.IvyVariableContainer#getVariable(java.lang.String)
+ */
+ public String getVariable(String name) {
+ String val = null;
+ if ((envPrefix != null) && name.startsWith(envPrefix)) {
+ val = System.getenv(name.substring(envPrefix.length()));
+ } else {
+ val = (String) variables.get(name);
+ }
+
+ return val;
+ }
+
+ public Object clone() {
+ IvyVariableContainerImpl clone;
+ try {
+ clone = (IvyVariableContainerImpl) super.clone();
+ } catch (CloneNotSupportedException e) {
+ throw new RuntimeException("unable to clone a " + this.getClass());
+ }
+ clone.variables = new HashMap(this.variables);
+ return clone;
+ }
+}
diff --git a/src/java/org/apache/ivy/core/settings/Validatable.java b/src/java/org/apache/ivy/core/settings/Validatable.java
new file mode 100644
index 0000000..f79370d
--- /dev/null
+++ b/src/java/org/apache/ivy/core/settings/Validatable.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.settings;
+
+/**
+ * Implemented by settings element which need to perform validation when settings are loaded.
+ */
+public interface Validatable {
+ /**
+ * Validates the Validatable, throwing an {@link IllegalStateException} if the current state is
+ * not valid.
+ *
+ * @throws IllegalStateException
+ * if the state of the {@link Validatable} is not valid.
+ */
+ public void validate();
+}
diff --git a/src/java/org/apache/ivy/core/settings/XmlSettingsParser.java b/src/java/org/apache/ivy/core/settings/XmlSettingsParser.java
new file mode 100644
index 0000000..a40cc1c
--- /dev/null
+++ b/src/java/org/apache/ivy/core/settings/XmlSettingsParser.java
@@ -0,0 +1,648 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.settings;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.text.ParseException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.parsers.SAXParserFactory;
+
+import org.apache.ivy.core.IvyPatternHelper;
+import org.apache.ivy.core.cache.RepositoryCacheManager;
+import org.apache.ivy.core.module.status.StatusManager;
+import org.apache.ivy.plugins.circular.CircularDependencyStrategy;
+import org.apache.ivy.plugins.conflict.ConflictManager;
+import org.apache.ivy.plugins.latest.LatestStrategy;
+import org.apache.ivy.plugins.lock.LockStrategy;
+import org.apache.ivy.plugins.matcher.PatternMatcher;
+import org.apache.ivy.util.Checks;
+import org.apache.ivy.util.Configurator;
+import org.apache.ivy.util.FileResolver;
+import org.apache.ivy.util.Message;
+import org.apache.ivy.util.url.CredentialsStore;
+import org.apache.ivy.util.url.URLHandler;
+import org.apache.ivy.util.url.URLHandlerRegistry;
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+/**
+ */
+public class XmlSettingsParser extends DefaultHandler {
+ /**
+ * Wraps an {@link IvyVariableContainer} delegating most method calls to the wrapped instance,
+ * except for a set of variables which are only stored locally in the wrapper, and not
+ * propagated to the wrapped instance.
+ */
+ private static final class IvyVariableContainerWrapper implements IvyVariableContainer {
+ private static final Collection SETTINGS_VARIABLES = Arrays.asList(new String[] {
+ "ivy.settings.dir", "ivy.settings.url", "ivy.settings.file", "ivy.conf.dir",
+ "ivy.conf.url", "ivy.conf.file"});
+
+ private final IvyVariableContainer variables;
+
+ private Map localVariables = new HashMap();
+
+ private IvyVariableContainerWrapper(IvyVariableContainer variables) {
+ this.variables = variables;
+ }
+
+ public void setVariable(String varName, String value, boolean overwrite) {
+ if (SETTINGS_VARIABLES.contains(varName)) {
+ if (!localVariables.containsKey(varName) || overwrite) {
+ localVariables.put(varName, value);
+ }
+ } else {
+ variables.setVariable(varName, value, overwrite);
+ }
+ }
+
+ public void setEnvironmentPrefix(String prefix) {
+ variables.setEnvironmentPrefix(prefix);
+ }
+
+ public String getVariable(String name) {
+ if (localVariables.containsKey(name)) {
+ return (String) localVariables.get(name);
+ }
+ return variables.getVariable(name);
+ }
+
+ public Object clone() {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ private Configurator configurator;
+
+ private List configuratorTags = Arrays.asList(new String[] {"resolvers", "namespaces",
+ "parsers", "latest-strategies", "conflict-managers", "outputters", "version-matchers",
+ "statuses", "circular-dependency-strategies", "triggers", "lock-strategies", "caches",
+ "signers"});
+
+ private IvySettings ivy;
+
+ private String defaultResolver;
+
+ private String defaultCM;
+
+ private String defaultLatest;
+
+ private String defaultCacheManager;
+
+ private String defaultCircular;
+
+ private String defaultLock;
+
+ private String currentConfiguratorTag;
+
+ private URL settings;
+
+ private boolean deprecatedMessagePrinted = false;
+
+ public XmlSettingsParser(IvySettings ivy) {
+ this.ivy = ivy;
+ }
+
+ public void parse(URL settings) throws ParseException, IOException {
+ configurator = new Configurator();
+ configurator.setFileResolver(new FileResolver() {
+ public File resolveFile(String path, String filename) {
+ return Checks.checkAbsolute(path, filename);
+ }
+ });
+ // put every type definition from ivy to configurator
+ Map typeDefs = ivy.getTypeDefs();
+ for (Iterator iter = typeDefs.keySet().iterator(); iter.hasNext();) {
+ String name = (String) iter.next();
+ configurator.typeDef(name, (Class) typeDefs.get(name));
+ }
+
+ doParse(settings);
+ }
+
+ private void doParse(URL settingsUrl) throws IOException, ParseException {
+ this.settings = settingsUrl;
+ InputStream stream = null;
+ try {
+ stream = URLHandlerRegistry.getDefault().openStream(settingsUrl);
+ InputSource inSrc = new InputSource(stream);
+ inSrc.setSystemId(settingsUrl.toExternalForm());
+ SAXParserFactory.newInstance().newSAXParser().parse(settingsUrl.toExternalForm(), this);
+ ivy.validate();
+ } catch (IOException e) {
+ throw e;
+ } catch (Exception e) {
+ ParseException pe = new ParseException("failed to load settings from " + settingsUrl
+ + ": " + e.getMessage(), 0);
+ pe.initCause(e);
+ throw pe;
+ } finally {
+ if (stream != null) {
+ try {
+ stream.close();
+ } catch (IOException e) {
+ // ignored
+ }
+ }
+ }
+ }
+
+ private void parse(Configurator configurator, URL configuration) throws IOException,
+ ParseException {
+ this.configurator = configurator;
+ doParse(configuration);
+ }
+
+ public void startElement(String uri, String localName, String qName, Attributes att)
+ throws SAXException {
+ // we first copy attributes in a Map to be able to modify them
+ Map attributes = new HashMap();
+ for (int i = 0; i < att.getLength(); i++) {
+ attributes.put(att.getQName(i), ivy.substitute(att.getValue(i)));
+ }
+
+ try {
+ if ("ivyconf".equals(qName)) {
+ deprecatedMessagePrinted = true;
+ Message.deprecated("'ivyconf' element is deprecated, use 'ivysettings' instead ("
+ + settings + ")");
+ }
+ if (configurator.getCurrent() != null) {
+ inConfiguratorStarted(qName, attributes);
+ } else if ("classpath".equals(qName)) {
+ classpathStarted(attributes);
+ } else if ("typedef".equals(qName)) {
+ typedefStarted(attributes);
+ } else if ("property".equals(qName)) {
+ propertyStarted(attributes);
+ } else if ("properties".equals(qName)) {
+ propertiesStarted(attributes);
+ } else if ("include".equals(qName)) {
+ includeStarted(attributes);
+ } else if ("settings".equals(qName) || "conf".equals(qName)) {
+ settingsStarted(qName, attributes);
+ } else if ("caches".equals(qName)) {
+ cachesStarted(qName, attributes);
+ } else if ("version-matchers".equals(qName)) {
+ versionMatchersStarted(qName, attributes);
+ } else if ("statuses".equals(qName)) {
+ statusesStarted(qName, attributes);
+ } else if (configuratorTags.contains(qName)) {
+ anyConfiguratorStarted(qName);
+ } else if ("macrodef".equals(qName)) {
+ macrodefStarted(qName, attributes);
+ } else if ("module".equals(qName)) {
+ moduleStarted(attributes);
+ } else if ("credentials".equals(qName)) {
+ credentialsStarted(attributes);
+ }
+ } catch (ParseException ex) {
+ SAXException sax = new SAXException("problem in config file: " + ex.getMessage(), ex);
+ sax.initCause(ex);
+ throw sax;
+ } catch (IOException ex) {
+ SAXException sax = new SAXException("io problem while parsing config file: "
+ + ex.getMessage(), ex);
+ sax.initCause(ex);
+ throw sax;
+ }
+ }
+
+ private void credentialsStarted(Map attributes) {
+ String realm = (String) attributes.remove("realm");
+ String host = (String) attributes.remove("host");
+ String userName = (String) attributes.remove("username");
+ String passwd = (String) attributes.remove("passwd");
+ CredentialsStore.INSTANCE.addCredentials(realm, host, userName, passwd);
+ }
+
+ private void moduleStarted(Map attributes) {
+ attributes.put(IvyPatternHelper.MODULE_KEY, attributes.remove("name"));
+ String resolver = (String) attributes.remove("resolver");
+ String branch = (String) attributes.remove("branch");
+ String cm = (String) attributes.remove("conflict-manager");
+ String resolveMode = (String) attributes.remove("resolveMode");
+ String matcher = (String) attributes.remove("matcher");
+ matcher = matcher == null ? PatternMatcher.EXACT_OR_REGEXP : matcher;
+ ivy.addModuleConfiguration(attributes, ivy.getMatcher(matcher), resolver, branch, cm,
+ resolveMode);
+ }
+
+ private void macrodefStarted(String qName, Map attributes) {
+ currentConfiguratorTag = qName;
+ Configurator.MacroDef macrodef = configurator
+ .startMacroDef((String) attributes.get("name"));
+ macrodef.addAttribute("name", null);
+ }
+
+ private void anyConfiguratorStarted(String qName) {
+ currentConfiguratorTag = qName;
+ configurator.setRoot(ivy);
+ }
+
+ private void statusesStarted(String qName, Map attributes) {
+ currentConfiguratorTag = qName;
+ StatusManager m = new StatusManager();
+ String defaultStatus = (String) attributes.get("default");
+ if (defaultStatus != null) {
+ m.setDefaultStatus(defaultStatus);
+ }
+ ivy.setStatusManager(m);
+ configurator.setRoot(m);
+ }
+
+ private void versionMatchersStarted(String qName, Map attributes) {
+ anyConfiguratorStarted(qName);
+ if ("true".equals(attributes.get("usedefaults"))) {
+ ivy.configureDefaultVersionMatcher();
+ }
+ }
+
+ private void cachesStarted(String qName, Map attributes) {
+ anyConfiguratorStarted(qName);
+ defaultLock = (String) attributes.get("lockStrategy");
+ defaultCacheManager = (String) attributes.get("default");
+
+ String cache = (String) attributes.get("defaultCacheDir");
+ if (cache != null) {
+ ivy.setDefaultCache(Checks.checkAbsolute(cache, "defaultCacheDir"));
+ }
+ String up2d = (String) attributes.get("checkUpToDate");
+ if (up2d != null) {
+ Message.deprecated("'checkUpToDate' is deprecated, "
+ + "use the 'overwriteMode' on the 'ivy:retrieve' task instead (" + settings
+ + ")");
+ ivy.setCheckUpToDate(Boolean.valueOf(up2d).booleanValue());
+ }
+ String resolutionDir = (String) attributes.get("resolutionCacheDir");
+ if (resolutionDir != null) {
+ ivy.setDefaultResolutionCacheBasedir(resolutionDir);
+ }
+ String useOrigin = (String) attributes.get("useOrigin");
+ if (useOrigin != null) {
+ ivy.setDefaultUseOrigin(Boolean.valueOf(useOrigin).booleanValue());
+ }
+ String cacheIvyPattern = (String) attributes.get("ivyPattern");
+ if (cacheIvyPattern != null) {
+ ivy.setDefaultCacheIvyPattern(cacheIvyPattern);
+ }
+ String cacheArtPattern = (String) attributes.get("artifactPattern");
+ if (cacheArtPattern != null) {
+ ivy.setDefaultCacheArtifactPattern(cacheArtPattern);
+ }
+ String repositoryDir = (String) attributes.get("repositoryCacheDir");
+ if (repositoryDir != null) {
+ ivy.setDefaultRepositoryCacheBasedir(repositoryDir);
+ }
+ }
+
+ private void settingsStarted(String qName, Map attributes) {
+ if ("conf".equals(qName) && !deprecatedMessagePrinted) {
+ Message.deprecated("'conf' is deprecated, use 'settings' instead (" + settings + ")");
+ }
+ String cache = (String) attributes.get("defaultCache");
+ if (cache != null) {
+ Message.deprecated("'defaultCache' is deprecated, "
+ + "use 'caches[@defaultCacheDir]' instead (" + settings + ")");
+ ivy.setDefaultCache(Checks.checkAbsolute(cache, "defaultCache"));
+ }
+ String defaultBranch = (String) attributes.get("defaultBranch");
+ if (defaultBranch != null) {
+ ivy.setDefaultBranch(defaultBranch);
+ }
+ String defaultResolveMode = (String) attributes.get("defaultResolveMode");
+ if (defaultResolveMode != null) {
+ ivy.setDefaultResolveMode(defaultResolveMode);
+ }
+ String validate = (String) attributes.get("validate");
+ if (validate != null) {
+ ivy.setValidate(Boolean.valueOf(validate).booleanValue());
+ }
+ String up2d = (String) attributes.get("checkUpToDate");
+ if (up2d != null) {
+ Message.deprecated("'checkUpToDate' is deprecated, "
+ + "use the 'overwriteMode' on the 'ivy:retrieve' task instead (" + settings
+ + ")");
+ ivy.setCheckUpToDate(Boolean.valueOf(up2d).booleanValue());
+ }
+ String useRemoteConfig = (String) attributes.get("useRemoteConfig");
+ if (useRemoteConfig != null) {
+ ivy.setUseRemoteConfig(Boolean.valueOf(useRemoteConfig).booleanValue());
+ }
+ String cacheIvyPattern = (String) attributes.get("cacheIvyPattern");
+ if (cacheIvyPattern != null) {
+ Message.deprecated("'cacheIvyPattern' is deprecated, use 'caches[@ivyPattern]' instead"
+ + " (" + settings + ")");
+ ivy.setDefaultCacheIvyPattern(cacheIvyPattern);
+ }
+ String cacheArtPattern = (String) attributes.get("cacheArtifactPattern");
+ if (cacheArtPattern != null) {
+ Message.deprecated("'cacheArtifactPattern' is deprecated, "
+ + "use 'caches[@artifactPattern]' instead (" + settings + ")");
+ ivy.setDefaultCacheArtifactPattern(cacheArtPattern);
+ }
+
+ // we do not set following defaults here since no instances has been registered yet
+ defaultResolver = (String) attributes.get("defaultResolver");
+ defaultCM = (String) attributes.get("defaultConflictManager");
+ defaultLatest = (String) attributes.get("defaultLatestStrategy");
+ defaultCircular = (String) attributes.get("circularDependencyStrategy");
+
+ String requestMethod = (String) attributes.get("httpRequestMethod");
+ if ("head".equalsIgnoreCase(requestMethod)) {
+ URLHandlerRegistry.getHttp().setRequestMethod(URLHandler.REQUEST_METHOD_HEAD);
+ } else if ("get".equalsIgnoreCase(requestMethod)) {
+ URLHandlerRegistry.getHttp().setRequestMethod(URLHandler.REQUEST_METHOD_GET);
+ } else if ((requestMethod != null) && (requestMethod.trim().length() > 0)) {
+ throw new IllegalArgumentException("Invalid httpRequestMethod specified, must be "
+ + "one of {'HEAD', 'GET'}");
+ }
+ }
+
+ private void includeStarted(Map attributes) throws IOException, ParseException {
+ final IvyVariableContainer variables = ivy.getVariableContainer();
+ ivy.setVariableContainer(new IvyVariableContainerWrapper(variables));
+ try {
+ String propFilePath = (String) attributes.get("file");
+ URL settingsURL = null;
+ if (propFilePath == null) {
+ propFilePath = (String) attributes.get("url");
+ if (propFilePath == null) {
+ throw new IllegalArgumentException(
+ "bad include tag: specify file or url to include");
+ } else {
+ try {
+ // First asume that it is an absolute URL
+ settingsURL = new URL(propFilePath);
+ } catch (MalformedURLException e) {
+ // If that fail, it may be because it is a relative one.
+ settingsURL = new URL(this.settings, propFilePath);
+ }
+ Message.verbose("including url: " + settingsURL.toString());
+ ivy.setSettingsVariables(settingsURL);
+ }
+ } else {
+ settingsURL = urlFromFileAttribute(propFilePath);
+ Message.verbose("including file: " + settingsURL);
+ if ("file".equals(settingsURL.getProtocol())) {
+ try {
+ File settingsFile = new File(new URI(settingsURL.toExternalForm()));
+ String optional = (String) attributes.get("optional");
+ if ("true".equals(optional) && !settingsFile.exists()) {
+ return;
+ }
+
+ ivy.setSettingsVariables(Checks.checkAbsolute(settingsFile,
+ "settings include path"));
+ } catch (URISyntaxException e) {
+ // try to make the best of it...
+ ivy.setSettingsVariables(Checks.checkAbsolute(settingsURL.getPath(),
+ "settings include path"));
+ }
+ } else {
+ ivy.setSettingsVariables(settingsURL);
+ }
+ }
+ new XmlSettingsParser(ivy).parse(configurator, settingsURL);
+ } finally {
+ ivy.setVariableContainer(variables);
+ }
+ }
+
+ /**
+ * Provide an URL referencing the given filepath. If filePath is an absolute path, then the
+ * resulting URL point to a local file, otherwise, the filepath is evaluated relatively to the
+ * URL of the current settings file (can be local file or remote URL).
+ */
+ private URL urlFromFileAttribute(String filePath) throws IOException {
+ try {
+ return new URL(filePath);
+ } catch (MalformedURLException e) {
+ // ignore, we'll try to create a correct URL below
+ }
+
+ File incFile = new File(filePath);
+ if (incFile.isAbsolute()) {
+ if (!incFile.exists()) {
+ throw new FileNotFoundException(incFile.getAbsolutePath());
+ }
+ return incFile.toURI().toURL();
+ } else if ("file".equals(this.settings.getProtocol())) {
+ try {
+ File settingsFile = new File(new URI(this.settings.toExternalForm()));
+ if (!settingsFile.exists()) {
+ throw new FileNotFoundException(settingsFile.getAbsolutePath());
+ }
+ return new File(settingsFile.getParentFile(), filePath).toURI().toURL();
+ } catch (URISyntaxException e) {
+ return new URL(this.settings, filePath);
+ }
+ } else {
+ return new URL(this.settings, filePath);
+ }
+ }
+
+ private void propertiesStarted(Map attributes) throws IOException {
+ String propFilePath = (String) attributes.get("file");
+ String environmentPrefix = (String) attributes.get("environment");
+ if (propFilePath != null) {
+ String overrideStr = (String) attributes.get("override");
+ boolean override = overrideStr == null ? true : Boolean.valueOf(overrideStr)
+ .booleanValue();
+ Message.verbose("loading properties: " + propFilePath);
+ try {
+ URL fileUrl = urlFromFileAttribute(propFilePath);
+ ivy.loadProperties(fileUrl, override);
+ } catch (FileNotFoundException e) {
+ Message.verbose("Unable to find property file: " + propFilePath);
+ }
+ } else if (environmentPrefix != null) {
+ ivy.getVariableContainer().setEnvironmentPrefix(environmentPrefix);
+ } else {
+ throw new IllegalArgumentException("Didn't find a 'file' or 'environment' attribute "
+ + "on the 'properties' element");
+ }
+ }
+
+ private void propertyStarted(Map attributes) {
+ String name = (String) attributes.get("name");
+ String value = (String) attributes.get("value");
+ String override = (String) attributes.get("override");
+ String isSetVar = (String) attributes.get("ifset");
+ String unlessSetVar = (String) attributes.get("unlessset");
+ if (name == null) {
+ throw new IllegalArgumentException("missing attribute name on property tag");
+ }
+ if (value == null) {
+ throw new IllegalArgumentException("missing attribute value on property tag");
+ }
+ ivy.setVariable(name, value, override == null ? true : Boolean.valueOf(override)
+ .booleanValue(), isSetVar, unlessSetVar);
+ }
+
+ private void typedefStarted(Map attributes) {
+ String name = (String) attributes.get("name");
+ String className = (String) attributes.get("classname");
+ Class clazz = ivy.typeDef(name, className);
+ configurator.typeDef(name, clazz);
+ }
+
+ private void classpathStarted(Map attributes) throws IOException {
+ String urlStr = (String) attributes.get("url");
+ URL url = null;
+ if (urlStr == null) {
+ String file = (String) attributes.get("file");
+ if (file == null) {
+ throw new IllegalArgumentException(
+ "either url or file should be given for classpath element");
+ } else {
+ url = urlFromFileAttribute(file);
+ }
+ } else {
+ url = new URL(urlStr);
+ }
+ ivy.addClasspathURL(url);
+ }
+
+ private void inConfiguratorStarted(String qName, Map attributes) {
+ if ("macrodef".equals(currentConfiguratorTag) && configurator.getTypeDef(qName) != null) {
+ String name = (String) attributes.get("name");
+ if (name == null) {
+ attributes.put("name", "@{name}");
+ } else if (name.indexOf("@{name}") != -1) {
+ attributes.put("name", name);
+ } else {
+ attributes.put("name", "@{name}-" + name);
+ }
+ }
+ if (attributes.get("ref") != null) {
+ if (attributes.size() != 1) {
+ throw new IllegalArgumentException("ref attribute should be the only one ! found "
+ + attributes.size() + " in " + qName);
+ }
+ String name = (String) attributes.get("ref");
+ Object child = null;
+ if ("resolvers".equals(currentConfiguratorTag) || "resolver".equals(qName)) {
+ child = ivy.getResolver(name);
+ if (child == null) {
+ throw new IllegalArgumentException("unknown resolver " + name
+ + ": resolver should be defined before being referenced");
+ }
+ } else if ("latest-strategies".equals(currentConfiguratorTag)) {
+ child = ivy.getLatestStrategy(name);
+ if (child == null) {
+ throw new IllegalArgumentException("unknown latest strategy " + name
+ + ": latest strategy should be defined before being referenced");
+ }
+ } else if ("conflict-managers".equals(currentConfiguratorTag)) {
+ child = ivy.getConflictManager(name);
+ if (child == null) {
+ throw new IllegalArgumentException("unknown conflict manager " + name
+ + ": conflict manager should be defined before being referenced");
+ }
+ }
+ if (child == null) {
+ throw new IllegalArgumentException("bad reference " + name);
+ }
+ configurator.addChild(qName, child);
+ } else {
+ configurator.startCreateChild(qName);
+ for (Iterator iter = attributes.keySet().iterator(); iter.hasNext();) {
+ String attName = (String) iter.next();
+ configurator.setAttribute(attName, (String) attributes.get(attName));
+ }
+ }
+ }
+
+ public void endElement(String uri, String localName, String qName) throws SAXException {
+ if (configurator.getCurrent() != null) {
+ if (configuratorTags.contains(qName) && configurator.getDepth() == 1) {
+ configurator.clear();
+ currentConfiguratorTag = null;
+ } else if ("macrodef".equals(qName) && configurator.getDepth() == 1) {
+ configurator.endMacroDef();
+ currentConfiguratorTag = null;
+ } else {
+ configurator.endCreateChild();
+ }
+ }
+ }
+
+ public void endDocument() throws SAXException {
+ if (defaultResolver != null) {
+ ivy.setDefaultResolver(ivy.substitute(defaultResolver));
+ }
+ if (defaultCM != null) {
+ ConflictManager conflictManager = ivy.getConflictManager(ivy.substitute(defaultCM));
+ if (conflictManager == null) {
+ throw new IllegalArgumentException("unknown conflict manager "
+ + ivy.substitute(defaultCM));
+ }
+ ivy.setDefaultConflictManager(conflictManager);
+ }
+ if (defaultLatest != null) {
+ LatestStrategy latestStrategy = ivy.getLatestStrategy(ivy.substitute(defaultLatest));
+ if (latestStrategy == null) {
+ throw new IllegalArgumentException("unknown latest strategy "
+ + ivy.substitute(defaultLatest));
+ }
+ ivy.setDefaultLatestStrategy(latestStrategy);
+ }
+ if (defaultCacheManager != null) {
+ RepositoryCacheManager cache = ivy.getRepositoryCacheManager(ivy
+ .substitute(defaultCacheManager));
+ if (cache == null) {
+ throw new IllegalArgumentException("unknown cache manager "
+ + ivy.substitute(defaultCacheManager));
+ }
+ ivy.setDefaultRepositoryCacheManager(cache);
+ }
+ if (defaultCircular != null) {
+ CircularDependencyStrategy strategy = ivy.getCircularDependencyStrategy(ivy
+ .substitute(defaultCircular));
+ if (strategy == null) {
+ throw new IllegalArgumentException("unknown circular dependency strategy "
+ + ivy.substitute(defaultCircular));
+ }
+ ivy.setCircularDependencyStrategy(strategy);
+ }
+ if (defaultLock != null) {
+ LockStrategy strategy = ivy.getLockStrategy(ivy.substitute(defaultLock));
+ if (strategy == null) {
+ throw new IllegalArgumentException("unknown lock strategy "
+ + ivy.substitute(defaultLock));
+ }
+ ivy.setDefaultLockStrategy(strategy);
+ }
+ }
+}
diff --git a/src/java/org/apache/ivy/core/settings/ivy.properties b/src/java/org/apache/ivy/core/settings/ivy.properties
new file mode 100644
index 0000000..6dcb219
--- /dev/null
+++ b/src/java/org/apache/ivy/core/settings/ivy.properties
@@ -0,0 +1,43 @@
+# ***************************************************************
+# * Licensed to the Apache Software Foundation (ASF) under one
+# * or more contributor license agreements. See the NOTICE file
+# * distributed with this work for additional information
+# * regarding copyright ownership. The ASF licenses this file
+# * to you under the Apache License, Version 2.0 (the
+# * "License"); you may not use this file except in compliance
+# * with the License. You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing,
+# * software distributed under the License is distributed on an
+# * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# * KIND, either express or implied. See the License for the
+# * specific language governing permissions and limitations
+# * under the License.
+# ***************************************************************
+ivy.project.dir = ${basedir}
+ivy.lib.dir = ${ivy.project.dir}/lib
+ivy.build.artifacts.dir = ${ivy.project.dir}/build/artifacts
+ivy.distrib.dir = ${ivy.project.dir}/distrib
+
+ivy.resolver.default.check.modified = false
+ivy.default.always.check.exact.revision = false
+
+ivy.configurations = *
+ivy.resolve.default.type.filter = *
+ivy.status = integration
+ivy.dep.file = ivy.xml
+ivy.settings.file = ivysettings.xml
+ivy.retrieve.pattern = ${ivy.lib.dir}/[artifact]-[revision](-[classifier]).[ext]
+ivy.deliver.ivy.pattern = ${ivy.distrib.dir}/[type]s/[artifact]-[revision](-[classifier]).[ext]
+ivy.publish.src.artifacts.pattern = ${ivy.distrib.dir}/[type]s/[artifact]-[revision](-[classifier]).[ext]
+ivy.cache.ttl.default = 10s
+
+ivy.report.output.pattern = [organisation]-[module]-[conf].[ext]
+
+ivy.buildlist.ivyfilepath = ivy.xml
+
+ivy.checksums=sha1,md5
+
+ivy.log.modules.in.use=false
diff --git a/src/java/org/apache/ivy/core/settings/ivysettings-1.4.xml b/src/java/org/apache/ivy/core/settings/ivysettings-1.4.xml
new file mode 100644
index 0000000..1998556
--- /dev/null
+++ b/src/java/org/apache/ivy/core/settings/ivysettings-1.4.xml
@@ -0,0 +1,28 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivysettings>
+ <settings defaultResolver="default"/>
+ <resolvers>
+ <ivyrep name="public" ivyroot="http://ivyrep.jayasoft.org/" />
+ </resolvers>
+ <include url="${ivy.default.settings.dir}/ivysettings-shared.xml"/>
+ <include url="${ivy.default.settings.dir}/ivysettings-local.xml"/>
+ <include url="${ivy.default.settings.dir}/ivysettings-main-chain.xml"/>
+ <include url="${ivy.default.settings.dir}/ivysettings-default-chain.xml"/>
+</ivysettings>
diff --git a/src/java/org/apache/ivy/core/settings/ivysettings-default-chain.xml b/src/java/org/apache/ivy/core/settings/ivysettings-default-chain.xml
new file mode 100644
index 0000000..2eeefd8
--- /dev/null
+++ b/src/java/org/apache/ivy/core/settings/ivysettings-default-chain.xml
@@ -0,0 +1,26 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivysettings>
+ <resolvers>
+ <chain name="default" returnFirst="true" checkmodified="true">
+ <resolver ref="local"/>
+ <resolver ref="main"/>
+ </chain>
+ </resolvers>
+</ivysettings>
diff --git a/src/java/org/apache/ivy/core/settings/ivysettings-local.xml b/src/java/org/apache/ivy/core/settings/ivysettings-local.xml
new file mode 100644
index 0000000..acf224d
--- /dev/null
+++ b/src/java/org/apache/ivy/core/settings/ivysettings-local.xml
@@ -0,0 +1,29 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivysettings>
+ <property name="ivy.local.default.root" value="${ivy.default.ivy.user.dir}/local" override="false"/>
+ <property name="ivy.local.default.ivy.pattern" value="[organisation]/[module]/[revision]/[type]s/[artifact].[ext]" override="false"/>
+ <property name="ivy.local.default.artifact.pattern" value="[organisation]/[module]/[revision]/[type]s/[artifact].[ext]" override="false"/>
+ <resolvers>
+ <filesystem name="local">
+ <ivy pattern="${ivy.local.default.root}/${ivy.local.default.ivy.pattern}" />
+ <artifact pattern="${ivy.local.default.root}/${ivy.local.default.artifact.pattern}" />
+ </filesystem>
+ </resolvers>
+</ivysettings>
diff --git a/src/java/org/apache/ivy/core/settings/ivysettings-main-chain.xml b/src/java/org/apache/ivy/core/settings/ivysettings-main-chain.xml
new file mode 100644
index 0000000..8cb5cf5
--- /dev/null
+++ b/src/java/org/apache/ivy/core/settings/ivysettings-main-chain.xml
@@ -0,0 +1,26 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivysettings>
+ <resolvers>
+ <chain name="main" dual="true">
+ <resolver ref="shared"/>
+ <resolver ref="public"/>
+ </chain>
+ </resolvers>
+</ivysettings>
diff --git a/src/java/org/apache/ivy/core/settings/ivysettings-public.xml b/src/java/org/apache/ivy/core/settings/ivysettings-public.xml
new file mode 100644
index 0000000..09d1e58
--- /dev/null
+++ b/src/java/org/apache/ivy/core/settings/ivysettings-public.xml
@@ -0,0 +1,23 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivysettings>
+ <resolvers>
+ <ibiblio name="public" m2compatible="true"/>
+ </resolvers>
+</ivysettings>
diff --git a/src/java/org/apache/ivy/core/settings/ivysettings-shared.xml b/src/java/org/apache/ivy/core/settings/ivysettings-shared.xml
new file mode 100644
index 0000000..b5b530c
--- /dev/null
+++ b/src/java/org/apache/ivy/core/settings/ivysettings-shared.xml
@@ -0,0 +1,29 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivysettings>
+ <property name="ivy.shared.default.root" value="${ivy.default.ivy.user.dir}/shared" override="false"/>
+ <property name="ivy.shared.default.ivy.pattern" value="[organisation]/[module]/[revision]/[type]s/[artifact].[ext]" override="false"/>
+ <property name="ivy.shared.default.artifact.pattern" value="[organisation]/[module]/[revision]/[type]s/[artifact].[ext]" override="false"/>
+ <resolvers>
+ <filesystem name="shared">
+ <ivy pattern="${ivy.shared.default.root}/${ivy.shared.default.ivy.pattern}" />
+ <artifact pattern="${ivy.shared.default.root}/${ivy.shared.default.artifact.pattern}" />
+ </filesystem>
+ </resolvers>
+</ivysettings>
diff --git a/src/java/org/apache/ivy/core/settings/ivysettings.xml b/src/java/org/apache/ivy/core/settings/ivysettings.xml
new file mode 100644
index 0000000..6f0beb3
--- /dev/null
+++ b/src/java/org/apache/ivy/core/settings/ivysettings.xml
@@ -0,0 +1,26 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<ivysettings>
+ <settings defaultResolver="default"/>
+ <include url="${ivy.default.settings.dir}/ivysettings-public.xml"/>
+ <include url="${ivy.default.settings.dir}/ivysettings-shared.xml"/>
+ <include url="${ivy.default.settings.dir}/ivysettings-local.xml"/>
+ <include url="${ivy.default.settings.dir}/ivysettings-main-chain.xml"/>
+ <include url="${ivy.default.settings.dir}/ivysettings-default-chain.xml"/>
+</ivysettings>
diff --git a/src/java/org/apache/ivy/core/settings/repository.properties b/src/java/org/apache/ivy/core/settings/repository.properties
new file mode 100644
index 0000000..d42e3d8
--- /dev/null
+++ b/src/java/org/apache/ivy/core/settings/repository.properties
@@ -0,0 +1,24 @@
+# ***************************************************************
+# * Licensed to the Apache Software Foundation (ASF) under one
+# * or more contributor license agreements. See the NOTICE file
+# * distributed with this work for additional information
+# * regarding copyright ownership. The ASF licenses this file
+# * to you under the Apache License, Version 2.0 (the
+# * "License"); you may not use this file except in compliance
+# * with the License. You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing,
+# * software distributed under the License is distributed on an
+# * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# * KIND, either express or implied. See the License for the
+# * specific language governing permissions and limitations
+# * under the License.
+# ***************************************************************
+ivy.ibiblio.default.artifact.root = http://www.ibiblio.org/maven/
+ivy.ibiblio.default.artifact.pattern = [module]/[type]s/[artifact]-[revision].[ext]
+
+ivy.ivyrep.default.ivy.pattern = [organisation]/[module]/ivy-[revision].xml
+ivy.ivyrep.default.artifact.root = ${ivy.ibiblio.default.artifact.root}
+ivy.ivyrep.default.artifact.pattern = ${ivy.ibiblio.default.artifact.pattern}
diff --git a/src/java/org/apache/ivy/core/settings/typedef.properties b/src/java/org/apache/ivy/core/settings/typedef.properties
new file mode 100644
index 0000000..ba140d2
--- /dev/null
+++ b/src/java/org/apache/ivy/core/settings/typedef.properties
@@ -0,0 +1,67 @@
+# ***************************************************************
+# * Licensed to the Apache Software Foundation (ASF) under one
+# * or more contributor license agreements. See the NOTICE file
+# * distributed with this work for additional information
+# * regarding copyright ownership. The ASF licenses this file
+# * to you under the Apache License, Version 2.0 (the
+# * "License"); you may not use this file except in compliance
+# * with the License. You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing,
+# * software distributed under the License is distributed on an
+# * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# * KIND, either express or implied. See the License for the
+# * specific language governing permissions and limitations
+# * under the License.
+# ***************************************************************
+filesystem = org.apache.ivy.plugins.resolver.FileSystemResolver
+chain = org.apache.ivy.plugins.resolver.ChainResolver
+ibiblio = org.apache.ivy.plugins.resolver.IBiblioResolver
+bintray = org.apache.ivy.plugins.resolver.BintrayResolver
+url = org.apache.ivy.plugins.resolver.URLResolver
+dual = org.apache.ivy.plugins.resolver.DualResolver
+ivyrep = org.apache.ivy.plugins.resolver.IvyRepResolver
+ssh = org.apache.ivy.plugins.resolver.SshResolver
+sftp = org.apache.ivy.plugins.resolver.SFTPResolver
+vsftp = org.apache.ivy.plugins.resolver.VsftpResolver
+vfs = org.apache.ivy.plugins.resolver.VfsResolver
+jar = org.apache.ivy.plugins.resolver.JarResolver
+cache = org.apache.ivy.plugins.resolver.CacheResolver
+packager = org.apache.ivy.plugins.resolver.packager.PackagerResolver
+obr = org.apache.ivy.osgi.obr.OBRResolver
+mirroredurl = org.apache.ivy.plugins.resolver.MirroredURLResolver
+updatesite = org.apache.ivy.osgi.updatesite.UpdateSiteResolver
+osgi-agg = org.apache.ivy.osgi.repo.AggregatedOSGiResolver
+
+latest-revision = org.apache.ivy.plugins.latest.LatestRevisionStrategy
+latest-lexico = org.apache.ivy.plugins.latest.LatestLexicographicStrategy
+latest-time = org.apache.ivy.plugins.latest.LatestTimeStrategy
+
+fixed-cm = org.apache.ivy.plugins.conflict.FixedConflictManager
+latest-cm = org.apache.ivy.plugins.conflict.LatestConflictManager
+compatible-cm = org.apache.ivy.plugins.conflict.LatestCompatibleConflictManager
+no-cm = org.apache.ivy.plugins.conflict.NoConflictManager
+strict-cm = org.apache.ivy.plugins.conflict.StrictConflictManager
+regexp-cm = org.apache.ivy.plugins.conflict.RegexpConflictManager
+
+
+namespace = org.apache.ivy.plugins.namespace.Namespace
+
+exact-vm = org.apache.ivy.plugins.version.ExactVersionMatcher
+chain-vm = org.apache.ivy.plugins.version.ChainVersionMatcher
+latest-vm = org.apache.ivy.plugins.version.LatestVersionMatcher
+sub-vm = org.apache.ivy.plugins.version.SubVersionMatcher
+range-vm = org.apache.ivy.plugins.version.VersionRangeMatcher
+pattern-vm = org.apache.ivy.plugins.version.PatternVersionMatcher
+
+ant-build = org.apache.ivy.ant.AntBuildTrigger
+ant-call = org.apache.ivy.ant.AntCallTrigger
+log = org.apache.ivy.plugins.trigger.LogTrigger
+
+cache = org.apache.ivy.core.cache.DefaultRepositoryCacheManager
+
+pgp = org.apache.ivy.plugins.signer.bouncycastle.OpenPGPSignatureGenerator
+
+osgi-manifest-parser = org.apache.ivy.osgi.core.OSGiManifestParser
diff --git a/src/java/org/apache/ivy/core/sort/CollectionOfModulesToSort.java b/src/java/org/apache/ivy/core/sort/CollectionOfModulesToSort.java
new file mode 100644
index 0000000..c185e37
--- /dev/null
+++ b/src/java/org/apache/ivy/core/sort/CollectionOfModulesToSort.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.sort;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleId;
+import org.apache.ivy.plugins.version.VersionMatcher;
+
+/**
+ * Wrap a collection of descriptores wrapped themself in ModuleInSort elements. It contains some
+ * dedicated function to retrieve module descriptors based on dependencies descriptors.<br>
+ * <i>This class is designed to be used internally by the ModuleDescriptorSorter.</i>
+ */
+class CollectionOfModulesToSort implements Iterable<ModuleInSort> {
+
+ private final List<ModuleInSort> moduleDescriptors;
+
+ private final VersionMatcher versionMatcher;
+
+ private final Map<ModuleId, Collection<ModuleInSort>> modulesByModuleId;
+
+ private final NonMatchingVersionReporter nonMatchingVersionReporter;
+
+ /**
+ * @param modulesToSort
+ * The collection of ModuleDescriptor to sort
+ * @param matcher
+ * The matcher to used to check if dependencyDescriptor match a module in this
+ * collection
+ * @param nonMatchingVersionReporter
+ */
+ public CollectionOfModulesToSort(Collection<ModuleDescriptor> modulesToSort,
+ VersionMatcher matcher, NonMatchingVersionReporter nonMatchingVersionReporter) {
+ this.versionMatcher = matcher;
+ this.nonMatchingVersionReporter = nonMatchingVersionReporter;
+ this.modulesByModuleId = new HashMap<ModuleId, Collection<ModuleInSort>>();
+ moduleDescriptors = new ArrayList<ModuleInSort>(modulesToSort.size());
+ for (ModuleDescriptor md : modulesToSort) {
+ ModuleInSort mdInSort = new ModuleInSort(md);
+ moduleDescriptors.add(mdInSort);
+ addToModulesByModuleId(md, mdInSort);
+ }
+ }
+
+ private void addToModulesByModuleId(ModuleDescriptor md, ModuleInSort mdInSort) {
+ ModuleId mdId = md.getModuleRevisionId().getModuleId();
+ List<ModuleInSort> mdInSortAsList = new LinkedList<ModuleInSort>();
+ mdInSortAsList.add(mdInSort);
+ Collection<ModuleInSort> previousList = modulesByModuleId.put(mdId, mdInSortAsList);
+ if (previousList != null) {
+ mdInSortAsList.addAll(previousList);
+ }
+ }
+
+ public Iterator<ModuleInSort> iterator() {
+ return moduleDescriptors.iterator();
+ }
+
+ public int size() {
+ return moduleDescriptors.size();
+ }
+
+ /**
+ * Find a matching module descriptor in the list of module to sort.
+ *
+ * @param descriptor
+ * @return a ModuleDescriptor from the collection of module descriptors to sort. If none exists
+ * returns null.
+ */
+ public ModuleInSort getModuleDescriptorDependency(DependencyDescriptor descriptor) {
+ Collection<ModuleInSort> modulesOfSameId = modulesByModuleId.get(descriptor
+ .getDependencyId());
+ if (modulesOfSameId == null) {
+ return null;
+ }
+ for (ModuleInSort mdInSort : modulesOfSameId) {
+ if (mdInSort.match(descriptor, versionMatcher)) {
+ return mdInSort;
+ } else {
+ nonMatchingVersionReporter.reportNonMatchingVersion(descriptor,
+ mdInSort.getSortedModuleDescriptor());
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/src/java/org/apache/ivy/core/sort/MessageBasedNonMatchingVersionReporter.java b/src/java/org/apache/ivy/core/sort/MessageBasedNonMatchingVersionReporter.java
new file mode 100644
index 0000000..7034ebd
--- /dev/null
+++ b/src/java/org/apache/ivy/core/sort/MessageBasedNonMatchingVersionReporter.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.sort;
+
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleId;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+
+abstract class MessageBasedNonMatchingVersionReporter implements NonMatchingVersionReporter {
+
+ public void reportNonMatchingVersion(DependencyDescriptor descriptor, ModuleDescriptor md) {
+ ModuleRevisionId dependencyRevisionId = descriptor.getDependencyRevisionId();
+ ModuleRevisionId parentRevisionId = descriptor.getParentRevisionId();
+ if (parentRevisionId == null) {
+ // There are some rare case where DependencyDescriptor have no parent.
+ // This is should not be used in the SortEngine, but if it is, we
+ // show a decent trace.
+ reportMessage("Non matching revision detected when sorting. Dependency "
+ + dependencyRevisionId + " doesn't match " + md.getModuleRevisionId());
+ } else {
+ ModuleId parentModuleId = parentRevisionId.getModuleId();
+ reportMessage("Non matching revision detected when sorting. " + parentModuleId
+ + " depends on " + dependencyRevisionId + ", doesn't match "
+ + md.getModuleRevisionId());
+ }
+ }
+
+ protected abstract void reportMessage(String msg);
+
+}
diff --git a/src/java/org/apache/ivy/core/sort/ModuleDescriptorSorter.java b/src/java/org/apache/ivy/core/sort/ModuleDescriptorSorter.java
new file mode 100644
index 0000000..da0fb3b
--- /dev/null
+++ b/src/java/org/apache/ivy/core/sort/ModuleDescriptorSorter.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.sort;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.plugins.circular.CircularDependencyException;
+import org.apache.ivy.plugins.circular.CircularDependencyStrategy;
+import org.apache.ivy.plugins.version.VersionMatcher;
+import org.apache.ivy.util.Message;
+
+/**
+ * Inner helper class for sorting ModuleDescriptors.<br>
+ * ModuleDescriptorSorter use CollectionOfModulesToSort to find the dependencies of the modules, and
+ * use ModuleInSort to store some temporary values attached to the modules to sort.
+ *
+ * @see ModuleInSort
+ * @see CollectionOfModulesToSort
+ */
+public class ModuleDescriptorSorter {
+
+ private final CollectionOfModulesToSort moduleDescriptors;
+
+ private final List<ModuleDescriptor> sorted = new LinkedList<ModuleDescriptor>();
+
+ private final CircularDependencyStrategy circularDepStrategy;
+
+ public ModuleDescriptorSorter(Collection<ModuleDescriptor> modulesDescriptorsToSort,
+ VersionMatcher matcher, NonMatchingVersionReporter nonMatchingVersionReporter,
+ CircularDependencyStrategy circularDepStrategy) {
+ this.circularDepStrategy = circularDepStrategy;
+ moduleDescriptors = new CollectionOfModulesToSort(modulesDescriptorsToSort, matcher,
+ nonMatchingVersionReporter);
+ }
+
+ /**
+ * Iterates over all modules calling sortModuleDescriptorsHelp.
+ *
+ * @return sorted module
+ * @throws CircularDependencyException
+ */
+ public List<ModuleDescriptor> sortModuleDescriptors() throws CircularDependencyException {
+ Message.debug("Nbr of module to sort : " + moduleDescriptors.size());
+ for (ModuleInSort m : moduleDescriptors) {
+ sortModuleDescriptorsHelp(m, m);
+ }
+ return sorted;
+ }
+
+ /**
+ * If current module has already been added to list, returns, Otherwise invokes
+ * sortModuleDescriptorsHelp for all dependencies contained within set of moduleDescriptors.
+ * Then finally adds self to list of sorted.<br/>
+ * When a loop is detected by a recursive call, the moduleDescriptors are not added immediately
+ * added to the sorted list. They are added as loop dependencies of the root, and will be added
+ * to the sorted list only when the root itself will be added.
+ *
+ * @param current
+ * Current module to add to sorted list.
+ * @throws CircularDependencyException
+ */
+ private void sortModuleDescriptorsHelp(ModuleInSort current, ModuleInSort caller)
+ throws CircularDependencyException {
+ // if already sorted return
+ if (current.isProcessed()) {
+ return;
+ }
+ if (current.checkLoop(caller, circularDepStrategy)) {
+ return;
+ }
+ DependencyDescriptor[] descriptors = current.getDependencies();
+ Message.debug("Sort dependencies of : " + current.toString()
+ + " / Number of dependencies = " + descriptors.length);
+ current.setCaller(caller);
+ for (int i = 0; i < descriptors.length; i++) {
+ ModuleInSort child = moduleDescriptors.getModuleDescriptorDependency(descriptors[i]);
+ if (child != null) {
+ sortModuleDescriptorsHelp(child, current);
+ }
+ }
+ current.endOfCall();
+ Message.debug("Sort done for : " + current.toString());
+ current.addToSortedListIfRequired(sorted);
+ }
+
+}
diff --git a/src/java/org/apache/ivy/core/sort/ModuleInSort.java b/src/java/org/apache/ivy/core/sort/ModuleInSort.java
new file mode 100644
index 0000000..f0f5556
--- /dev/null
+++ b/src/java/org/apache/ivy/core/sort/ModuleInSort.java
@@ -0,0 +1,187 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.sort;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.ivy.Ivy;
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.plugins.circular.CircularDependencyHelper;
+import org.apache.ivy.plugins.circular.CircularDependencyStrategy;
+import org.apache.ivy.plugins.version.VersionMatcher;
+import org.apache.ivy.util.Message;
+
+/**
+ * Decorates a ModuleDescriptor with some attributes used during the sort. Thus every instance of a
+ * ModuleInSort can be used in only one ModuleDescriptorSorter at a time.<br>
+ * The added fields are : <br>
+ * <ul>
+ * <li><code>isSorted</code> : is true iff this module has already been added to the sorted list.</li>
+ * <li><code>loopElements</code> : When the module is the root of a loop (=the first element of a
+ * loop met during the sort), <code>loopElements</code> contains all ModuleInSort of the loop
+ * (excluding the root itself.</li>
+ * <li><code>isLoopIntermediateElement</code> : When a loop is detected, all modules included in the
+ * loop (except the root) have <code>isLoopIntermediateElement</code> set to true.</li>
+ * <li><code>caller</code> : During the sort, we traverse recursively the graph. When doing that,
+ * caller point to the parent element.
+ */
+class ModuleInSort {
+
+ private final ModuleDescriptor module;
+
+ private boolean isSorted = false;
+
+ private List<ModuleInSort> loopElements = new LinkedList<ModuleInSort>();
+
+ private boolean isLoopIntermediateElement = false;
+
+ private ModuleInSort caller;
+
+ public ModuleInSort(ModuleDescriptor moduleToSort) {
+ module = moduleToSort;
+ }
+
+ public boolean isInLoop() {
+ return isLoopIntermediateElement;
+ }
+
+ /** This ModuleInSort has been placed on the sorted list */
+ public boolean isSorted() {
+ if (isSorted) {
+ Message.debug("Module descriptor already sorted : "
+ + module.getModuleRevisionId().toString());
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * This ModuleInSort has already been analyzed. It is either already added to the sorted list,
+ * either it is included in a loop and will be added when the root of the loop will be added to
+ * the list.
+ */
+ public boolean isProcessed() {
+ if (isSorted || isLoopIntermediateElement) {
+ Message.debug("Module descriptor is processed : "
+ + module.getModuleRevisionId().toString());
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public void setCaller(ModuleInSort caller) {
+ this.caller = caller;
+ }
+
+ public void endOfCall() {
+ caller = null;
+ }
+
+ /**
+ * Check if a adding this element as a dependency of caller will introduce a circular
+ * dependency. If it is, all the elements of the loop are flaged as 'loopIntermediateElement',
+ * and the loopElements of this module (which is the root of the loop) is updated. The
+ * depStrategy is invoked on order to report a correct circular loop message.
+ *
+ * @param futurCaller
+ * @param depStrategy
+ * @return true if a loop is detected.
+ */
+ public boolean checkLoop(ModuleInSort futurCaller, CircularDependencyStrategy depStrategy) {
+ if (caller != null) {
+ LinkedList<ModuleRevisionId> elemOfLoop = new LinkedList<ModuleRevisionId>();
+ elemOfLoop.add(this.module.getModuleRevisionId());
+ for (ModuleInSort stackEl = futurCaller; stackEl != this; stackEl = stackEl.caller) {
+ elemOfLoop.add(stackEl.module.getModuleRevisionId());
+ stackEl.isLoopIntermediateElement = true;
+ loopElements.add(stackEl);
+ }
+ elemOfLoop.add(this.module.getModuleRevisionId());
+ ModuleRevisionId[] mrids = elemOfLoop.toArray(new ModuleRevisionId[elemOfLoop.size()]);
+ depStrategy.handleCircularDependency(mrids);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Add this module to the sorted list except if this module is an intermediary element of a
+ * loop. If this module is the 'root' of a loop, then all elements of that loops are added
+ * before.
+ *
+ * @param sorted
+ * The list of sorted elements on which this module will be added
+ */
+ public void addToSortedListIfRequired(List<ModuleDescriptor> sorted) {
+ if (!isLoopIntermediateElement) {
+ addToSortList(sorted);
+ }
+ }
+
+ /**
+ * Add this module to the sorted list. If current is the 'root' of a loop, then all elements of
+ * that loops are added before.
+ */
+ private void addToSortList(List<ModuleDescriptor> sortedList) {
+ for (ModuleInSort moduleInLoop : loopElements) {
+ moduleInLoop.addToSortList(sortedList);
+ }
+ if (!this.isSorted()) {
+ sortedList.add(module);
+ this.isSorted = true;
+ }
+ }
+
+ public String toString() {
+ return module.getModuleRevisionId().toString();
+ }
+
+ public DependencyDescriptor[] getDependencies() {
+ return module.getDependencies();
+ }
+
+ /** Log a warning saying that a loop is detected */
+ public static void logLoopWarning(List loopElement) {
+ Message.warn("circular dependency detected during sort: "
+ + CircularDependencyHelper.formatMessageFromDescriptors(loopElement));
+ }
+
+ /**
+ * Return true if this module match the DependencyDescriptor with the given versionMatcher. If
+ * this module has no version defined, then true is always returned.
+ */
+ public boolean match(DependencyDescriptor descriptor, VersionMatcher versionMatcher) {
+ ModuleDescriptor md = module;
+ return md.getResolvedModuleRevisionId().getRevision() == null
+ || md.getResolvedModuleRevisionId().getRevision().equals(Ivy.getWorkingRevision())
+ || versionMatcher.accept(descriptor.getDependencyRevisionId(), md);
+ // Checking md.getResolvedModuleRevisionId().getRevision().equals(Ivy.getWorkingRevision()
+ // allow to consider any local non resolved ivy.xml
+ // as a valid module.
+ }
+
+ public ModuleDescriptor getSortedModuleDescriptor() {
+ return module;
+ }
+}
diff --git a/src/java/org/apache/ivy/core/sort/NonMatchingVersionReporter.java b/src/java/org/apache/ivy/core/sort/NonMatchingVersionReporter.java
new file mode 100644
index 0000000..5870474
--- /dev/null
+++ b/src/java/org/apache/ivy/core/sort/NonMatchingVersionReporter.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.sort;
+
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+
+public interface NonMatchingVersionReporter {
+
+ /**
+ * Report to the user that ivy has detected that a module to sort has a dependency on an other
+ * module to sort, but the revisions doesn't match.
+ *
+ * @param descriptor
+ * The non matching dependency descriptor.
+ * @param md
+ * The module to sort having the corect moduleID but a non matching revision
+ */
+ public void reportNonMatchingVersion(DependencyDescriptor descriptor, ModuleDescriptor md);
+
+}
diff --git a/src/java/org/apache/ivy/core/sort/SilentNonMatchingVersionReporter.java b/src/java/org/apache/ivy/core/sort/SilentNonMatchingVersionReporter.java
new file mode 100644
index 0000000..5212d24
--- /dev/null
+++ b/src/java/org/apache/ivy/core/sort/SilentNonMatchingVersionReporter.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.sort;
+
+import org.apache.ivy.util.Message;
+
+/**
+ * A NonMatchingVersionReporter that only print debug message.
+ */
+public class SilentNonMatchingVersionReporter extends MessageBasedNonMatchingVersionReporter
+ implements NonMatchingVersionReporter {
+
+ protected void reportMessage(String msg) {
+ Message.debug(msg);
+ }
+
+}
diff --git a/src/java/org/apache/ivy/core/sort/SimpleSortEngineSettings.java b/src/java/org/apache/ivy/core/sort/SimpleSortEngineSettings.java
new file mode 100644
index 0000000..cb645b7
--- /dev/null
+++ b/src/java/org/apache/ivy/core/sort/SimpleSortEngineSettings.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.sort;
+
+import org.apache.ivy.plugins.circular.CircularDependencyStrategy;
+import org.apache.ivy.plugins.version.VersionMatcher;
+
+public class SimpleSortEngineSettings implements SortEngineSettings {
+
+ private CircularDependencyStrategy circularStrategy;
+
+ private VersionMatcher versionMatcher;
+
+ public CircularDependencyStrategy getCircularDependencyStrategy() {
+ return circularStrategy;
+ }
+
+ public VersionMatcher getVersionMatcher() {
+ return versionMatcher;
+ }
+
+ public void setCircularDependencyStrategy(CircularDependencyStrategy strategy) {
+ circularStrategy = strategy;
+ }
+
+ public void setVersionMatcher(VersionMatcher matcher) {
+ versionMatcher = matcher;
+ }
+
+}
diff --git a/src/java/org/apache/ivy/core/sort/SortEngine.java b/src/java/org/apache/ivy/core/sort/SortEngine.java
new file mode 100644
index 0000000..e6b6774
--- /dev/null
+++ b/src/java/org/apache/ivy/core/sort/SortEngine.java
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.sort;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.resolve.IvyNode;
+import org.apache.ivy.plugins.circular.CircularDependencyException;
+import org.apache.ivy.plugins.circular.CircularDependencyStrategy;
+import org.apache.ivy.plugins.circular.IgnoreCircularDependencyStrategy;
+import org.apache.ivy.plugins.version.VersionMatcher;
+import org.apache.ivy.util.Checks;
+
+public class SortEngine {
+
+ private SortEngineSettings settings;
+
+ public SortEngine(SortEngineSettings settings) {
+ if (settings == null) {
+ throw new NullPointerException("SortEngine.settings can not be null");
+ }
+ this.settings = settings;
+ }
+
+ /**
+ * Same as {@link #sortModuleDescriptors(Collection, SortOptions)} but for <code>IvyNode</code>
+ * s.
+ *
+ * @param nodes
+ * a Collection of nodes to sort
+ * @param options
+ * Options to use to sort the nodes.
+ * @return a List of sorted IvyNode
+ * @throws CircularDependencyException
+ * if a circular dependency exists and circular dependency strategy decide to throw
+ * an exception
+ */
+ public List<IvyNode> sortNodes(Collection<IvyNode> nodes, SortOptions options) {
+ /*
+ * here we want to use the sort algorithm which work on module descriptors : so we first put
+ * dependencies on a map from descriptors to dependency, then we sort the keySet (i.e. a
+ * collection of descriptors), then we replace in the sorted list each descriptor by the
+ * corresponding dependency
+ */
+
+ Map<ModuleDescriptor, List<IvyNode>> dependenciesMap = new LinkedHashMap<ModuleDescriptor, List<IvyNode>>();
+ List<IvyNode> nulls = new ArrayList<IvyNode>();
+ for (IvyNode node : nodes) {
+ if (node.getDescriptor() == null) {
+ nulls.add(node);
+ } else {
+ List<IvyNode> n = dependenciesMap.get(node.getDescriptor());
+ if (n == null) {
+ n = new ArrayList<IvyNode>();
+ dependenciesMap.put(node.getDescriptor(), n);
+ }
+ n.add(node);
+ }
+ }
+ List<ModuleDescriptor> list = sortModuleDescriptors(dependenciesMap.keySet(), options);
+ final double adjustFactor = 1.3;
+ List<IvyNode> ret = new ArrayList<IvyNode>(
+ (int) (list.size() * adjustFactor + nulls.size()));
+ // attempt to adjust the size to avoid too much list resizing
+ for (int i = 0; i < list.size(); i++) {
+ ModuleDescriptor md = list.get(i);
+ List<IvyNode> n = dependenciesMap.get(md);
+ ret.addAll(n);
+ }
+ ret.addAll(0, nulls);
+ return ret;
+ }
+
+ /**
+ * Sorts the given ModuleDescriptors from the less dependent to the more dependent. This sort
+ * ensures that a ModuleDescriptor is always found in the list before all ModuleDescriptors
+ * depending directly on it.
+ *
+ * @param moduleDescriptors
+ * a Collection of ModuleDescriptor to sort
+ * @param options
+ * Options to use to sort the descriptors.
+ * @return a List of sorted ModuleDescriptors
+ * @throws CircularDependencyException
+ * if a circular dependency exists and circular dependency strategy decide to throw
+ * an exception
+ */
+ public List<ModuleDescriptor> sortModuleDescriptors(
+ Collection<ModuleDescriptor> moduleDescriptors, SortOptions options)
+ throws CircularDependencyException {
+ Checks.checkNotNull(options, "options");
+ ModuleDescriptorSorter sorter = new ModuleDescriptorSorter(moduleDescriptors,
+ getVersionMatcher(), options.getNonMatchingVersionReporter(),
+ options.isUseCircularDependencyStrategy() ? getCircularStrategy()
+ : IgnoreCircularDependencyStrategy.getInstance());
+ return sorter.sortModuleDescriptors();
+ }
+
+ protected CircularDependencyStrategy getCircularStrategy() {
+ return settings.getCircularDependencyStrategy();
+ }
+
+ protected VersionMatcher getVersionMatcher() {
+ return settings.getVersionMatcher();
+ }
+
+}
diff --git a/src/java/org/apache/ivy/core/sort/SortEngineSettings.java b/src/java/org/apache/ivy/core/sort/SortEngineSettings.java
new file mode 100644
index 0000000..a1fe8ea
--- /dev/null
+++ b/src/java/org/apache/ivy/core/sort/SortEngineSettings.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.sort;
+
+import org.apache.ivy.plugins.circular.CircularDependencyStrategy;
+import org.apache.ivy.plugins.version.VersionMatcher;
+
+/**
+ * The settings/collaborators used by the SortEngine.
+ */
+public interface SortEngineSettings {
+
+ public CircularDependencyStrategy getCircularDependencyStrategy();
+
+ public VersionMatcher getVersionMatcher();
+}
diff --git a/src/java/org/apache/ivy/core/sort/SortOptions.java b/src/java/org/apache/ivy/core/sort/SortOptions.java
new file mode 100644
index 0000000..adb7bb1
--- /dev/null
+++ b/src/java/org/apache/ivy/core/sort/SortOptions.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.sort;
+
+public class SortOptions {
+ public static final SortOptions DEFAULT = new SortOptions();
+
+ public static final SortOptions SILENT = new SortOptions().setNonMatchingVersionReporter(
+ new SilentNonMatchingVersionReporter()).setUseCircularDependencyStrategy(false);
+
+ /**
+ * Used to report some non matching version (when a modules depends on a specific revision of an
+ * other modules present in the of modules to sort with a different revision.
+ */
+ private NonMatchingVersionReporter nonMatchingVersionReporter = new WarningNonMatchingVersionReporter();
+
+ /**
+ * Should the default circular dependency strategy be used when a circular dependency is found,
+ * or should circular dependencies be ignored?
+ */
+ private boolean useCircularDependencyStrategy = true;
+
+ public NonMatchingVersionReporter getNonMatchingVersionReporter() {
+ return nonMatchingVersionReporter;
+ }
+
+ public SortOptions setNonMatchingVersionReporter(
+ NonMatchingVersionReporter nonMatchingVersionReporter) {
+ this.nonMatchingVersionReporter = nonMatchingVersionReporter;
+ return this;
+ }
+
+ public boolean isUseCircularDependencyStrategy() {
+ return useCircularDependencyStrategy;
+ }
+
+ public SortOptions setUseCircularDependencyStrategy(boolean useCircularDependencyStrategy) {
+ this.useCircularDependencyStrategy = useCircularDependencyStrategy;
+ return this;
+ }
+}
diff --git a/src/java/org/apache/ivy/core/sort/WarningNonMatchingVersionReporter.java b/src/java/org/apache/ivy/core/sort/WarningNonMatchingVersionReporter.java
new file mode 100644
index 0000000..56b39f3
--- /dev/null
+++ b/src/java/org/apache/ivy/core/sort/WarningNonMatchingVersionReporter.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.core.sort;
+
+import org.apache.ivy.util.Message;
+
+/**
+ * A NonMatchingVersionReporter that raise warnings.
+ */
+public class WarningNonMatchingVersionReporter extends MessageBasedNonMatchingVersionReporter
+ implements NonMatchingVersionReporter {
+
+ protected void reportMessage(String msg) {
+ Message.warn(msg);
+ }
+
+}
diff --git a/src/java/org/apache/ivy/logo.png b/src/java/org/apache/ivy/logo.png
new file mode 100644
index 0000000..4baf170
Binary files /dev/null and b/src/java/org/apache/ivy/logo.png differ
diff --git a/src/java/org/apache/ivy/osgi/core/BundleArtifact.java b/src/java/org/apache/ivy/osgi/core/BundleArtifact.java
new file mode 100644
index 0000000..180fa01
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/core/BundleArtifact.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.core;
+
+import java.net.URI;
+
+public class BundleArtifact {
+
+ private boolean source = false;
+
+ private URI uri;
+
+ private String format;
+
+ public BundleArtifact(boolean source, URI uri, String format) {
+ this.source = source;
+ this.uri = uri;
+ this.format = format;
+ }
+
+ public boolean isSource() {
+ return source;
+ }
+
+ public URI getUri() {
+ return uri;
+ }
+
+ public String getFormat() {
+ return format;
+ }
+
+}
diff --git a/src/java/org/apache/ivy/osgi/core/BundleCapability.java b/src/java/org/apache/ivy/osgi/core/BundleCapability.java
new file mode 100644
index 0000000..96f493d
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/core/BundleCapability.java
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.core;
+
+import org.apache.ivy.osgi.util.Version;
+
+public class BundleCapability {
+
+ private final String name;
+
+ private final Version version;
+
+ private final String type;
+
+ public BundleCapability(String type, String name, Version version) {
+ this.type = type;
+ this.name = name;
+ this.version = version;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Version getVersion() {
+ return version;
+ }
+
+ public Version getRawVersion() {
+ return version;
+ }
+
+ public String toString() {
+ return name + (version == null ? "" : ";" + version);
+ }
+
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ result = prime * result + ((version == null) ? 0 : version.hashCode());
+ return result;
+ }
+
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (!(obj instanceof BundleCapability)) {
+ return false;
+ }
+ BundleCapability other = (BundleCapability) obj;
+ if (name == null) {
+ if (other.name != null) {
+ return false;
+ }
+ } else if (!name.equals(other.name)) {
+ return false;
+ }
+ if (version == null) {
+ if (other.version != null) {
+ return false;
+ }
+ } else if (!version.equals(other.version)) {
+ return false;
+ }
+ return true;
+ }
+
+}
\ No newline at end of file
diff --git a/src/java/org/apache/ivy/osgi/core/BundleInfo.java b/src/java/org/apache/ivy/osgi/core/BundleInfo.java
new file mode 100644
index 0000000..8840351
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/core/BundleInfo.java
@@ -0,0 +1,381 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.core;
+
+import java.util.ArrayList;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.ivy.osgi.util.Version;
+
+/**
+ * Bundle info extracted from the bundle manifest.
+ *
+ */
+public class BundleInfo {
+
+ public static final Version DEFAULT_VERSION = new Version(1, 0, 0, null);
+
+ public static final String PACKAGE_TYPE = "package";
+
+ public static final String BUNDLE_TYPE = "bundle";
+
+ public static final String EXECUTION_ENVIRONMENT_TYPE = "ee";
+
+ public static final String SERVICE_TYPE = "service";
+
+ private String symbolicName;
+
+ private String presentationName;
+
+ private String id;
+
+ private Version version;
+
+ private Set<BundleRequirement> requirements = new LinkedHashSet<BundleRequirement>();
+
+ private Set<BundleCapability> capabilities = new LinkedHashSet<BundleCapability>();
+
+ private List<String> executionEnvironments = new ArrayList<String>();
+
+ private String description;
+
+ private String documentation;
+
+ private String license;
+
+ private Integer size;
+
+ private boolean isSource = false;
+
+ /** the symbolic name of the bundle it is source of */
+ private String symbolicNameTarget;
+
+ /** the version of the bundle it is source of */
+ private Version versionTarget;
+
+ private boolean hasInnerClasspath;
+
+ private List<String> classpath;
+
+ private List<BundleArtifact> artifacts = new ArrayList<BundleArtifact>();
+
+ public BundleInfo(String name, Version version) {
+ this.symbolicName = name;
+ this.version = version;
+ }
+
+ public String toString() {
+ StringBuffer builder = new StringBuffer();
+ builder.append("BundleInfo [executionEnvironments=");
+ builder.append(executionEnvironments);
+ builder.append(", capabilities=");
+ builder.append(capabilities);
+ builder.append(", requirements=");
+ builder.append(requirements);
+ builder.append(", symbolicName=");
+ builder.append(symbolicName);
+ builder.append(", version=");
+ builder.append(version);
+ builder.append("]");
+ if (symbolicNameTarget != null) {
+ builder.append(" source of ");
+ builder.append(symbolicNameTarget);
+ builder.append("@");
+ builder.append(versionTarget);
+ }
+ return builder.toString();
+ }
+
+ public String getSymbolicName() {
+ return symbolicName;
+ }
+
+ public Version getVersion() {
+ return version == null ? DEFAULT_VERSION : version;
+ }
+
+ public Version getRawVersion() {
+ return version;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setPresentationName(String presentationName) {
+ this.presentationName = presentationName;
+ }
+
+ public String getPresentationName() {
+ return presentationName;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDocumentation(String documentation) {
+ this.documentation = documentation;
+ }
+
+ public String getDocumentation() {
+ return documentation;
+ }
+
+ public void setLicense(String license) {
+ this.license = license;
+ }
+
+ public String getLicense() {
+ return license;
+ }
+
+ public void setSize(Integer size) {
+ this.size = size;
+ }
+
+ public Integer getSize() {
+ return size;
+ }
+
+ public void addRequirement(BundleRequirement requirement) {
+ requirements.add(requirement);
+ }
+
+ public Set<BundleRequirement> getRequirements() {
+ return requirements;
+ }
+
+ public void addCapability(BundleCapability capability) {
+ capabilities.add(capability);
+ }
+
+ public Set<BundleCapability> getCapabilities() {
+ return capabilities;
+ }
+
+ public List<String> getExecutionEnvironments() {
+ return executionEnvironments;
+ }
+
+ public void setExecutionEnvironments(List<String> executionEnvironments) {
+ this.executionEnvironments = executionEnvironments;
+ for (String executionEnvironment : executionEnvironments) {
+ addRequirement(new BundleRequirement(EXECUTION_ENVIRONMENT_TYPE, executionEnvironment,
+ null, null));
+ }
+ }
+
+ public void addExecutionEnvironment(String name) {
+ executionEnvironments.add(name);
+ }
+
+ public void setSource(boolean isSource) {
+ this.isSource = isSource;
+ }
+
+ public boolean isSource() {
+ return isSource;
+ }
+
+ public void setSymbolicNameTarget(String symbolicNameTarget) {
+ this.symbolicNameTarget = symbolicNameTarget;
+ }
+
+ public String getSymbolicNameTarget() {
+ return symbolicNameTarget;
+ }
+
+ public void setVersionTarget(Version versionTarget) {
+ this.versionTarget = versionTarget;
+ }
+
+ public Version getVersionTarget() {
+ return versionTarget;
+ }
+
+ public void setHasInnerClasspath(boolean hasInnerClasspath) {
+ this.hasInnerClasspath = hasInnerClasspath;
+ }
+
+ public boolean hasInnerClasspath() {
+ return hasInnerClasspath;
+ }
+
+ public void setClasspath(List<String> classpath) {
+ this.classpath = classpath;
+ }
+
+ public List<String> getClasspath() {
+ return classpath;
+ }
+
+ public void addArtifact(BundleArtifact artifact) {
+ artifacts.add(artifact);
+ }
+
+ public void removeArtifact(BundleArtifact same) {
+ artifacts.remove(same);
+ }
+
+ public List<BundleArtifact> getArtifacts() {
+ return artifacts;
+ }
+
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((capabilities == null) ? 0 : capabilities.hashCode());
+ result = prime * result + ((requirements == null) ? 0 : requirements.hashCode());
+ result = prime * result + ((symbolicName == null) ? 0 : symbolicName.hashCode());
+ result = prime * result + ((version == null) ? 0 : version.hashCode());
+ result = prime * result
+ + ((executionEnvironments == null) ? 0 : executionEnvironments.hashCode());
+ result = prime * result
+ + ((symbolicNameTarget == null) ? 0 : symbolicNameTarget.hashCode());
+ result = prime * result + ((versionTarget == null) ? 0 : versionTarget.hashCode());
+ return result;
+ }
+
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (!(obj instanceof BundleInfo)) {
+ return false;
+ }
+ BundleInfo other = (BundleInfo) obj;
+ if (capabilities == null) {
+ if (other.capabilities != null) {
+ return false;
+ }
+ } else if (!capabilities.equals(other.capabilities)) {
+ return false;
+ }
+ if (requirements == null) {
+ if (other.requirements != null) {
+ return false;
+ }
+ } else if (!requirements.equals(other.requirements)) {
+ return false;
+ }
+ if (symbolicName == null) {
+ if (other.symbolicName != null) {
+ return false;
+ }
+ } else if (!symbolicName.equals(other.symbolicName)) {
+ return false;
+ }
+ if (version == null) {
+ if (other.version != null) {
+ return false;
+ }
+ } else if (!version.equals(other.version)) {
+ return false;
+ }
+ if (executionEnvironments == null) {
+ if (other.executionEnvironments != null) {
+ return false;
+ }
+ } else if (!executionEnvironments.equals(other.executionEnvironments)) {
+ return false;
+ }
+ if (isSource != other.isSource) {
+ return false;
+ }
+ if (symbolicNameTarget == null) {
+ if (other.symbolicNameTarget != null) {
+ return false;
+ }
+ } else if (!symbolicNameTarget.equals(other.symbolicNameTarget)) {
+ return false;
+ }
+ if (versionTarget == null) {
+ if (other.versionTarget != null) {
+ return false;
+ }
+ } else if (!versionTarget.equals(other.versionTarget)) {
+ return false;
+ }
+ if (hasInnerClasspath != other.hasInnerClasspath) {
+ return false;
+ }
+ if (classpath == null) {
+ if (other.classpath != null) {
+ return false;
+ }
+ } else if (!classpath.equals(other.classpath)) {
+ return false;
+ }
+ return true;
+ }
+
+ public Set<BundleRequirement> getRequires() {
+ Set<BundleRequirement> set = new LinkedHashSet<BundleRequirement>();
+ for (BundleRequirement requirement : requirements) {
+ if (requirement.getType().equals(BUNDLE_TYPE)) {
+ set.add(requirement);
+ }
+ }
+ return set;
+ }
+
+ public Set<BundleRequirement> getImports() {
+ Set<BundleRequirement> set = new LinkedHashSet<BundleRequirement>();
+ for (BundleRequirement requirement : requirements) {
+ if (requirement.getType().equals(PACKAGE_TYPE)) {
+ set.add(requirement);
+ }
+ }
+ return set;
+ }
+
+ public Set<ExportPackage> getExports() {
+ Set<ExportPackage> set = new LinkedHashSet<ExportPackage>();
+ for (BundleCapability capability : capabilities) {
+ if (PACKAGE_TYPE.equals(capability.getType())) {
+ set.add((ExportPackage) capability);
+ }
+ }
+ return set;
+ }
+
+ public Set<BundleCapability> getServices() {
+ Set<BundleCapability> set = new LinkedHashSet<BundleCapability>();
+ for (BundleCapability capability : capabilities) {
+ if (SERVICE_TYPE.equals(capability.getType())) {
+ set.add(capability);
+ }
+ }
+ return set;
+ }
+
+}
\ No newline at end of file
diff --git a/src/java/org/apache/ivy/osgi/core/BundleInfoAdapter.java b/src/java/org/apache/ivy/osgi/core/BundleInfoAdapter.java
new file mode 100644
index 0000000..136a32c
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/core/BundleInfoAdapter.java
@@ -0,0 +1,370 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.core;
+
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.jar.Manifest;
+
+import org.apache.ivy.Ivy;
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.Configuration;
+import org.apache.ivy.core.module.descriptor.Configuration.Visibility;
+import org.apache.ivy.core.module.descriptor.DefaultArtifact;
+import org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.DefaultExcludeRule;
+import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
+import org.apache.ivy.core.module.descriptor.ExtraInfoHolder;
+import org.apache.ivy.core.module.id.ArtifactId;
+import org.apache.ivy.core.module.id.ModuleId;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.osgi.util.Version;
+import org.apache.ivy.osgi.util.VersionRange;
+import org.apache.ivy.plugins.matcher.ExactOrRegexpPatternMatcher;
+import org.apache.ivy.plugins.matcher.PatternMatcher;
+import org.apache.ivy.plugins.parser.ModuleDescriptorParser;
+
+public class BundleInfoAdapter {
+
+ public static final String CONF_NAME_DEFAULT = "default";
+
+ public static final Configuration CONF_DEFAULT = new Configuration(CONF_NAME_DEFAULT);
+
+ public static final String CONF_NAME_OPTIONAL = "optional";
+
+ public static final Configuration CONF_OPTIONAL = new Configuration(CONF_NAME_OPTIONAL,
+ Visibility.PUBLIC, "Optional dependencies", new String[] {CONF_NAME_DEFAULT}, true,
+ null);
+
+ public static final String CONF_NAME_TRANSITIVE_OPTIONAL = "transitive-optional";
+
+ public static final Configuration CONF_TRANSITIVE_OPTIONAL = new Configuration(
+ CONF_NAME_TRANSITIVE_OPTIONAL, Visibility.PUBLIC, "Optional dependencies",
+ new String[] {CONF_NAME_OPTIONAL}, true, null);
+
+ public static final String CONF_USE_PREFIX = "use_";
+
+ public static final String EXTRA_INFO_EXPORT_PREFIX = "_osgi_export_";
+
+ public static DefaultModuleDescriptor toModuleDescriptor(ModuleDescriptorParser parser,
+ URI baseUri, BundleInfo bundle, ExecutionEnvironmentProfileProvider profileProvider) {
+ return toModuleDescriptor(parser, baseUri, bundle, null, profileProvider);
+ }
+
+ /**
+ *
+ * @param baseUri
+ * uri to help build the absolute url if the bundle info has a relative uri.
+ * @return
+ * @throws ProfileNotFoundException
+ */
+ public static DefaultModuleDescriptor toModuleDescriptor(ModuleDescriptorParser parser,
+ URI baseUri, BundleInfo bundle, Manifest manifest,
+ ExecutionEnvironmentProfileProvider profileProvider) throws ProfileNotFoundException {
+ DefaultModuleDescriptor md = new DefaultModuleDescriptor(parser, null);
+ md.addExtraAttributeNamespace("o", Ivy.getIvyHomeURL() + "osgi");
+ ModuleRevisionId mrid = asMrid(BundleInfo.BUNDLE_TYPE, bundle.getSymbolicName(),
+ bundle.getVersion());
+ md.setResolvedPublicationDate(new Date());
+ md.setModuleRevisionId(mrid);
+
+ md.addConfiguration(CONF_DEFAULT);
+ md.addConfiguration(CONF_OPTIONAL);
+ md.addConfiguration(CONF_TRANSITIVE_OPTIONAL);
+
+ Set<String> exportedPkgNames = new HashSet<String>(bundle.getExports().size());
+ for (ExportPackage exportPackage : bundle.getExports()) {
+ md.getExtraInfos().add(
+ new ExtraInfoHolder(EXTRA_INFO_EXPORT_PREFIX + exportPackage.getName(),
+ exportPackage.getVersion().toString()));
+ exportedPkgNames.add(exportPackage.getName());
+ String[] confDependencies = new String[exportPackage.getUses().size() + 1];
+ int i = 0;
+ for (String use : exportPackage.getUses()) {
+ confDependencies[i++] = CONF_USE_PREFIX + use;
+ }
+ confDependencies[i] = CONF_NAME_DEFAULT;
+ md.addConfiguration(new Configuration(CONF_USE_PREFIX + exportPackage.getName(),
+ Visibility.PUBLIC, "Exported package " + exportPackage.getName(),
+ confDependencies, true, null));
+ }
+
+ requirementAsDependency(md, bundle, exportedPkgNames);
+
+ if (baseUri != null) {
+ for (BundleArtifact bundleArtifact : bundle.getArtifacts()) {
+ String type = "jar";
+ String ext = "jar";
+ String packaging = null;
+ if (bundle.hasInnerClasspath() && !bundleArtifact.isSource()) {
+ packaging = "bundle";
+ }
+ if ("packed".equals(bundleArtifact.getFormat())) {
+ ext = "jar.pack.gz";
+ if (packaging != null) {
+ packaging += ",pack200";
+ } else {
+ packaging = "pack200";
+ }
+ }
+ if (bundleArtifact.isSource()) {
+ type = "source";
+ }
+ URI uri = bundleArtifact.getUri();
+ if (uri != null) {
+ DefaultArtifact artifact = buildArtifact(mrid, baseUri, uri, type, ext,
+ packaging);
+ md.addArtifact(CONF_NAME_DEFAULT, artifact);
+ }
+ }
+ }
+
+ if (profileProvider != null) {
+ for (String env : bundle.getExecutionEnvironments()) {
+ ExecutionEnvironmentProfile profile = profileProvider.getProfile(env);
+ if (profile == null) {
+ throw new ProfileNotFoundException("Execution environment profile " + env
+ + " not found");
+ }
+ for (String pkg : profile.getPkgNames()) {
+ ArtifactId id = new ArtifactId(ModuleId.newInstance(BundleInfo.PACKAGE_TYPE,
+ pkg), PatternMatcher.ANY_EXPRESSION, PatternMatcher.ANY_EXPRESSION,
+ PatternMatcher.ANY_EXPRESSION);
+ DefaultExcludeRule rule = new DefaultExcludeRule(id,
+ ExactOrRegexpPatternMatcher.INSTANCE, null);
+ String[] confs = md.getConfigurationsNames();
+ for (int i = 0; i < confs.length; i++) {
+ rule.addConfiguration(confs[i]);
+ }
+ md.addExcludeRule(rule);
+ }
+ }
+ }
+
+ if (manifest != null) {
+ for (Entry<Object, Object> entries : manifest.getMainAttributes().entrySet()) {
+ md.addExtraInfo(new ExtraInfoHolder(entries.getKey().toString(), entries.getValue()
+ .toString()));
+ }
+ }
+
+ return md;
+ }
+
+ public static DefaultArtifact buildArtifact(ModuleRevisionId mrid, URI baseUri, URI uri,
+ String type, String ext, String packaging) {
+ DefaultArtifact artifact;
+ if ("ivy".equals(uri.getScheme())) {
+ artifact = decodeIvyURI(uri);
+ } else {
+ if (!uri.isAbsolute()) {
+ uri = baseUri.resolve(uri);
+ }
+ Map<String, String> extraAtt = new HashMap<String, String>();
+ if (packaging != null) {
+ extraAtt.put("packaging", packaging);
+ }
+ try {
+ artifact = new DefaultArtifact(mrid, null, mrid.getName(), type, ext, new URL(
+ uri.toString()), extraAtt);
+ } catch (MalformedURLException e) {
+ throw new RuntimeException("Unable to make the uri into the url", e);
+ }
+ }
+ return artifact;
+ }
+
+ public static List<String> getConfigurations(BundleInfo bundle) {
+ List<String> confs = new ArrayList<String>();
+ confs.add(CONF_NAME_DEFAULT);
+ confs.add(CONF_NAME_OPTIONAL);
+ confs.add(CONF_NAME_TRANSITIVE_OPTIONAL);
+
+ for (ExportPackage exportPackage : bundle.getExports()) {
+ confs.add(CONF_USE_PREFIX + exportPackage.getName());
+ }
+
+ return confs;
+ }
+
+ public static URI buildIvyURI(Artifact artifact) {
+ ModuleRevisionId mrid = artifact.getModuleRevisionId();
+ return asIvyURI(mrid.getOrganisation(), mrid.getName(), mrid.getBranch(),
+ mrid.getRevision(), artifact.getType(), artifact.getName(), artifact.getExt());
+ }
+
+ private static URI asIvyURI(String org, String name, String branch, String rev, String type,
+ String art, String ext) {
+ StringBuffer builder = new StringBuffer();
+ builder.append("ivy:///");
+ builder.append(org);
+ builder.append('/');
+ builder.append(name);
+ builder.append('?');
+ if (branch != null) {
+ builder.append("branch=");
+ builder.append(branch);
+ }
+ if (rev != null) {
+ builder.append("&rev=");
+ builder.append(rev);
+ }
+ if (type != null) {
+ builder.append("&type=");
+ builder.append(type);
+ }
+ if (art != null) {
+ builder.append("&art=");
+ builder.append(art);
+ }
+ if (ext != null) {
+ builder.append("&ext=");
+ builder.append(ext);
+ }
+ try {
+ return new URI(builder.toString());
+ } catch (URISyntaxException e) {
+ throw new RuntimeException("illformed ivy url", e);
+ }
+ }
+
+ private static DefaultArtifact decodeIvyURI(final URI uri) {
+ String org = null;
+ String name = null;
+ String branch = null;
+ String rev = null;
+ String art = null;
+ String type = null;
+ String ext = null;
+
+ String path = uri.getPath();
+ if (!path.startsWith("/")) {
+ throw new IllegalArgumentException(
+ "An ivy url should be of the form ivy:///org/module but was : " + uri);
+ }
+ int i = path.indexOf('/', 1);
+ if (i < 0) {
+ throw new IllegalArgumentException("Expecting an organisation in the ivy url: " + uri);
+ }
+ org = path.substring(1, i);
+ name = path.substring(i + 1);
+
+ String query = uri.getQuery();
+ String[] parameters = query.split("&");
+ for (int j = 0; j < parameters.length; j++) {
+ String parameter = parameters[j];
+ if (parameter.length() == 0) {
+ continue;
+ }
+ String[] nameAndValue = parameter.split("=");
+ if (nameAndValue.length != 2) {
+ throw new IllegalArgumentException("Malformed query string in the ivy url: " + uri);
+ } else if (nameAndValue[0].equals("branch")) {
+ branch = nameAndValue[1];
+ } else if (nameAndValue[0].equals("rev")) {
+ rev = nameAndValue[1];
+ } else if (nameAndValue[0].equals("art")) {
+ art = nameAndValue[1];
+ } else if (nameAndValue[0].equals("type")) {
+ type = nameAndValue[1];
+ } else if (nameAndValue[0].equals("ext")) {
+ ext = nameAndValue[1];
+ } else {
+ throw new IllegalArgumentException("Unrecognized parameter '" + nameAndValue[0]
+ + " in the query string of the ivy url: " + uri);
+ }
+ }
+
+ ModuleRevisionId amrid = ModuleRevisionId.newInstance(org, name, branch, rev);
+ DefaultArtifact artifact = new DefaultArtifact(amrid, null, art, type, ext);
+ return artifact;
+ }
+
+ private static void requirementAsDependency(DefaultModuleDescriptor md, BundleInfo bundleInfo,
+ Set<String> exportedPkgNames) {
+ for (BundleRequirement requirement : bundleInfo.getRequirements()) {
+ String type = requirement.getType();
+ String name = requirement.getName();
+
+ if (BundleInfo.PACKAGE_TYPE.equals(type) && exportedPkgNames.contains(name)) {
+ // don't declare package exported by the current bundle
+ continue;
+ }
+
+ if (BundleInfo.EXECUTION_ENVIRONMENT_TYPE.equals(type)) {
+ // execution environment are handled elsewhere
+ continue;
+ }
+
+ ModuleRevisionId ddmrid = asMrid(type, name, requirement.getVersion());
+ DefaultDependencyDescriptor dd = new DefaultDependencyDescriptor(ddmrid, false);
+
+ String conf = CONF_NAME_DEFAULT;
+ if (BundleInfo.PACKAGE_TYPE.equals(type)) {
+ // declare the configuration for the package
+ conf = CONF_USE_PREFIX + name;
+ md.addConfiguration(new Configuration(CONF_USE_PREFIX + name, Visibility.PUBLIC,
+ "Exported package " + name, new String[] {CONF_NAME_DEFAULT}, true, null));
+ dd.addDependencyConfiguration(conf, conf);
+ }
+
+ if ("optional".equals(requirement.getResolution())) {
+ dd.addDependencyConfiguration(CONF_NAME_OPTIONAL, conf);
+ dd.addDependencyConfiguration(CONF_NAME_TRANSITIVE_OPTIONAL,
+ CONF_NAME_TRANSITIVE_OPTIONAL);
+ } else {
+ dd.addDependencyConfiguration(CONF_NAME_DEFAULT, conf);
+ }
+
+ md.addDependency(dd);
+ }
+
+ }
+
+ public static ModuleRevisionId asMrid(String type, String name, Version v) {
+ return ModuleRevisionId.newInstance(type, name, v == null ? null : v.toString());
+ }
+
+ public static ModuleRevisionId asMrid(String type, String name, VersionRange v) {
+ String revision;
+ if (v == null) {
+ revision = "[0,)";
+ } else {
+ revision = v.toIvyRevision();
+ }
+ return ModuleRevisionId.newInstance(type, name, revision);
+ }
+
+ public static class ProfileNotFoundException extends RuntimeException {
+
+ public ProfileNotFoundException(String msg) {
+ super(msg);
+ }
+
+ }
+}
diff --git a/src/java/org/apache/ivy/osgi/core/BundleRequirement.java b/src/java/org/apache/ivy/osgi/core/BundleRequirement.java
new file mode 100644
index 0000000..92b5dd3
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/core/BundleRequirement.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.core;
+
+import org.apache.ivy.osgi.util.VersionRange;
+
+public class BundleRequirement {
+
+ private final String name;
+
+ private final String resolution;
+
+ private final VersionRange version;
+
+ private final String type;
+
+ public BundleRequirement(String type, String name, VersionRange version, String resolution) {
+ this.type = type;
+ this.name = name;
+ this.version = version;
+ this.resolution = resolution;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public VersionRange getVersion() {
+ return version;
+ }
+
+ public String getResolution() {
+ return resolution;
+ }
+
+ public String toString() {
+ return name + (version == null ? "" : ";" + version)
+ + (resolution == null ? "" : " (" + resolution + ")");
+ }
+
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((type == null) ? 0 : type.hashCode());
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ result = prime * result + ((resolution == null) ? 0 : resolution.hashCode());
+ result = prime * result + ((version == null) ? 0 : version.hashCode());
+ return result;
+ }
+
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (!(obj instanceof BundleRequirement)) {
+ return false;
+ }
+ BundleRequirement other = (BundleRequirement) obj;
+ if (type == null) {
+ if (other.type != null) {
+ return false;
+ }
+ } else if (!type.equals(other.type)) {
+ return false;
+ }
+ if (name == null) {
+ if (other.name != null) {
+ return false;
+ }
+ } else if (!name.equals(other.name)) {
+ return false;
+ }
+ if (resolution == null) {
+ if (other.resolution != null) {
+ return false;
+ }
+ } else if (!resolution.equals(other.resolution)) {
+ return false;
+ }
+ if (version == null) {
+ if (other.version != null) {
+ return false;
+ }
+ } else if (!version.equals(other.version)) {
+ return false;
+ }
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/src/java/org/apache/ivy/osgi/core/ExecutionEnvironmentProfile.java b/src/java/org/apache/ivy/osgi/core/ExecutionEnvironmentProfile.java
new file mode 100644
index 0000000..8c079bb
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/core/ExecutionEnvironmentProfile.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.core;
+
+import java.util.Set;
+import java.util.TreeSet;
+
+public class ExecutionEnvironmentProfile {
+
+ Set<String> pkgNames = new TreeSet<String>();
+
+ private final String name;
+
+ public ExecutionEnvironmentProfile(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Set<String> getPkgNames() {
+ return pkgNames;
+ }
+
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((name == null) ? 0 : name.hashCode());
+ result = prime * result + ((pkgNames == null) ? 0 : pkgNames.hashCode());
+ return result;
+ }
+
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (!(obj instanceof ExecutionEnvironmentProfile)) {
+ return false;
+ }
+ ExecutionEnvironmentProfile other = (ExecutionEnvironmentProfile) obj;
+ if (name == null) {
+ if (other.name != null) {
+ return false;
+ }
+ } else if (!name.equals(other.name)) {
+ return false;
+ }
+ if (pkgNames == null) {
+ if (other.pkgNames != null) {
+ return false;
+ }
+ } else if (!pkgNames.equals(other.pkgNames)) {
+ return false;
+ }
+ return true;
+ }
+
+ public String toString() {
+ return name + ":" + pkgNames;
+ }
+}
diff --git a/src/java/org/apache/ivy/osgi/core/ExecutionEnvironmentProfileProvider.java b/src/java/org/apache/ivy/osgi/core/ExecutionEnvironmentProfileProvider.java
new file mode 100644
index 0000000..45f3bc1
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/core/ExecutionEnvironmentProfileProvider.java
@@ -0,0 +1,135 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.core;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Properties;
+
+import org.apache.ivy.util.Message;
+
+public class ExecutionEnvironmentProfileProvider {
+
+ private static final String DEFAULT_PROFILES_FILE = "jvm-packages.properties";
+
+ private static final String PACKAGE_PREFIX = "org/apache/ivy/osgi/core/";
+
+ private Map<String, ExecutionEnvironmentProfile> profileList;
+
+ private static final ExecutionEnvironmentProfileProvider INSTANCE;
+
+ static {
+ try {
+ INSTANCE = new ExecutionEnvironmentProfileProvider();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static ExecutionEnvironmentProfileProvider getInstance() {
+ return INSTANCE;
+ }
+
+ public ExecutionEnvironmentProfileProvider() throws IOException {
+ profileList = loadDefaultProfileList();
+ }
+
+ public ExecutionEnvironmentProfile getProfile(String profile) {
+ return profileList.get(profile);
+ }
+
+ public static Map<String, ExecutionEnvironmentProfile> loadDefaultProfileList()
+ throws IOException {
+ ClassLoader loader = ExecutionEnvironmentProfileProvider.class.getClassLoader();
+ InputStream defaultProfilesFile = loader.getResourceAsStream(PACKAGE_PREFIX
+ + DEFAULT_PROFILES_FILE);
+ if (defaultProfilesFile == null) {
+ throw new FileNotFoundException(PACKAGE_PREFIX + DEFAULT_PROFILES_FILE
+ + " not found in the classpath");
+ }
+ Properties props = new Properties();
+ try {
+ props.load(defaultProfilesFile);
+ } finally {
+ defaultProfilesFile.close();
+ }
+ Map<String, ExecutionEnvironmentProfile> profiles = new HashMap<String, ExecutionEnvironmentProfile>();
+ for (Entry<Object, Object> prop : props.entrySet()) {
+ String propName = (String) prop.getKey();
+ if (propName.endsWith(".pkglist")) {
+ String profileName = propName.substring(0, propName.length() - 8);
+ if (!profiles.containsKey(profileName)) {
+ loadProfile(props, profiles, profileName);
+ }
+ }
+ }
+ return profiles;
+ }
+
+ private static ExecutionEnvironmentProfile loadProfile(Properties props,
+ Map<String, ExecutionEnvironmentProfile> profiles, String name) {
+
+ ExecutionEnvironmentProfile profile = new ExecutionEnvironmentProfile(name);
+
+ // load the package for the extended profile
+ String extendedProfileName = props.getProperty(name + ".extends");
+ if (extendedProfileName != null) {
+ ExecutionEnvironmentProfile extendedProfile = profiles.get(extendedProfileName);
+ if (extendedProfile == null) {
+ // not loaded yet, so load it now
+ extendedProfile = loadProfile(props, profiles, extendedProfileName);
+ }
+ profile.pkgNames.addAll(extendedProfile.pkgNames);
+ }
+
+ // load the actual list
+ String pkgList = props.getProperty(name + ".pkglist");
+ String[] packages = pkgList.split(",");
+ for (int i = 0; i < packages.length; i++) {
+ String pkg = packages[i].trim();
+ if (pkg.length() != 0) {
+ profile.pkgNames.add(pkg);
+ }
+ }
+
+ profiles.put(name, profile);
+
+ String aliasList = props.getProperty(name + ".aliases");
+ if (aliasList != null) {
+ String[] aliases = aliasList.split(",");
+ for (int i = 0; i < aliases.length; i++) {
+ String alias = aliases[i].trim();
+ if (alias.length() != 0) {
+ ExecutionEnvironmentProfile profileAlias = new ExecutionEnvironmentProfile(
+ alias);
+ profileAlias.pkgNames = profile.pkgNames;
+ profiles.put(alias, profileAlias);
+ }
+ }
+ }
+
+ Message.verbose("Execution environment profile " + profile.getName() + " loaded");
+
+ return profile;
+ }
+
+}
diff --git a/src/java/org/apache/ivy/osgi/core/ExportPackage.java b/src/java/org/apache/ivy/osgi/core/ExportPackage.java
new file mode 100644
index 0000000..25e0334
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/core/ExportPackage.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.core;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.apache.ivy.osgi.util.Version;
+
+public class ExportPackage extends BundleCapability {
+
+ private final Set<String> uses = new HashSet<String>();
+
+ public ExportPackage(String name, Version version) {
+ super(BundleInfo.PACKAGE_TYPE, name, version);
+ }
+
+ public void addUse(String pkg) {
+ uses.add(pkg);
+ }
+
+ public Version getVersion() {
+ return super.getVersion() == null ? BundleInfo.DEFAULT_VERSION : super.getVersion();
+ }
+
+ public Set<String> getUses() {
+ return uses;
+ }
+
+ public int hashCode() {
+ final int prime = 31;
+ int result = super.hashCode();
+ result = prime * result + ((uses == null) ? 0 : uses.hashCode());
+ return result;
+ }
+
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!super.equals(obj)) {
+ return false;
+ }
+ ExportPackage other = (ExportPackage) obj;
+ if (uses == null) {
+ if (other.uses != null) {
+ return false;
+ }
+ } else if (!uses.equals(other.uses)) {
+ return false;
+ }
+ return true;
+ }
+
+}
\ No newline at end of file
diff --git a/src/java/org/apache/ivy/osgi/core/ManifestHeaderElement.java b/src/java/org/apache/ivy/osgi/core/ManifestHeaderElement.java
new file mode 100644
index 0000000..7aa2176
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/core/ManifestHeaderElement.java
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.core;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+public class ManifestHeaderElement {
+ private List<String> values = new ArrayList<String>();
+
+ private Map<String, String> attributes = new HashMap<String, String>();
+
+ private Map<String, String> directives = new HashMap<String, String>();
+
+ public List<String> getValues() {
+ return values;
+ }
+
+ public void addValue(String value) {
+ values.add(value);
+ }
+
+ public Map<String, String> getAttributes() {
+ return attributes;
+ }
+
+ public void addAttribute(String name, String value) {
+ attributes.put(name, value);
+ }
+
+ public Map<String, String> getDirectives() {
+ return directives;
+ }
+
+ public void addDirective(String name, String value) {
+ directives.put(name, value);
+ }
+
+ public boolean equals(Object obj) {
+ if (!(obj instanceof ManifestHeaderElement)) {
+ return false;
+ }
+ ManifestHeaderElement other = (ManifestHeaderElement) obj;
+ if (other.values.size() != values.size()) {
+ return false;
+ }
+ for (String value : values) {
+ if (!other.values.contains(value)) {
+ return false;
+ }
+ }
+ if (other.directives.size() != directives.size()) {
+ return false;
+ }
+ for (Entry<String, String> directive : directives.entrySet()) {
+ if (!directive.getValue().equals(other.directives.get(directive.getKey()))) {
+ return false;
+ }
+ }
+ if (other.attributes.size() != attributes.size()) {
+ return false;
+ }
+ for (Entry<String, String> attribute : attributes.entrySet()) {
+ if (!attribute.getValue().equals(other.attributes.get(attribute.getKey()))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public String toString() {
+ String string = "";
+ Iterator<String> itValues = values.iterator();
+ while (itValues.hasNext()) {
+ string = string.concat(itValues.next());
+ if (itValues.hasNext()) {
+ string = string.concat(";");
+ }
+ }
+ for (Entry<String, String> directive : directives.entrySet()) {
+ string = string.concat(";");
+ string = string.concat(directive.getKey());
+ string = string.concat(":=");
+ string = string.concat(directive.getValue());
+ }
+ for (Entry<String, String> attribute : attributes.entrySet()) {
+ string = string.concat(";");
+ string = string.concat(attribute.getKey());
+ string = string.concat("=");
+ string = string.concat(attribute.getValue());
+ }
+ return string;
+ }
+}
diff --git a/src/java/org/apache/ivy/osgi/core/ManifestHeaderValue.java b/src/java/org/apache/ivy/osgi/core/ManifestHeaderValue.java
new file mode 100644
index 0000000..d793975
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/core/ManifestHeaderValue.java
@@ -0,0 +1,416 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.core;
+
+import java.io.PrintStream;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Parse a header of a manifest. The manifest header is composed with the following rules:
+ *
+ * <pre>
+ * header ::= header-element (',' header-element)*
+ * header-element ::= values (';' (attribute | directive) )*
+ * values ::= value (';' value)*
+ * value ::= <any string value that does not have ';' or ','>
+ * attribute ::= key '=' value
+ * directive ::= key '=' value
+ * key ::= token
+ * value ::= token | quoted-string | double-quoted-string
+ * </pre>
+ */
+public class ManifestHeaderValue {
+
+ private List<ManifestHeaderElement> elements = new ArrayList<ManifestHeaderElement>();
+
+ ManifestHeaderValue() {
+ // just for unit testing
+ }
+
+ public ManifestHeaderValue(String header) throws ParseException {
+ if (header != null) {
+ new ManifestHeaderParser(header).parse();
+ }
+ }
+
+ public List<ManifestHeaderElement> getElements() {
+ return elements;
+ }
+
+ public String getSingleValue() {
+ if (elements.isEmpty()) {
+ return null;
+ }
+ List<String> values = getElements().iterator().next().getValues();
+ if (values.isEmpty()) {
+ return null;
+ }
+ return values.iterator().next();
+ }
+
+ public List<String> getValues() {
+ if (elements.isEmpty()) {
+ return Collections.emptyList();
+ }
+ List<String> list = new ArrayList<String>();
+ for (ManifestHeaderElement element : getElements()) {
+ list.addAll(element.getValues());
+ }
+ return list;
+ }
+
+ void addElement(ManifestHeaderElement element) {
+ this.elements.add(element);
+ }
+
+ class ManifestHeaderParser {
+
+ /**
+ * header to parse
+ */
+ private final String header;
+
+ /**
+ * the length of the source
+ */
+ private int length;
+
+ /**
+ * buffer
+ */
+ private StringBuffer buffer = new StringBuffer();
+
+ /**
+ * position in the source
+ */
+ private int pos = 0;
+
+ /**
+ * last read character
+ */
+ private char c;
+
+ /**
+ * the header element being build
+ */
+ private ManifestHeaderElement elem = new ManifestHeaderElement();
+
+ /**
+ * Once at true (at the first attribute parsed), only parameters are allowed
+ */
+ private boolean valuesParsed;
+
+ /**
+ * the last parsed parameter name
+ */
+ private String paramName;
+
+ /**
+ * true if the last parsed parameter is a directive (assigned via :=)
+ */
+ private boolean isDirective;
+
+ /**
+ * Default constructor
+ *
+ * @param header
+ * the header to parse
+ */
+ ManifestHeaderParser(String header) {
+ this.header = header;
+ this.length = header.length();
+ }
+
+ /**
+ * Do the parsing
+ *
+ * @throws ParseException
+ */
+ void parse() throws ParseException {
+ do {
+ elem = new ManifestHeaderElement();
+ int posElement = pos;
+ parseElement();
+ if (elem.getValues().isEmpty()) {
+ error("No defined value", posElement);
+ // try to recover: ignore that element
+ continue;
+ }
+ addElement(elem);
+ } while (pos < length);
+ }
+
+ private char readNext() {
+ if (pos == length) {
+ c = '\0';
+ } else {
+ c = header.charAt(pos++);
+ }
+ return c;
+ }
+
+ private void error(String message) throws ParseException {
+ error(message, pos - 1);
+ }
+
+ private void error(String message, int p) throws ParseException {
+ throw new ParseException(message, p);
+ }
+
+ private void parseElement() throws ParseException {
+ valuesParsed = false;
+ do {
+ parseValueOrParameter();
+ } while (c == ';' && pos < length);
+ }
+
+ private void parseValueOrParameter() throws ParseException {
+ // true if the value/parameter parsing has started, white spaces skipped
+ boolean start = false;
+ do {
+ switch (readNext()) {
+ case '\0':
+ break;
+ case ';':
+ case ',':
+ endValue();
+ return;
+ case ':':
+ case '=':
+ endParameterName();
+ parseSeparator();
+ parseParameterValue();
+ return;
+ case ' ':
+ case '\t':
+ case '\n':
+ case '\r':
+ if (start) {
+ buffer.append(c);
+ }
+ break;
+ default:
+ start = true;
+ buffer.append(c);
+ }
+ } while (pos < length);
+ endValue();
+ }
+
+ private void endValue() throws ParseException {
+ if (valuesParsed) {
+ error("Early end of a parameter");
+ // try to recover: ignore it
+ buffer.setLength(0);
+ return;
+ }
+ if (buffer.length() == 0) {
+ error("Empty value");
+ // try to recover: just ignore the error
+ }
+ elem.addValue(buffer.toString());
+ buffer.setLength(0);
+ }
+
+ private void endParameterName() throws ParseException {
+ if (buffer.length() == 0) {
+ error("Empty parameter name");
+ // try to recover: won't store the value
+ paramName = null;
+ }
+ paramName = buffer.toString();
+ buffer.setLength(0);
+ }
+
+ private void parseSeparator() throws ParseException {
+ if (c == '=') {
+ isDirective = false;
+ return;
+ }
+ if (readNext() != '=') {
+ error("Expecting '='");
+ // try to recover: will ignore this parameter
+ pos--;
+ paramName = null;
+ }
+ isDirective = true;
+ }
+
+ private void parseParameterValue() throws ParseException {
+ // true if the value parsing has started, white spaces skipped
+ boolean start = false;
+ // true if the value parsing is ended, then only white spaces are allowed
+ boolean end = false;
+ boolean doubleQuoted = false;
+ do {
+ switch (readNext()) {
+ case '\0':
+ break;
+
+ case ',':
+ case ';':
+ endParameterValue();
+ return;
+ case '=':
+ case ':':
+ error("Illegal character '" + c + "' in parameter value of " + paramName);
+ // try to recover: ignore that parameter
+ paramName = null;
+ break;
+ case '\"':
+ doubleQuoted = true;
+ case '\'':
+ if (end && paramName != null) {
+ error("Expecting the end of a parameter value");
+ // try to recover: ignore that parameter
+ paramName = null;
+ }
+ if (start) {
+ // quote in the middle of the value, just add it as a quote
+ buffer.append(c);
+ } else {
+ start = true;
+ appendQuoted(doubleQuoted);
+ end = true;
+ }
+ break;
+ case '\\':
+ if (end && paramName != null) {
+ error("Expecting the end of a parameter value");
+ // try to recover: ignore that parameter
+ paramName = null;
+ }
+ start = true;
+ appendEscaped();
+ break;
+ case ' ':
+ case '\t':
+ case '\n':
+ case '\r':
+ if (start) {
+ end = true;
+ }
+ break;
+ default:
+ if (end && paramName != null) {
+ error("Expecting the end of a parameter value");
+ // try to recover: ignore that parameter
+ paramName = null;
+ }
+ start = true;
+ buffer.append(c);
+ }
+ } while (pos < length);
+ endParameterValue();
+ }
+
+ private void endParameterValue() throws ParseException {
+ if (paramName == null) {
+ // recovering from an incorrect parameter: skip the value
+ return;
+ }
+ if (buffer.length() == 0) {
+ error("Empty parameter value");
+ // try to recover: do not store the parameter
+ return;
+ }
+ String value = buffer.toString();
+ if (isDirective) {
+ elem.addDirective(paramName, value);
+ } else {
+ elem.addAttribute(paramName, value);
+ }
+ valuesParsed = true;
+ buffer.setLength(0);
+ }
+
+ private void appendQuoted(boolean doubleQuoted) {
+ do {
+ switch (readNext()) {
+ case '\0':
+ break;
+ case '\"':
+ if (doubleQuoted) {
+ return;
+ }
+ buffer.append(c);
+ break;
+ case '\'':
+ if (!doubleQuoted) {
+ return;
+ }
+ buffer.append(c);
+ break;
+ case '\\':
+ break;
+ default:
+ buffer.append(c);
+ }
+ } while (pos < length);
+ }
+
+ private void appendEscaped() {
+ if (pos < length) {
+ buffer.append(readNext());
+ } else {
+ buffer.append(c);
+ }
+ }
+ }
+
+ public boolean equals(Object obj) {
+ if (!(obj instanceof ManifestHeaderValue)) {
+ return false;
+ }
+ ManifestHeaderValue other = (ManifestHeaderValue) obj;
+ if (other.elements.size() != elements.size()) {
+ return false;
+ }
+ for (ManifestHeaderElement element : elements) {
+ if (!other.elements.contains(element)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public String toString() {
+ String string = "";
+ Iterator<ManifestHeaderElement> it = elements.iterator();
+ while (it.hasNext()) {
+ string = string.concat(it.next().toString());
+ if (it.hasNext()) {
+ string = string.concat(",");
+ }
+ }
+ return string;
+ }
+
+ public static void writeParseException(PrintStream out, String source, ParseException e) {
+ out.println(e.getMessage());
+ out.print(" " + source + "\n ");
+ for (int i = 0; i < e.getErrorOffset(); i++) {
+ out.print(' ');
+ }
+ out.println('^');
+ }
+}
diff --git a/src/java/org/apache/ivy/osgi/core/ManifestParser.java b/src/java/org/apache/ivy/osgi/core/ManifestParser.java
new file mode 100644
index 0000000..2aec72f
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/core/ManifestParser.java
@@ -0,0 +1,302 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.core;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.text.ParseException;
+import java.util.List;
+import java.util.jar.Attributes;
+import java.util.jar.JarInputStream;
+import java.util.jar.Manifest;
+
+import org.apache.ivy.osgi.util.Version;
+import org.apache.ivy.osgi.util.VersionRange;
+
+/**
+ * Provides an OSGi manifest parser.
+ *
+ */
+public class ManifestParser {
+
+ private static final String EXPORT_PACKAGE = "Export-Package";
+
+ private static final String IMPORT_PACKAGE = "Import-Package";
+
+ private static final String EXPORT_SERVICE = "Export-Service";
+
+ private static final String IMPORT_SERVICE = "Import-Service";
+
+ private static final String REQUIRE_BUNDLE = "Require-Bundle";
+
+ private static final String BUNDLE_VERSION = "Bundle-Version";
+
+ private static final String BUNDLE_NAME = "Bundle-Name";
+
+ private static final String BUNDLE_DESCRIPTION = "Bundle-Description";
+
+ private static final String BUNDLE_SYMBOLIC_NAME = "Bundle-SymbolicName";
+
+ private static final String BUNDLE_MANIFEST_VERSION = "Bundle-ManifestVersion";
+
+ private static final String BUNDLE_REQUIRED_EXECUTION_ENVIRONMENT = "Bundle-RequiredExecutionEnvironment";
+
+ private static final String BUNDLE_CLASSPATH = "Bundle-ClassPath";
+
+ private static final String ECLIPSE_SOURCE_BUNDLE = "Eclipse-SourceBundle";
+
+ private static final String ATTR_RESOLUTION = "resolution";
+
+ private static final String ATTR_VERSION = "version";
+
+ private static final String ATTR_BUNDLE_VERSION = "bundle-version";
+
+ private static final String ATTR_USE = "use";
+
+ public static BundleInfo parseJarManifest(InputStream jarStream) throws IOException,
+ ParseException {
+ JarInputStream jis = new JarInputStream(jarStream);
+ Manifest manifest = jis.getManifest();
+ if (manifest == null) {
+ return null;
+ }
+ BundleInfo bundleInfo = parseManifest(manifest);
+ return bundleInfo;
+ }
+
+ public static BundleInfo parseManifest(File manifestFile) throws IOException, ParseException {
+ FileInputStream fis = new FileInputStream(manifestFile);
+ try {
+ BundleInfo parseManifest = parseManifest(fis);
+ return parseManifest;
+ } finally {
+ try {
+ fis.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ }
+
+ public static BundleInfo parseManifest(String manifest) throws IOException, ParseException {
+ ByteArrayInputStream bais = new ByteArrayInputStream(manifest.getBytes("UTF-8"));
+ BundleInfo parseManifest = parseManifest(bais);
+ bais.close();
+ return parseManifest;
+ }
+
+ public static BundleInfo parseManifest(InputStream manifestStream) throws IOException,
+ ParseException {
+ BundleInfo parseManifest = parseManifest(new Manifest(manifestStream));
+ return parseManifest;
+ }
+
+ public static BundleInfo parseManifest(Manifest manifest) throws ParseException {
+ Attributes mainAttributes = manifest.getMainAttributes();
+
+ // Eclipse source bundle doesn't have it. Disable it until proven actually useful
+ // String manifestVersion = mainAttributes.getValue(BUNDLE_MANIFEST_VERSION);
+ // if (manifestVersion == null) {
+ // // non OSGi manifest
+ // throw new ParseException("No " + BUNDLE_MANIFEST_VERSION + " in the manifest", 0);
+ // }
+
+ String symbolicName = new ManifestHeaderValue(mainAttributes.getValue(BUNDLE_SYMBOLIC_NAME))
+ .getSingleValue();
+ if (symbolicName == null) {
+ throw new ParseException("No " + BUNDLE_SYMBOLIC_NAME + " in the manifest", 0);
+ }
+
+ String description = new ManifestHeaderValue(mainAttributes.getValue(BUNDLE_DESCRIPTION))
+ .getSingleValue();
+ if (description == null) {
+ description = new ManifestHeaderValue(mainAttributes.getValue(BUNDLE_DESCRIPTION))
+ .getSingleValue();
+ }
+
+ String vBundle = new ManifestHeaderValue(mainAttributes.getValue(BUNDLE_VERSION))
+ .getSingleValue();
+ Version version;
+ try {
+ version = versionOf(vBundle);
+ } catch (NumberFormatException e) {
+ throw new ParseException("The " + BUNDLE_VERSION + " has an incorrect version: "
+ + vBundle + " (" + e.getMessage() + ")", 0);
+ }
+
+ BundleInfo bundleInfo = new BundleInfo(symbolicName, version);
+
+ bundleInfo.setDescription(description);
+
+ List<String> environments = new ManifestHeaderValue(
+ mainAttributes.getValue(BUNDLE_REQUIRED_EXECUTION_ENVIRONMENT)).getValues();
+ bundleInfo.setExecutionEnvironments(environments);
+
+ parseRequirement(bundleInfo, mainAttributes, REQUIRE_BUNDLE, BundleInfo.BUNDLE_TYPE,
+ ATTR_BUNDLE_VERSION);
+ parseRequirement(bundleInfo, mainAttributes, IMPORT_PACKAGE, BundleInfo.PACKAGE_TYPE,
+ ATTR_VERSION);
+ parseRequirement(bundleInfo, mainAttributes, IMPORT_SERVICE, BundleInfo.SERVICE_TYPE,
+ ATTR_VERSION);
+
+ ManifestHeaderValue exportElements = new ManifestHeaderValue(
+ mainAttributes.getValue(EXPORT_PACKAGE));
+ for (ManifestHeaderElement exportElement : exportElements.getElements()) {
+ String vExport = exportElement.getAttributes().get(ATTR_VERSION);
+ Version v = null;
+ try {
+ v = versionOf(vExport);
+ } catch (NumberFormatException e) {
+ throw new ParseException("The " + EXPORT_PACKAGE + " has an incorrect version: "
+ + vExport + " (" + e.getMessage() + ")", 0);
+ }
+
+ for (String name : exportElement.getValues()) {
+ ExportPackage export = new ExportPackage(name, v);
+ String uses = exportElement.getDirectives().get(ATTR_USE);
+ if (uses != null) {
+ String[] split = uses.trim().split(",");
+ for (int i = 0; i < split.length; i++) {
+ export.addUse(split[i].trim());
+ }
+ }
+ bundleInfo.addCapability(export);
+ }
+ }
+
+ parseCapability(bundleInfo, mainAttributes, EXPORT_SERVICE, BundleInfo.SERVICE_TYPE);
+
+ // handle Eclipse specific source attachement
+ String eclipseSourceBundle = mainAttributes.getValue(ECLIPSE_SOURCE_BUNDLE);
+ if (eclipseSourceBundle != null) {
+ bundleInfo.setSource(true);
+ ManifestHeaderValue eclipseSourceBundleValue = new ManifestHeaderValue(
+ eclipseSourceBundle);
+ ManifestHeaderElement element = eclipseSourceBundleValue.getElements().iterator()
+ .next();
+ String symbolicNameTarget = element.getValues().iterator().next();
+ bundleInfo.setSymbolicNameTarget(symbolicNameTarget);
+ String v = element.getAttributes().get(ATTR_VERSION);
+ if (v != null) {
+ bundleInfo.setVersionTarget(new Version(v));
+ }
+ }
+
+ String bundleClasspath = mainAttributes.getValue(BUNDLE_CLASSPATH);
+ if (bundleClasspath != null) {
+ ManifestHeaderValue bundleClasspathValue = new ManifestHeaderValue(bundleClasspath);
+ bundleInfo.setClasspath(bundleClasspathValue.getValues());
+ bundleInfo.setHasInnerClasspath(true);
+ }
+
+ return bundleInfo;
+ }
+
+ private static void parseRequirement(BundleInfo bundleInfo, Attributes mainAttributes,
+ String headerName, String type, String versionAttr) throws ParseException {
+ ManifestHeaderValue elements = new ManifestHeaderValue(mainAttributes.getValue(headerName));
+ for (ManifestHeaderElement element : elements.getElements()) {
+ String resolution = element.getDirectives().get(ATTR_RESOLUTION);
+ String attVersion = element.getAttributes().get(versionAttr);
+ VersionRange version = null;
+ try {
+ version = versionRangeOf(attVersion);
+ } catch (ParseException e) {
+ throw new ParseException("The " + headerName + " has an incorrect version: "
+ + attVersion + " (" + e.getMessage() + ")", 0);
+ }
+
+ for (String name : element.getValues()) {
+ bundleInfo.addRequirement(new BundleRequirement(type, name, version, resolution));
+ }
+ }
+ }
+
+ private static void parseCapability(BundleInfo bundleInfo, Attributes mainAttributes,
+ String headerName, String type) throws ParseException {
+ ManifestHeaderValue elements = new ManifestHeaderValue(mainAttributes.getValue(headerName));
+ for (ManifestHeaderElement element : elements.getElements()) {
+ String attVersion = element.getAttributes().get(ATTR_VERSION);
+ Version version = null;
+ try {
+ version = versionOf(attVersion);
+ } catch (NumberFormatException e) {
+ throw new ParseException("The " + headerName + " has an incorrect version: "
+ + attVersion + " (" + e.getMessage() + ")", 0);
+ }
+
+ for (String name : element.getValues()) {
+ BundleCapability export = new BundleCapability(type, name, version);
+ bundleInfo.addCapability(export);
+ }
+ }
+
+ }
+
+ private static VersionRange versionRangeOf(String v) throws ParseException {
+ if (v == null) {
+ return null;
+ }
+ return new VersionRange(v);
+ }
+
+ private static Version versionOf(String v) throws ParseException {
+ if (v == null) {
+ return null;
+ }
+ return new Version(v);
+ }
+
+ /**
+ * Ensure that the lines are not longer than 72 characters, so it can be parsed by the
+ * {@link Manifest} class
+ *
+ * @param manifest
+ * @return
+ */
+ public static String formatLines(String manifest) {
+ StringBuffer buffer = new StringBuffer(manifest.length());
+ String[] lines = manifest.split("\n");
+ for (int i = 0; i < lines.length; i++) {
+ if (lines[i].length() <= 72) {
+ buffer.append(lines[i]);
+ buffer.append('\n');
+ } else {
+ buffer.append(lines[i].substring(0, 72));
+ buffer.append("\n ");
+ int n = 72;
+ while (n <= lines[i].length() - 1) {
+ int end = n + 71;
+ if (end > lines[i].length()) {
+ end = lines[i].length();
+ }
+ buffer.append(lines[i].substring(n, end));
+ buffer.append('\n');
+ if (end != lines[i].length()) {
+ buffer.append(' ');
+ }
+ n = end;
+ }
+ }
+ }
+ return buffer.toString();
+ }
+}
diff --git a/src/java/org/apache/ivy/osgi/core/OSGiManifestParser.java b/src/java/org/apache/ivy/osgi/core/OSGiManifestParser.java
new file mode 100644
index 0000000..66cced1
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/core/OSGiManifestParser.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.core;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.text.ParseException;
+import java.util.Date;
+import java.util.Locale;
+import java.util.jar.Manifest;
+
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.DefaultArtifact;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.plugins.parser.ModuleDescriptorParser;
+import org.apache.ivy.plugins.parser.ParserSettings;
+import org.apache.ivy.plugins.parser.xml.XmlModuleDescriptorWriter;
+import org.apache.ivy.plugins.repository.Resource;
+import org.apache.ivy.plugins.repository.url.URLResource;
+
+public class OSGiManifestParser implements ModuleDescriptorParser {
+
+ private static final OSGiManifestParser INSTANCE = new OSGiManifestParser();
+
+ public static OSGiManifestParser getInstance() {
+ return INSTANCE;
+ }
+
+ private ExecutionEnvironmentProfileProvider profileProvider = ExecutionEnvironmentProfileProvider
+ .getInstance();
+
+ public void add(ExecutionEnvironmentProfileProvider pp) {
+ this.profileProvider = pp;
+ }
+
+ public boolean accept(Resource res) {
+ if (res == null || res.getName() == null || res.getName().trim().equals("")) {
+ return false;
+ }
+ return res.getName().toUpperCase(Locale.US).endsWith("MANIFEST.MF");
+ }
+
+ public ModuleDescriptor parseDescriptor(ParserSettings ivySettings, URL descriptorURL,
+ Resource res, boolean validate) throws ParseException, IOException {
+ Manifest m = new Manifest(res.openStream());
+ BundleInfo bundleInfo = ManifestParser.parseManifest(m);
+ try {
+ bundleInfo.addArtifact(new BundleArtifact(false, new URI(res.getName()), null));
+ } catch (URISyntaxException e) {
+ throw new RuntimeException("Unsupported repository, resources names are not uris", e);
+ }
+ return BundleInfoAdapter.toModuleDescriptor(this, null, bundleInfo, m, profileProvider);
+ }
+
+ public void toIvyFile(InputStream is, Resource res, File destFile, ModuleDescriptor md)
+ throws ParseException, IOException {
+ try {
+ XmlModuleDescriptorWriter.write(md, destFile);
+ } finally {
+ if (is != null) {
+ is.close();
+ }
+ }
+ }
+
+ public ModuleDescriptor parseDescriptor(ParserSettings ivySettings, URL descriptorURL,
+ boolean validate) throws ParseException, IOException {
+ URLResource resource = new URLResource(descriptorURL);
+ return parseDescriptor(ivySettings, descriptorURL, resource, validate);
+ }
+
+ public String getType() {
+ return "manifest";
+ }
+
+ public Artifact getMetadataArtifact(ModuleRevisionId mrid, Resource res) {
+ return DefaultArtifact.newIvyArtifact(mrid, new Date(res.getLastModified()));
+ }
+
+ public String toString() {
+ return "manifest parser";
+ }
+}
diff --git a/src/java/org/apache/ivy/osgi/core/OsgiLatestStrategy.java b/src/java/org/apache/ivy/osgi/core/OsgiLatestStrategy.java
new file mode 100644
index 0000000..e160fce
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/core/OsgiLatestStrategy.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.core;
+
+import java.text.ParseException;
+import java.util.Comparator;
+
+import org.apache.ivy.core.IvyContext;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.osgi.util.Version;
+import org.apache.ivy.plugins.latest.ArtifactInfo;
+import org.apache.ivy.plugins.latest.ComparatorLatestStrategy;
+import org.apache.ivy.plugins.version.VersionMatcher;
+
+public class OsgiLatestStrategy extends ComparatorLatestStrategy {
+
+ final class MridComparator implements Comparator<ModuleRevisionId> {
+
+ public int compare(ModuleRevisionId o1, ModuleRevisionId o2) {
+ Version v1;
+ Version v2;
+ try {
+ v1 = new Version(o1.getRevision());
+ v2 = new Version(o2.getRevision());
+ } catch (ParseException e) {
+ throw new RuntimeException("Uncomparable versions:" + o1.getRevision() + " and "
+ + o2.getRevision() + " (" + e.getMessage() + ")");
+ }
+ try {
+ return v1.compareTo(v2);
+ } catch (RuntimeException e) {
+ if (e.getCause() instanceof ParseException) {
+ throw new RuntimeException("Uncomparable versions:" + o1.getRevision()
+ + " and " + o2.getRevision() + " (" + e.getMessage() + ")");
+ }
+ throw e;
+ }
+ }
+
+ }
+
+ final class ArtifactInfoComparator implements Comparator<ArtifactInfo> {
+
+ public int compare(ArtifactInfo o1, ArtifactInfo o2) {
+ String rev1 = o1.getRevision();
+ String rev2 = o2.getRevision();
+
+ /*
+ * The revisions can still be not resolved, so we use the current version matcher to
+ * know if one revision is dynamic, and in this case if it should be considered greater
+ * or lower than the other one. Note that if the version matcher compare method returns
+ * 0, it's because it's not possible to know which revision is greater. In this case we
+ * consider the dynamic one to be greater, because most of the time it will then be
+ * actually resolved and a real comparison will occur.
+ */
+ VersionMatcher vmatcher = IvyContext.getContext().getSettings().getVersionMatcher();
+ ModuleRevisionId mrid1 = ModuleRevisionId.newInstance("", "", rev1);
+ ModuleRevisionId mrid2 = ModuleRevisionId.newInstance("", "", rev2);
+
+ if (vmatcher.isDynamic(mrid1)) {
+ int c = vmatcher.compare(mrid1, mrid2, mridComparator);
+ return c >= 0 ? 1 : -1;
+ } else if (vmatcher.isDynamic(mrid2)) {
+ int c = vmatcher.compare(mrid2, mrid1, mridComparator);
+ return c >= 0 ? -1 : 1;
+ }
+
+ return mridComparator.compare(mrid1, mrid2);
+ }
+ }
+
+ private final Comparator<ModuleRevisionId> mridComparator = new MridComparator();
+
+ private final Comparator<ArtifactInfo> artifactInfoComparator = new ArtifactInfoComparator();
+
+ public OsgiLatestStrategy() {
+ setComparator(artifactInfoComparator);
+ setName("latest-osgi");
+ }
+
+}
diff --git a/src/java/org/apache/ivy/osgi/core/jvm-packages.properties b/src/java/org/apache/ivy/osgi/core/jvm-packages.properties
new file mode 100644
index 0000000..4aa4ec1
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/core/jvm-packages.properties
@@ -0,0 +1,220 @@
+# ***************************************************************
+# * Licensed to the Apache Software Foundation (ASF) under one
+# * or more contributor license agreements. See the NOTICE file
+# * distributed with this work for additional information
+# * regarding copyright ownership. The ASF licenses this file
+# * to you under the Apache License, Version 2.0 (the
+# * "License"); you may not use this file except in compliance
+# * with the License. You may obtain a copy of the License at
+# *
+# * http://www.apache.org/licenses/LICENSE-2.0
+# *
+# * Unless required by applicable law or agreed to in writing,
+# * software distributed under the License is distributed on an
+# * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# * KIND, either express or implied. See the License for the
+# * specific language governing permissions and limitations
+# * under the License.
+# ***************************************************************
+
+OSGi/Minimum-1.0.aliases = OSGI_MINIMUM-1.0
+OSGi/Minimum-1.0.pkglist =
+
+OSGi/Minimum-1.1.aliases = OSGI_MINIMUM-1.1
+OSGi/Minimum-1.1.pkglist =
+
+OSGi/Minimum-1.2.aliases = OSGI_MINIMUM-1.2
+OSGi/Minimum-1.2.pkglist =
+
+CDC-1.0/Foundation-1.0.aliases = CDC-1.0_Foundation-1.0
+CDC-1.0/Foundation-1.0.pkglist = \
+ javax.microedition.io
+
+CDC-1.1/Foundation-1.1.aliases = CDC-1.1_Foundation-1.1
+CDC-1.1/Foundation-1.1.extends = CDC-1.0/Foundation-1.0
+CDC-1.1/Foundation-1.1.pkglist = \
+ javax.microedition.pki,\
+ javax.security.auth.x500
+
+JavaSE-1.2.aliases = J2SE-1.2
+JavaSE-1.2.pkglist = \
+ javax.accessibility,\
+ javax.swing,\
+ javax.swing.border,\
+ javax.swing.colorchooser,\
+ javax.swing.event,\
+ javax.swing.filechooser,\
+ javax.swing.plaf,\
+ javax.swing.plaf.basic,\
+ javax.swing.plaf.metal,\
+ javax.swing.plaf.multi,\
+ javax.swing.table,\
+ javax.swing.text,\
+ javax.swing.text.html,\
+ javax.swing.text.html.parser,\
+ javax.swing.text.rtf,\
+ javax.swing.tree,\
+ javax.swing.undo,\
+ org.omg.CORBA,\
+ org.omg.CORBA.DynAnyPackage,\
+ org.omg.CORBA.ORBPackage,\
+ org.omg.CORBA.portable,\
+ org.omg.CORBA.TypeCodePackage,\
+ org.omg.CosNaming,\
+ org.omg.CosNaming.NamingContextPackage
+
+JavaSE-1.3.aliases = J2SE-1.3
+JavaSE-1.3.extends = JavaSE-1.2
+JavaSE-1.3.pkglist = \
+ javax.naming,\
+ javax.naming.directory,\
+ javax.naming.event,\
+ javax.naming.ldap,\
+ javax.naming.spi,\
+ javax.rmi,\
+ javax.rmi.CORBA,\
+ javax.sound.midi,\
+ javax.sound.midi.spi,\
+ javax.sound.sampled,\
+ javax.sound.sampled.spi,\
+ javax.transaction,\
+ org.omg.CORBA_2_3,\
+ org.omg.CORBA_2_3.portable,\
+ org.omg.SendingContext,\
+ org.omg.stub.java.rmi
+
+JavaSE-1.4.aliases = J2SE-1.4
+JavaSE-1.4.extends = JavaSE-1.3
+JavaSE-1.4.pkglist = \
+ javax.crypto,\
+ javax.crypto.interfaces,\
+ javax.crypto.spec,\
+ javax.imageio,\
+ javax.imageio.event,\
+ javax.imageio.metadata,\
+ javax.imageio.plugins.jpeg,\
+ javax.imageio.spi,\
+ javax.imageio.stream,\
+ javax.net,\
+ javax.net.ssl,\
+ javax.print,\
+ javax.print.attribute,\
+ javax.print.attribute.standard,\
+ javax.print.event,\
+ javax.security.auth,\
+ javax.security.auth.callback,\
+ javax.security.auth.kerberos,\
+ javax.security.auth.login,\
+ javax.security.auth.spi,\
+ javax.security.auth.x500,\
+ javax.security.cert,\
+ javax.sql,\
+ javax.transaction.xa,\
+ javax.xml.parsers,\
+ javax.xml.transform,\
+ javax.xml.transform.dom,\
+ javax.xml.transform.sax,\
+ javax.xml.transform.stream,\
+ org.ietf.jgss,\
+ org.omg.CosNaming.NamingContextExtPackage,\
+ org.omg.Dynamic,\
+ org.omg.DynamicAny,\
+ org.omg.DynamicAny.DynAnyFactoryPackage,\
+ org.omg.DynamicAny.DynAnyPackage,\
+ org.omg.IOP,\
+ org.omg.IOP.CodecFactoryPackage,\
+ org.omg.IOP.CodecPackage,\
+ org.omg.Messaging,\
+ org.omg.PortableInterceptor,\
+ org.omg.PortableInterceptor.ORBInitInfoPackage,\
+ org.omg.PortableServer,\
+ org.omg.PortableServer.CurrentPackage,\
+ org.omg.PortableServer.POAManagerPackage,\
+ org.omg.PortableServer.POAPackage,\
+ org.omg.PortableServer.portable,\
+ org.omg.PortableServer.ServantLocatorPackage,\
+ org.w3c.dom,\
+ org.w3c.dom.css,\
+ org.w3c.dom.events,\
+ org.w3c.dom.html,\
+ org.w3c.dom.stylesheets,\
+ org.w3c.dom.views,\
+ org.xml.sax,\
+ org.xml.sax.ext,\
+ org.xml.sax.helpers
+
+JavaSE-1.5.aliases = J2SE-1.5
+JavaSE-1.5.extends = JavaSE-1.4
+JavaSE-1.5.pkglist = \
+ javax.activity,\
+ javax.imageio.plugins.bmp,\
+ javax.management,\
+ javax.management.loading,\
+ javax.management.modelmbean,\
+ javax.management.monitor,\
+ javax.management.openmbean,\
+ javax.management.relation,\
+ javax.management.remote,\
+ javax.management.remote.rmi,\
+ javax.management.timer,\
+ javax.rmi.ssl,\
+ javax.security.sasl,\
+ javax.sql.rowset,\
+ javax.sql.rowset.serial,\
+ javax.sql.rowset.spi,\
+ javax.swing.plaf.synth,\
+ javax.xml,\
+ javax.xml.datatype,\
+ javax.xml.namespace,\
+ javax.xml.validation,\
+ javax.xml.xpath,\
+ org.w3c.dom.bootstrap,\
+ org.w3c.dom.ls,\
+ org.w3c.dom.ranges,\
+ org.w3c.dom.traversal
+
+JavaSE-1.6.extends = JavaSE-1.5
+JavaSE-1.6.pkglist = \
+ javax.activation,\
+ javax.annotation,\
+ javax.annotation.processing,\
+ javax.jws,\
+ javax.jws.soap,\
+ javax.lang.model,\
+ javax.lang.model.element,\
+ javax.lang.model.type,\
+ javax.lang.model.util,\
+ javax.script,\
+ javax.tools,\
+ javax.xml.bind,\
+ javax.xml.bind.annotation,\
+ javax.xml.bind.annotation.adapters,\
+ javax.xml.bind.attachment,\
+ javax.xml.bind.helpers,\
+ javax.xml.bind.util,\
+ javax.xml.crypto,\
+ javax.xml.crypto.dom,\
+ javax.xml.crypto.dsig,\
+ javax.xml.crypto.dsig.dom,\
+ javax.xml.crypto.dsig.keyinfo,\
+ javax.xml.crypto.dsig.spec,\
+ javax.xml.soap,\
+ javax.xml.stream,\
+ javax.xml.stream.events,\
+ javax.xml.stream.util,\
+ javax.xml.transform.stax,\
+ javax.xml.ws,\
+ javax.xml.ws.handler,\
+ javax.xml.ws.handler.soap,\
+ javax.xml.ws.http,\
+ javax.xml.ws.soap,\
+ javax.xml.ws.spi,\
+ javax.xml.ws.spi.http,\
+ javax.xml.ws.wsaddressing
+
+JavaSE-1.7.extends = JavaSE-1.6
+JavaSE-1.7.pkglist = \
+ javax.swing.plaf.nimbus
+
+JavaSE-1.8.extends = JavaSE-1.7
+JavaSE-1.8.pkglist =
diff --git a/src/java/org/apache/ivy/osgi/filter/AndFilter.java b/src/java/org/apache/ivy/osgi/filter/AndFilter.java
new file mode 100644
index 0000000..7fe700e
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/filter/AndFilter.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.filter;
+
+import java.util.Map;
+
+public class AndFilter extends MultiOperatorFilter {
+
+ public AndFilter() {
+ super();
+ }
+
+ public AndFilter(OSGiFilter[] filters) {
+ super(filters);
+ }
+
+ protected char operator() {
+ return '&';
+ }
+
+ @Override
+ public boolean eval(Map<String, String> properties) {
+ for (OSGiFilter filter : getSubFilters()) {
+ if (!filter.eval(properties)) {
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/src/java/org/apache/ivy/osgi/filter/CompareFilter.java b/src/java/org/apache/ivy/osgi/filter/CompareFilter.java
new file mode 100644
index 0000000..cc8f564
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/filter/CompareFilter.java
@@ -0,0 +1,138 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.filter;
+
+import java.util.Map;
+
+public class CompareFilter extends OSGiFilter {
+
+ public static enum Operator {
+
+ EQUALS("="), LOWER_THAN("<"), LOWER_OR_EQUAL("<="), GREATER_THAN(">"), GREATER_OR_EQUAL(
+ ">=");
+
+ private String op;
+
+ private Operator(String op) {
+ this.op = op;
+ }
+
+ public String toString() {
+ return op;
+ }
+ }
+
+ private Operator operator;
+
+ private final String rightValue;
+
+ private final String leftValue;
+
+ public CompareFilter(String leftValue, Operator operator, String rightValue) {
+ this.leftValue = leftValue;
+ this.rightValue = rightValue;
+ this.operator = operator;
+ }
+
+ public String getLeftValue() {
+ return leftValue;
+ }
+
+ public Operator getOperator() {
+ return operator;
+ }
+
+ public String getRightValue() {
+ return rightValue;
+ }
+
+ public void append(StringBuffer builder) {
+ builder.append("(");
+ builder.append(leftValue);
+ builder.append(operator.toString());
+ builder.append(rightValue);
+ builder.append(")");
+ }
+
+ @Override
+ public boolean eval(Map<String, String> properties) {
+ String actualValue = properties.get(leftValue);
+ if (actualValue == null) {
+ return false;
+ }
+ int diff = rightValue.compareTo(actualValue);
+ switch (operator) {
+ case EQUALS:
+ return diff == 0;
+ case GREATER_THAN:
+ return diff > 0;
+ case GREATER_OR_EQUAL:
+ return diff >= 0;
+ case LOWER_OR_EQUAL:
+ return diff <= 0;
+ case LOWER_THAN:
+ return diff < 0;
+ default:
+ throw new IllegalStateException();
+ }
+ }
+
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((leftValue == null) ? 0 : leftValue.hashCode());
+ result = prime * result + ((operator == null) ? 0 : operator.hashCode());
+ result = prime * result + ((rightValue == null) ? 0 : rightValue.hashCode());
+ return result;
+ }
+
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (!(obj instanceof CompareFilter)) {
+ return false;
+ }
+ CompareFilter other = (CompareFilter) obj;
+ if (leftValue == null) {
+ if (other.leftValue != null) {
+ return false;
+ }
+ } else if (!leftValue.equals(other.leftValue)) {
+ return false;
+ }
+ if (operator == null) {
+ if (other.operator != null) {
+ return false;
+ }
+ } else if (!operator.equals(other.operator)) {
+ return false;
+ }
+ if (rightValue == null) {
+ if (other.rightValue != null) {
+ return false;
+ }
+ } else if (!rightValue.equals(other.rightValue)) {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/src/java/org/apache/ivy/osgi/filter/MultiOperatorFilter.java b/src/java/org/apache/ivy/osgi/filter/MultiOperatorFilter.java
new file mode 100644
index 0000000..f407c05
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/filter/MultiOperatorFilter.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.filter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public abstract class MultiOperatorFilter extends OSGiFilter {
+
+ private List<OSGiFilter> subFilters = new ArrayList<OSGiFilter>();
+
+ public MultiOperatorFilter() {
+ // default constructor
+ }
+
+ public MultiOperatorFilter(OSGiFilter[] filters) {
+ for (int i = 0; i < filters.length; i++) {
+ OSGiFilter filter = filters[i];
+ add(filter);
+ }
+ }
+
+ abstract protected char operator();
+
+ public void append(StringBuffer builder) {
+ builder.append('(');
+ builder.append(operator());
+ for (OSGiFilter filter : subFilters) {
+ filter.append(builder);
+ }
+ builder.append(')');
+ }
+
+ public void add(OSGiFilter subFilter2) {
+ subFilters.add(subFilter2);
+ }
+
+ public List<OSGiFilter> getSubFilters() {
+ return subFilters;
+ }
+
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ for (OSGiFilter subFilter : subFilters) {
+ result = prime * result + ((subFilter == null) ? 0 : subFilter.hashCode());
+ }
+ return result;
+ }
+
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (!(obj instanceof MultiOperatorFilter)) {
+ return false;
+ }
+ MultiOperatorFilter other = (MultiOperatorFilter) obj;
+ if (subFilters == null) {
+ if (other.subFilters != null) {
+ return false;
+ }
+ } else if (other.subFilters == null) {
+ return false;
+ } else if (subFilters.size() != other.subFilters.size()) {
+ return false;
+ } else if (!subFilters.containsAll(other.subFilters)) {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/src/java/org/apache/ivy/osgi/filter/NotFilter.java b/src/java/org/apache/ivy/osgi/filter/NotFilter.java
new file mode 100644
index 0000000..fce5cd8
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/filter/NotFilter.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.filter;
+
+import java.util.Map;
+
+public class NotFilter extends UniOperatorFilter {
+
+ public NotFilter(OSGiFilter subFilter) {
+ super(subFilter);
+ }
+
+ protected char operator() {
+ return '!';
+ }
+
+ @Override
+ public boolean eval(Map<String, String> properties) {
+ return !getSubFilter().eval(properties);
+ }
+}
diff --git a/src/java/org/apache/ivy/osgi/filter/OSGiFilter.java b/src/java/org/apache/ivy/osgi/filter/OSGiFilter.java
new file mode 100644
index 0000000..e406755
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/filter/OSGiFilter.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.filter;
+
+import java.util.Map;
+
+public abstract class OSGiFilter {
+
+ public String toString() {
+ StringBuffer builder = new StringBuffer();
+ append(builder);
+ return builder.toString();
+ }
+
+ public abstract void append(StringBuffer builder);
+
+ public abstract boolean eval(Map<String, String> properties);
+
+}
diff --git a/src/java/org/apache/ivy/osgi/filter/OSGiFilterParser.java b/src/java/org/apache/ivy/osgi/filter/OSGiFilterParser.java
new file mode 100644
index 0000000..cd3d6e2
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/filter/OSGiFilterParser.java
@@ -0,0 +1,217 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.filter;
+
+import java.text.ParseException;
+
+import org.apache.ivy.osgi.filter.CompareFilter.Operator;
+
+public class OSGiFilterParser {
+
+ public static OSGiFilter parse(String text) throws ParseException {
+ return new Parser(text).parse();
+ }
+
+ static class Parser {
+
+ /**
+ * text to parse
+ */
+ private final String text;
+
+ /**
+ * the length of the source
+ */
+ private int length;
+
+ /**
+ * position in the source
+ */
+ private int pos = 0;
+
+ /**
+ * last read character
+ */
+ private char c;
+
+ /**
+ * Default constructor
+ *
+ * @param header
+ * the header to parse
+ */
+ Parser(String text) {
+ this.text = text;
+ this.length = text.length();
+ }
+
+ /**
+ * Do the parsing
+ *
+ * @return
+ *
+ * @throws ParseException
+ */
+ OSGiFilter parse() throws ParseException {
+ return parseFilter();
+ }
+
+ private char readNext() {
+ if (pos == length) {
+ c = '\0';
+ } else {
+ c = text.charAt(pos++);
+ }
+ return c;
+ }
+
+ private void unread() {
+ if (pos > 0) {
+ pos--;
+ }
+ }
+
+ private OSGiFilter parseFilter() throws ParseException {
+ skipWhiteSpace();
+ readNext();
+ if (c != '(') {
+ throw new ParseException("Expecting '(' as the start of the filter", pos);
+ }
+ OSGiFilter filter;
+ switch (readNext()) {
+ case '&':
+ filter = parseAnd();
+ break;
+ case '|':
+ filter = parseOr();
+ break;
+ case '!':
+ filter = parseNot();
+ break;
+ default:
+ unread();
+ filter = parseCompare();
+ break;
+ }
+ readNext();
+ if (c != ')') {
+ throw new ParseException("Expecting ')' as the end of the filter", pos);
+ }
+ return filter;
+ }
+
+ private OSGiFilter parseCompare() throws ParseException {
+ String leftValue = parseCompareValue();
+ Operator operator = parseCompareOperator();
+ String rightValue = parseCompareValue();
+ return new CompareFilter(leftValue, operator, rightValue);
+ }
+
+ private String parseCompareValue() {
+ StringBuffer builder = new StringBuffer();
+ do {
+ readNext();
+ if (!isOperator(c) && c != ')' && c != '(') {
+ builder.append(c);
+ } else {
+ unread();
+ break;
+ }
+ } while (pos < length);
+ return builder.toString();
+ }
+
+ private boolean isOperator(char ch) {
+ return ch == '=' || ch == '<' || ch == '>';
+ }
+
+ private Operator parseCompareOperator() throws ParseException {
+ switch (readNext()) {
+ case '=':
+ return Operator.EQUALS;
+ case '>':
+ if (readNext() == '=') {
+ return Operator.GREATER_OR_EQUAL;
+ }
+ unread();
+ return Operator.GREATER_THAN;
+ case '<':
+ if (readNext() == '=') {
+ return Operator.LOWER_OR_EQUAL;
+ }
+ unread();
+ return Operator.LOWER_THAN;
+ default:
+ break;
+ }
+ throw new ParseException("Expecting an operator: =, <, <=, > or >=", pos);
+ }
+
+ private OSGiFilter parseAnd() throws ParseException {
+ AndFilter filter = new AndFilter();
+ parseMultiOperator(filter);
+ return filter;
+ }
+
+ private OSGiFilter parseOr() throws ParseException {
+ OrFilter filter = new OrFilter();
+ parseMultiOperator(filter);
+ return filter;
+ }
+
+ private void parseMultiOperator(MultiOperatorFilter filter) throws ParseException {
+ do {
+ skipWhiteSpace();
+ readNext();
+ if (c == '(') {
+ unread();
+ filter.add(parseFilter());
+ } else {
+ unread();
+ break;
+ }
+ } while (pos < length);
+ if (filter.getSubFilters().size() == 0) {
+ throw new ParseException("Expecting at least one sub filter", pos);
+ }
+ }
+
+ private OSGiFilter parseNot() throws ParseException {
+ readNext();
+ if (c != '(') {
+ throw new ParseException("The ! operator is expecting a filter", pos);
+ }
+ unread();
+ return new NotFilter(parseFilter());
+ }
+
+ private void skipWhiteSpace() {
+ do {
+ switch (readNext()) {
+ case ' ':
+ continue;
+ default:
+ unread();
+ return;
+ }
+ } while (pos < length);
+ }
+
+ }
+
+}
diff --git a/src/java/org/apache/ivy/osgi/filter/OrFilter.java b/src/java/org/apache/ivy/osgi/filter/OrFilter.java
new file mode 100644
index 0000000..ef8b126
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/filter/OrFilter.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.filter;
+
+import java.util.Map;
+
+public class OrFilter extends MultiOperatorFilter {
+
+ public OrFilter() {
+ super();
+ }
+
+ public OrFilter(OSGiFilter[] filters) {
+ super(filters);
+ }
+
+ protected char operator() {
+ return '|';
+ }
+
+ @Override
+ public boolean eval(Map<String, String> properties) {
+ for (OSGiFilter filter : getSubFilters()) {
+ if (filter.eval(properties)) {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/src/java/org/apache/ivy/osgi/filter/UniOperatorFilter.java b/src/java/org/apache/ivy/osgi/filter/UniOperatorFilter.java
new file mode 100644
index 0000000..02e7ef5
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/filter/UniOperatorFilter.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.filter;
+
+public abstract class UniOperatorFilter extends OSGiFilter {
+
+ private final OSGiFilter subFilter;
+
+ public UniOperatorFilter(OSGiFilter subFilter) {
+ this.subFilter = subFilter;
+ }
+
+ abstract protected char operator();
+
+ public void append(StringBuffer builder) {
+ builder.append("(");
+ builder.append(operator());
+ builder.append(subFilter.toString());
+ builder.append(")");
+ }
+
+ public OSGiFilter getSubFilter() {
+ return subFilter;
+ }
+
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((subFilter == null) ? 0 : subFilter.hashCode());
+ return result;
+ }
+
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (!(obj instanceof UniOperatorFilter)) {
+ return false;
+ }
+ UniOperatorFilter other = (UniOperatorFilter) obj;
+ if (subFilter == null) {
+ if (other.subFilter != null) {
+ return false;
+ }
+ } else if (!subFilter.equals(other.subFilter)) {
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/src/java/org/apache/ivy/osgi/obr/OBRResolver.java b/src/java/org/apache/ivy/osgi/obr/OBRResolver.java
new file mode 100644
index 0000000..581be3c
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/obr/OBRResolver.java
@@ -0,0 +1,147 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.obr;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.text.ParseException;
+
+import org.apache.ivy.core.cache.CacheResourceOptions;
+import org.apache.ivy.core.event.EventManager;
+import org.apache.ivy.core.report.ArtifactDownloadReport;
+import org.apache.ivy.osgi.obr.xml.OBRXMLParser;
+import org.apache.ivy.osgi.repo.AbstractOSGiResolver;
+import org.apache.ivy.plugins.repository.Resource;
+import org.apache.ivy.plugins.repository.url.URLResource;
+import org.xml.sax.SAXException;
+
+public class OBRResolver extends AbstractOSGiResolver {
+
+ private String repoXmlURL;
+
+ private String repoXmlFile;
+
+ private Long metadataTtl;
+
+ private Boolean forceMetadataUpdate;
+
+ public void setRepoXmlFile(String repositoryXmlFile) {
+ this.repoXmlFile = repositoryXmlFile;
+ }
+
+ public void setRepoXmlURL(String repositoryXmlURL) {
+ this.repoXmlURL = repositoryXmlURL;
+ }
+
+ public void setMetadataTtl(Long metadataTtl) {
+ this.metadataTtl = metadataTtl;
+ }
+
+ public void setForceMetadataUpdate(Boolean forceMetadataUpdate) {
+ this.forceMetadataUpdate = forceMetadataUpdate;
+ }
+
+ protected void init() {
+ if (repoXmlFile != null && repoXmlURL != null) {
+ throw new RuntimeException("The OBR repository resolver " + getName()
+ + " couldn't be configured: repoXmlFile and repoXmlUrl cannot be set both");
+ }
+ if (repoXmlFile != null) {
+ File f = new File(repoXmlFile);
+ loadRepoFromFile(f.getParentFile().toURI(), f, repoXmlFile);
+ } else if (repoXmlURL != null) {
+ final URL url;
+ try {
+ url = new URL(repoXmlURL);
+ } catch (MalformedURLException e) {
+ throw new RuntimeException("The OBR repository resolver " + getName()
+ + " couldn't be configured: repoXmlURL '" + repoXmlURL + "' is not an URL");
+ }
+
+ ArtifactDownloadReport report;
+ EventManager eventManager = getEventManager();
+ try {
+ if (eventManager != null) {
+ getRepository().addTransferListener(eventManager);
+ }
+ Resource obrResource = new URLResource(url);
+ CacheResourceOptions options = new CacheResourceOptions();
+ if (metadataTtl != null) {
+ options.setTtl(metadataTtl.longValue());
+ }
+ if (forceMetadataUpdate != null) {
+ options.setForce(forceMetadataUpdate.booleanValue());
+ }
+ report = getRepositoryCacheManager().downloadRepositoryResource(obrResource, "obr",
+ "obr", "xml", options, getRepository());
+ } finally {
+ if (eventManager != null) {
+ getRepository().removeTransferListener(eventManager);
+ }
+ }
+
+ URI baseURI;
+ try {
+ baseURI = new URI(repoXmlURL);
+ } catch (URISyntaxException e) {
+ throw new RuntimeException("illegal uri");
+ }
+ loadRepoFromFile(baseURI, report.getLocalFile(), repoXmlURL);
+
+ } else {
+ throw new RuntimeException("The OBR repository resolver " + getName()
+ + " couldn't be configured: repoXmlFile or repoXmlUrl is missing");
+ }
+ }
+
+ private void loadRepoFromFile(URI baseUri, File repoFile, String sourceLocation) {
+ FileInputStream in;
+ try {
+ in = new FileInputStream(repoFile);
+ } catch (FileNotFoundException e) {
+ throw new RuntimeException("The OBR repository resolver " + getName()
+ + " couldn't be configured: the file " + sourceLocation + " was not found");
+ }
+ try {
+ setRepoDescriptor(OBRXMLParser.parse(baseUri, in));
+ } catch (ParseException e) {
+ throw new RuntimeException("The OBR repository resolver " + getName()
+ + " couldn't be configured: the file " + sourceLocation
+ + " is incorrectly formed (" + e.getMessage() + ")", e);
+ } catch (IOException e) {
+ throw new RuntimeException("The OBR repository resolver " + getName()
+ + " couldn't be configured: the file " + sourceLocation
+ + " could not be read (" + e.getMessage() + ")", e);
+ } catch (SAXException e) {
+ throw new RuntimeException("The OBR repository resolver " + getName()
+ + " couldn't be configured: the file " + sourceLocation
+ + " has incorrect XML (" + e.getMessage() + ")", e);
+ }
+ try {
+ in.close();
+ } catch (IOException e) {
+ // don't care
+ }
+ }
+}
diff --git a/src/java/org/apache/ivy/osgi/obr/xml/Capability.java b/src/java/org/apache/ivy/osgi/obr/xml/Capability.java
new file mode 100644
index 0000000..4712b94
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/obr/xml/Capability.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.obr.xml;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class Capability {
+
+ private List<CapabilityProperty> properties = new ArrayList<CapabilityProperty>();
+
+ private String name;
+
+ public Capability(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void addProperty(String n, String value, String type) {
+ properties.add(new CapabilityProperty(n, value, type));
+ }
+
+ public List<CapabilityProperty> getProperties() {
+ return properties;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder buffer = new StringBuilder();
+ buffer.append(name);
+ for (CapabilityProperty p : properties) {
+ buffer.append(" ");
+ buffer.append(p);
+ }
+ return buffer.toString();
+ }
+}
diff --git a/src/java/org/apache/ivy/osgi/obr/xml/CapabilityAdapter.java b/src/java/org/apache/ivy/osgi/obr/xml/CapabilityAdapter.java
new file mode 100644
index 0000000..2df1f11
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/obr/xml/CapabilityAdapter.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.obr.xml;
+
+import java.text.ParseException;
+
+import org.apache.ivy.osgi.core.BundleCapability;
+import org.apache.ivy.osgi.core.BundleInfo;
+import org.apache.ivy.osgi.core.ExportPackage;
+import org.apache.ivy.osgi.util.Version;
+import org.apache.ivy.util.Message;
+
+public class CapabilityAdapter {
+
+ public static void adapt(BundleInfo bundleInfo, Capability capability) throws ParseException {
+ String name = capability.getName();
+ if (BundleInfo.PACKAGE_TYPE.equals(name)) {
+ ExportPackage exportPackage = getExportPackage(bundleInfo, capability);
+ bundleInfo.addCapability(exportPackage);
+ } else if (BundleInfo.BUNDLE_TYPE.equals(name)) {
+ // nothing to do, already handled at the resource tag level
+ } else if (BundleInfo.SERVICE_TYPE.equals(name)) {
+ BundleCapability service = getOSGiService(bundleInfo, capability);
+ bundleInfo.addCapability(service);
+ } else {
+ Message.warn("Unsupported capability '" + name + "' on the bundle '"
+ + bundleInfo.getSymbolicName() + "'");
+ }
+ }
+
+ private static ExportPackage getExportPackage(BundleInfo bundleInfo, Capability capability)
+ throws ParseException {
+ String pkgName = null;
+ Version version = null;
+ String uses = null;
+ for (CapabilityProperty property : capability.getProperties()) {
+ String propName = property.getName();
+ if ("package".equals(propName)) {
+ pkgName = property.getValue();
+ } else if ("version".equals(propName)) {
+ version = new Version(property.getValue());
+ } else if ("uses".equals(propName)) {
+ uses = property.getValue();
+ } else {
+ Message.warn("Unsupported property '" + propName
+ + "' on the 'package' capability of the bundle '"
+ + bundleInfo.getSymbolicName() + "'");
+ }
+ }
+ if (pkgName == null) {
+ throw new ParseException("No package name for the capability", 0);
+ }
+ ExportPackage exportPackage = new ExportPackage(pkgName, version);
+ if (uses != null) {
+ String[] split = uses.trim().split(",");
+ for (int i = 0; i < split.length; i++) {
+ String u = split[i];
+ exportPackage.addUse(u.trim());
+ }
+ }
+ return exportPackage;
+ }
+
+ private static BundleCapability getOSGiService(BundleInfo bundleInfo, Capability capability)
+ throws ParseException {
+ String name = null;
+ Version version = null;
+
+ for (CapabilityProperty property : capability.getProperties()) {
+ String propName = property.getName();
+ if ("service".equals(propName)) {
+ name = property.getValue();
+ } else if ("version".equals(propName)) {
+ version = new Version(property.getValue());
+ } else {
+ Message.warn("Unsupported property '" + propName
+ + "' on the 'package' capability of the bundle '"
+ + bundleInfo.getSymbolicName() + "'");
+ }
+ }
+
+ if (name == null) {
+ throw new ParseException("No service name for the capability", 0);
+ }
+ BundleCapability service = new BundleCapability(BundleInfo.SERVICE_TYPE, name, version);
+ return service;
+ }
+}
diff --git a/src/java/org/apache/ivy/osgi/obr/xml/CapabilityProperty.java b/src/java/org/apache/ivy/osgi/obr/xml/CapabilityProperty.java
new file mode 100644
index 0000000..a363ae0
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/obr/xml/CapabilityProperty.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.obr.xml;
+
+public class CapabilityProperty {
+
+ private String name;
+
+ private String value;
+
+ private String type;
+
+ public CapabilityProperty(String name, String value, String type) {
+ this.name = name;
+ this.value = value;
+ this.type = type;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ @Override
+ public String toString() {
+ return (type == null ? "" : "[" + type + "]") + name + "=" + value;
+ }
+
+}
diff --git a/src/java/org/apache/ivy/osgi/obr/xml/OBRXMLParser.java b/src/java/org/apache/ivy/osgi/obr/xml/OBRXMLParser.java
new file mode 100644
index 0000000..9353880
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/obr/xml/OBRXMLParser.java
@@ -0,0 +1,407 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.obr.xml;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.text.ParseException;
+
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.apache.ivy.osgi.core.BundleArtifact;
+import org.apache.ivy.osgi.core.BundleInfo;
+import org.apache.ivy.osgi.core.ExecutionEnvironmentProfileProvider;
+import org.apache.ivy.osgi.filter.OSGiFilter;
+import org.apache.ivy.osgi.filter.OSGiFilterParser;
+import org.apache.ivy.osgi.repo.BundleRepoDescriptor;
+import org.apache.ivy.osgi.util.DelegatingHandler;
+import org.apache.ivy.osgi.util.Version;
+import org.apache.ivy.util.Message;
+import org.apache.ivy.util.XMLHelper;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
+public class OBRXMLParser {
+
+ public static BundleRepoDescriptor parse(URI baseUri, InputStream in) throws ParseException,
+ IOException, SAXException {
+ RepositoryHandler handler = new RepositoryHandler(baseUri);
+ try {
+ XMLHelper.parse(in, null, handler, null);
+ } catch (ParserConfigurationException e) {
+ throw new SAXException(e);
+ }
+ return handler.repo;
+ }
+
+ static class RepositoryHandler extends DelegatingHandler {
+
+ static final String REPOSITORY = "repository";
+
+ static final String LASTMODIFIED = "lastmodified";
+
+ static final String NAME = "name";
+
+ BundleRepoDescriptor repo;
+
+ private final URI baseUri;
+
+ public RepositoryHandler(URI baseUri) {
+ super(REPOSITORY);
+ this.baseUri = baseUri;
+ addChild(new ResourceHandler(), new ChildElementHandler<ResourceHandler>() {
+ public void childHanlded(ResourceHandler child) {
+ repo.addBundle(child.bundleInfo);
+ }
+ });
+ }
+
+ protected void handleAttributes(Attributes atts) {
+ repo = new BundleRepoDescriptor(baseUri,
+ ExecutionEnvironmentProfileProvider.getInstance());
+
+ repo.setName(atts.getValue(NAME));
+
+ repo.setLastModified(atts.getValue(LASTMODIFIED));
+ }
+ }
+
+ static class ResourceHandler extends DelegatingHandler {
+
+ private static final String DEFAULT_VERSION = "1.0.0";
+
+ static final String RESOURCE = "resource";
+
+ static final String ID = "id";
+
+ static final String PRESENTATION_NAME = "presentationname";
+
+ static final String SYMBOLIC_NAME = "symbolicname";
+
+ static final String URI = "uri";
+
+ static final String VERSION = "version";
+
+ BundleInfo bundleInfo;
+
+ public ResourceHandler() {
+ super(RESOURCE);
+
+ setSkipOnError(true); // if anything bad happen in any children, just ignore the
+ // resource
+
+ addChild(new ResourceSourceHandler(), new ChildElementHandler<ResourceSourceHandler>() {
+ public void childHanlded(ResourceSourceHandler child) {
+ String uri = child.getBufferedChars().trim();
+ if (!uri.endsWith(".jar")) {
+ // the maven plugin is putting some useless source url sometimes...
+ log(Message.MSG_WARN,
+ "A source uri is suspect, it is not ending with .jar, it is probably"
+ + " a pointer to a download page. Ignoring it.");
+ return;
+ }
+ try {
+ bundleInfo.addArtifact(new BundleArtifact(true, new URI(uri), null));
+ } catch (URISyntaxException e) {
+ log(Message.MSG_WARN, "Incorrect uri " + uri + ". The source of "
+ + bundleInfo.getSymbolicName() + " is then ignored.");
+ return;
+ }
+ }
+ });
+ addChild(new ResourceDescriptionHandler(),
+ new ChildElementHandler<ResourceDescriptionHandler>() {
+ public void childHanlded(ResourceDescriptionHandler child) {
+ bundleInfo.setDescription(child.getBufferedChars().trim());
+ }
+ });
+ addChild(new ResourceDocumentationHandler(),
+ new ChildElementHandler<ResourceDocumentationHandler>() {
+ public void childHanlded(ResourceDocumentationHandler child) {
+ bundleInfo.setDocumentation(child.getBufferedChars().trim());
+ }
+ });
+ addChild(new ResourceLicenseHandler(),
+ new ChildElementHandler<ResourceLicenseHandler>() {
+ public void childHanlded(ResourceLicenseHandler child) {
+ bundleInfo.setLicense(child.getBufferedChars().trim());
+ }
+ });
+ addChild(new ResourceSizeHandler(), new ChildElementHandler<ResourceSizeHandler>() {
+ public void childHanlded(ResourceSizeHandler child) {
+ String size = child.getBufferedChars().trim();
+ try {
+ bundleInfo.setSize(Integer.valueOf(size));
+ } catch (NumberFormatException e) {
+ log(Message.MSG_WARN,
+ "Invalid size for the bundle " + bundleInfo.getSymbolicName() + ": "
+ + size + ". This size is then ignored.");
+ }
+ }
+ });
+ addChild(new CapabilityHandler(), new ChildElementHandler<CapabilityHandler>() {
+ public void childHanlded(CapabilityHandler child) throws SAXParseException {
+
+ try {
+ CapabilityAdapter.adapt(bundleInfo, child.capability);
+ } catch (ParseException e) {
+ throw new SAXParseException("Invalid capability: " + e.getMessage(), child
+ .getLocator());
+ }
+ }
+ });
+ addChild(new RequireHandler(), new ChildElementHandler<RequireHandler>() {
+ public void childHanlded(RequireHandler child) throws SAXParseException {
+ try {
+ RequirementAdapter.adapt(bundleInfo, child.requirement);
+ } catch (UnsupportedFilterException e) {
+ throw new SAXParseException("Unsupported requirement filter: "
+ + child.filter + " (" + e.getMessage() + ")", getLocator());
+ } catch (ParseException e) {
+ throw new SAXParseException(
+ "Error in the requirement filter on the bundle: " + e.getMessage(),
+ getLocator());
+ }
+ }
+ });
+ addChild(new ExtendHandler(), new ChildElementHandler<ExtendHandler>() {
+ public void childHanlded(ExtendHandler child) throws SAXParseException {
+ // TODO handle fragment host
+ }
+ });
+ }
+
+ protected void handleAttributes(Attributes atts) throws SAXException {
+ String symbolicname = atts.getValue(SYMBOLIC_NAME);
+ if (symbolicname == null) {
+ log(Message.MSG_ERR, "Resource with no symobilc name, skipping it.");
+ skip();
+ return;
+ }
+
+ String v = getOptionalAttribute(atts, VERSION, DEFAULT_VERSION);
+ Version version;
+ try {
+ version = new Version(v);
+ } catch (ParseException e) {
+ log(Message.MSG_ERR, "Incorrect resource version: " + v + ". The resource "
+ + symbolicname + " is then ignored.");
+ skip();
+ return;
+ }
+
+ bundleInfo = new BundleInfo(symbolicname, version);
+ bundleInfo.setPresentationName(atts.getValue(PRESENTATION_NAME));
+ String uri = atts.getValue(URI);
+ if (uri != null) {
+ try {
+ bundleInfo.addArtifact(new BundleArtifact(false, new URI(uri), null));
+ } catch (URISyntaxException e) {
+ log(Message.MSG_ERR, "Incorrect uri " + uri + ". The resource " + symbolicname
+ + " is then ignored.");
+ skip();
+ return;
+ }
+ }
+ bundleInfo.setId(atts.getValue(ID));
+ }
+
+ protected String getCurrentElementIdentifier() {
+ return bundleInfo.getSymbolicName() + "/" + bundleInfo.getVersion();
+ }
+
+ }
+
+ static class ResourceSourceHandler extends DelegatingHandler {
+
+ static final String SOURCE = "source";
+
+ public ResourceSourceHandler() {
+ super(SOURCE);
+ setBufferingChar(true);
+ }
+
+ }
+
+ static class ResourceDescriptionHandler extends DelegatingHandler {
+
+ static final String DESCRIPTION = "description";
+
+ public ResourceDescriptionHandler() {
+ super(DESCRIPTION);
+ setBufferingChar(true);
+ }
+
+ }
+
+ static class ResourceDocumentationHandler extends DelegatingHandler {
+
+ static final String DOCUMENTATION = "documentation";
+
+ public ResourceDocumentationHandler() {
+ super(DOCUMENTATION);
+ setBufferingChar(true);
+ }
+ }
+
+ static class ResourceLicenseHandler extends DelegatingHandler {
+
+ static final String LICENSE = "license";
+
+ public ResourceLicenseHandler() {
+ super(LICENSE);
+ setBufferingChar(true);
+ }
+
+ }
+
+ static class ResourceSizeHandler extends DelegatingHandler {
+
+ static final String SIZE = "size";
+
+ public ResourceSizeHandler() {
+ super(SIZE);
+ setBufferingChar(true);
+ }
+ }
+
+ static class CapabilityHandler extends DelegatingHandler {
+
+ static final String CAPABILITY = "capability";
+
+ static final String NAME = "name";
+
+ Capability capability;
+
+ public CapabilityHandler() {
+ super(CAPABILITY);
+ addChild(new CapabilityPropertyHandler(),
+ new ChildElementHandler<CapabilityPropertyHandler>() {
+ public void childHanlded(CapabilityPropertyHandler child) {
+ String name = child.name;
+ String value = child.value;
+ String type = child.type;
+
+ capability.addProperty(name, value, type);
+ }
+ });
+ }
+
+ protected void handleAttributes(Attributes atts) throws SAXException {
+ String name = getRequiredAttribute(atts, NAME);
+ capability = new Capability(name);
+ }
+
+ }
+
+ static class CapabilityPropertyHandler extends DelegatingHandler {
+
+ static final String CAPABILITY_PROPERTY = "p";
+
+ static final String NAME = "n";
+
+ static final String VALUE = "v";
+
+ static final String TYPE = "t";
+
+ String name;
+
+ String value;
+
+ String type;
+
+ public CapabilityPropertyHandler() {
+ super(CAPABILITY_PROPERTY);
+ }
+
+ protected void handleAttributes(Attributes atts) throws SAXException {
+ name = getRequiredAttribute(atts, NAME);
+ value = getRequiredAttribute(atts, VALUE);
+ type = atts.getValue(TYPE);
+ }
+ }
+
+ static class AbstractRequirementHandler extends DelegatingHandler {
+
+ static final String NAME = "name";
+
+ static final String OPTIONAL = "optional";
+
+ static final String MULTIPLE = "multiple";
+
+ static final String FILTER = "filter";
+
+ Requirement requirement;
+
+ OSGiFilter filter;
+
+ public AbstractRequirementHandler(String name) {
+ super(name);
+ }
+
+ protected void handleAttributes(Attributes atts) throws SAXException {
+ String name = getRequiredAttribute(atts, NAME);
+
+ String filterText = atts.getValue(FILTER);
+ filter = null;
+ if (filterText != null) {
+ try {
+ filter = OSGiFilterParser.parse(filterText);
+ } catch (ParseException e) {
+ throw new SAXParseException("Requirement with illformed filter: " + filterText,
+ getLocator());
+ }
+ }
+
+ Boolean optional = getOptionalBooleanAttribute(atts, OPTIONAL, null);
+ Boolean multiple = getOptionalBooleanAttribute(atts, MULTIPLE, null);
+
+ requirement = new Requirement(name, filter);
+ if (optional != null) {
+ requirement.setOptional(optional.booleanValue());
+ }
+ if (multiple != null) {
+ requirement.setMultiple(multiple.booleanValue());
+ }
+ }
+
+ }
+
+ static class RequireHandler extends AbstractRequirementHandler {
+
+ static final String REQUIRE = "require";
+
+ public RequireHandler() {
+ super(REQUIRE);
+ }
+
+ }
+
+ static class ExtendHandler extends AbstractRequirementHandler {
+
+ static final String EXTEND = "extend";
+
+ public ExtendHandler() {
+ super(EXTEND);
+ }
+
+ }
+}
diff --git a/src/java/org/apache/ivy/osgi/obr/xml/OBRXMLWriter.java b/src/java/org/apache/ivy/osgi/obr/xml/OBRXMLWriter.java
new file mode 100644
index 0000000..190b957
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/obr/xml/OBRXMLWriter.java
@@ -0,0 +1,288 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.obr.xml;
+
+import java.io.OutputStream;
+import java.text.ParseException;
+import java.util.Set;
+
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.sax.SAXTransformerFactory;
+import javax.xml.transform.sax.TransformerHandler;
+import javax.xml.transform.stream.StreamResult;
+
+import org.apache.ivy.core.IvyContext;
+import org.apache.ivy.osgi.core.BundleArtifact;
+import org.apache.ivy.osgi.core.BundleCapability;
+import org.apache.ivy.osgi.core.BundleInfo;
+import org.apache.ivy.osgi.core.BundleRequirement;
+import org.apache.ivy.osgi.core.ExportPackage;
+import org.apache.ivy.osgi.core.ManifestParser;
+import org.apache.ivy.osgi.obr.xml.OBRXMLParser.CapabilityHandler;
+import org.apache.ivy.osgi.obr.xml.OBRXMLParser.CapabilityPropertyHandler;
+import org.apache.ivy.osgi.obr.xml.OBRXMLParser.RepositoryHandler;
+import org.apache.ivy.osgi.obr.xml.OBRXMLParser.RequireHandler;
+import org.apache.ivy.osgi.obr.xml.OBRXMLParser.ResourceHandler;
+import org.apache.ivy.osgi.obr.xml.OBRXMLParser.ResourceSourceHandler;
+import org.apache.ivy.osgi.repo.ManifestAndLocation;
+import org.apache.ivy.osgi.util.Version;
+import org.apache.ivy.osgi.util.VersionRange;
+import org.apache.ivy.util.Message;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+public class OBRXMLWriter {
+
+ public static ContentHandler newHandler(OutputStream out, String encoding, boolean indent)
+ throws TransformerConfigurationException {
+ SAXTransformerFactory tf = (SAXTransformerFactory) SAXTransformerFactory.newInstance();
+ TransformerHandler hd = tf.newTransformerHandler();
+ Transformer serializer = tf.newTransformer();
+ StreamResult stream = new StreamResult(out);
+ serializer.setOutputProperty(OutputKeys.ENCODING, encoding);
+ serializer.setOutputProperty(OutputKeys.INDENT, indent ? "yes" : "no");
+ hd.setResult(stream);
+ return hd;
+ }
+
+ public static void writeManifests(Iterable<ManifestAndLocation> manifestAndLocations,
+ ContentHandler handler, boolean quiet) throws SAXException {
+ int level = quiet ? Message.MSG_DEBUG : Message.MSG_WARN;
+ handler.startDocument();
+ AttributesImpl atts = new AttributesImpl();
+ handler.startElement("", RepositoryHandler.REPOSITORY, RepositoryHandler.REPOSITORY, atts);
+ int nbOk = 0;
+ int nbRejected = 0;
+ for (ManifestAndLocation manifestAndLocation : manifestAndLocations) {
+ BundleInfo bundleInfo;
+ try {
+ bundleInfo = ManifestParser.parseManifest(manifestAndLocation.getManifest());
+ bundleInfo
+ .addArtifact(new BundleArtifact(false, manifestAndLocation.getUri(), null));
+ if (manifestAndLocation.getSourceURI() != null) {
+ bundleInfo.addArtifact(new BundleArtifact(true, manifestAndLocation
+ .getSourceURI(), null));
+ }
+ nbOk++;
+ } catch (ParseException e) {
+ nbRejected++;
+ IvyContext
+ .getContext()
+ .getMessageLogger()
+ .log("Rejected " + manifestAndLocation.getUri() + ": " + e.getMessage(),
+ level);
+ continue;
+ }
+ saxBundleInfo(bundleInfo, handler);
+ }
+ handler.endElement("", RepositoryHandler.REPOSITORY, RepositoryHandler.REPOSITORY);
+ handler.endDocument();
+ Message.info(nbOk + " bundle" + (nbOk > 1 ? "s" : "") + " added, " + nbRejected + " bundle"
+ + (nbRejected > 1 ? "s" : "") + " rejected.");
+ }
+
+ public static void writeBundles(Iterable<BundleInfo> bundleInfos, ContentHandler handler)
+ throws SAXException {
+ handler.startDocument();
+ AttributesImpl atts = new AttributesImpl();
+ handler.startElement("", RepositoryHandler.REPOSITORY, RepositoryHandler.REPOSITORY, atts);
+ for (BundleInfo bundleInfo : bundleInfos) {
+ saxBundleInfo(bundleInfo, handler);
+ }
+ handler.endElement("", RepositoryHandler.REPOSITORY, RepositoryHandler.REPOSITORY);
+ handler.endDocument();
+ }
+
+ private static void saxBundleInfo(BundleInfo bundleInfo, ContentHandler handler)
+ throws SAXException {
+ AttributesImpl atts = new AttributesImpl();
+ addAttr(atts, ResourceHandler.SYMBOLIC_NAME, bundleInfo.getSymbolicName());
+ addAttr(atts, ResourceHandler.VERSION, bundleInfo.getRawVersion());
+ for (BundleArtifact artifact : bundleInfo.getArtifacts()) {
+ if (!artifact.isSource()) {
+ addAttr(atts, ResourceHandler.URI, bundleInfo.getArtifacts().get(0).getUri()
+ .toString());
+ break;
+ }
+ }
+ handler.startElement("", ResourceHandler.RESOURCE, ResourceHandler.RESOURCE, atts);
+ for (BundleArtifact artifact : bundleInfo.getArtifacts()) {
+ if (artifact.isSource()) {
+ startElement(handler, ResourceSourceHandler.SOURCE);
+ characters(handler, artifact.getUri().toString());
+ endElement(handler, ResourceSourceHandler.SOURCE);
+ break;
+ }
+ }
+ for (BundleCapability capability : bundleInfo.getCapabilities()) {
+ saxCapability(capability, handler);
+ }
+ for (BundleRequirement requirement : bundleInfo.getRequirements()) {
+ saxRequirement(requirement, handler);
+ }
+ handler.endElement("", ResourceHandler.RESOURCE, ResourceHandler.RESOURCE);
+ handler.characters("\n".toCharArray(), 0, 1);
+ }
+
+ private static void saxCapability(BundleCapability capability, ContentHandler handler)
+ throws SAXException {
+ AttributesImpl atts = new AttributesImpl();
+ String type = capability.getType();
+ addAttr(atts, CapabilityHandler.NAME, type);
+ handler.startElement("", CapabilityHandler.CAPABILITY, CapabilityHandler.CAPABILITY, atts);
+ if (type.equals(BundleInfo.BUNDLE_TYPE)) {
+ // nothing to do, already handled with the resource tag
+ } else if (type.equals(BundleInfo.PACKAGE_TYPE)) {
+ saxCapabilityProperty("package", capability.getName(), handler);
+ Version v = capability.getRawVersion();
+ if (v != null) {
+ saxCapabilityProperty("version", v.toString(), handler);
+ }
+ Set<String> uses = ((ExportPackage) capability).getUses();
+ if (uses != null && !uses.isEmpty()) {
+ StringBuffer builder = new StringBuffer();
+ for (String use : uses) {
+ if (builder.length() != 0) {
+ builder.append(',');
+ }
+ builder.append(use);
+ }
+ saxCapabilityProperty("uses", builder.toString(), handler);
+ }
+ } else if (type.equals(BundleInfo.SERVICE_TYPE)) {
+ saxCapabilityProperty("service", capability.getName(), handler);
+ Version v = capability.getRawVersion();
+ if (v != null) {
+ saxCapabilityProperty("version", v.toString(), handler);
+ }
+ } else {
+ // oups
+ }
+ handler.endElement("", CapabilityHandler.CAPABILITY, CapabilityHandler.CAPABILITY);
+ handler.characters("\n".toCharArray(), 0, 1);
+ }
+
+ private static void saxCapabilityProperty(String n, String v, ContentHandler handler)
+ throws SAXException {
+ saxCapabilityProperty(n, null, v, handler);
+ }
+
+ private static void saxCapabilityProperty(String n, String t, String v, ContentHandler handler)
+ throws SAXException {
+ AttributesImpl atts = new AttributesImpl();
+ addAttr(atts, CapabilityPropertyHandler.NAME, n);
+ if (t != null) {
+ addAttr(atts, CapabilityPropertyHandler.TYPE, t);
+ }
+ addAttr(atts, CapabilityPropertyHandler.VALUE, v);
+ handler.startElement("", CapabilityPropertyHandler.CAPABILITY_PROPERTY,
+ CapabilityPropertyHandler.CAPABILITY_PROPERTY, atts);
+ handler.endElement("", CapabilityPropertyHandler.CAPABILITY_PROPERTY,
+ CapabilityPropertyHandler.CAPABILITY_PROPERTY);
+ handler.characters("\n".toCharArray(), 0, 1);
+ }
+
+ private static void saxRequirement(BundleRequirement requirement, ContentHandler handler)
+ throws SAXException {
+ AttributesImpl atts = new AttributesImpl();
+ addAttr(atts, RequireHandler.NAME, requirement.getType());
+ if ("optional".equals(requirement.getResolution())) {
+ addAttr(atts, RequireHandler.OPTIONAL, Boolean.TRUE.toString());
+ }
+ addAttr(atts, RequireHandler.FILTER, buildFilter(requirement));
+ handler.startElement("", RequireHandler.REQUIRE, RequireHandler.REQUIRE, atts);
+ handler.endElement("", RequireHandler.REQUIRE, RequireHandler.REQUIRE);
+ handler.characters("\n".toCharArray(), 0, 1);
+ }
+
+ private static String buildFilter(BundleRequirement requirement) {
+ StringBuffer filter = new StringBuffer();
+ VersionRange v = requirement.getVersion();
+ if (v != null) {
+ appendVersion(filter, v);
+ }
+ filter.append('(');
+ filter.append(requirement.getType());
+ filter.append("=");
+ filter.append(requirement.getName());
+ filter.append(')');
+ if (v != null) {
+ filter.append(')');
+ }
+ return filter.toString();
+ }
+
+ private static void appendVersion(StringBuffer filter, VersionRange v) {
+ filter.append("(&");
+ Version start = v.getStartVersion();
+ if (start != null) {
+ if (!v.isStartExclusive()) {
+ filter.append("(version>=");
+ filter.append(start.toString());
+ filter.append(')');
+ } else {
+ filter.append("(!");
+ filter.append("(version<=");
+ filter.append(start.toString());
+ filter.append("))");
+ }
+ }
+ Version end = v.getEndVersion();
+ if (end != null) {
+ if (!v.isEndExclusive()) {
+ filter.append("(version<=");
+ filter.append(end.toString());
+ filter.append(')');
+ } else {
+ filter.append("(!");
+ filter.append("(version>=");
+ filter.append(end.toString());
+ filter.append("))");
+ }
+ }
+ }
+
+ private static void addAttr(AttributesImpl atts, String name, String value) {
+ if (value != null) {
+ atts.addAttribute("", name, name, "CDATA", value);
+ }
+ }
+
+ private static void addAttr(AttributesImpl atts, String name, Object value) {
+ if (value != null) {
+ atts.addAttribute("", name, name, "CDATA", value.toString());
+ }
+ }
+
+ private static void startElement(ContentHandler handler, String name) throws SAXException {
+ handler.startElement("", name, name, new AttributesImpl());
+ }
+
+ private static void endElement(ContentHandler handler, String name) throws SAXException {
+ handler.endElement("", name, name);
+ }
+
+ private static void characters(ContentHandler handler, String value) throws SAXException {
+ char[] chars = value.toCharArray();
+ handler.characters(chars, 0, chars.length);
+ }
+
+}
diff --git a/src/java/org/apache/ivy/osgi/obr/xml/Requirement.java b/src/java/org/apache/ivy/osgi/obr/xml/Requirement.java
new file mode 100644
index 0000000..14f12c6
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/obr/xml/Requirement.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.obr.xml;
+
+import org.apache.ivy.osgi.filter.OSGiFilter;
+
+public class Requirement {
+
+ private final String name;
+
+ private boolean optional;
+
+ private final OSGiFilter filter;
+
+ private boolean multiple = false;
+
+ public Requirement(String name, OSGiFilter filter) {
+ this.name = name;
+ this.filter = filter;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public OSGiFilter getFilter() {
+ return filter;
+ }
+
+ public void setOptional(boolean optional) {
+ this.optional = optional;
+ }
+
+ public boolean isOptional() {
+ return optional;
+ }
+
+ public void setMultiple(boolean multiple) {
+ this.multiple = multiple;
+ }
+
+ public boolean isMultiple() {
+ return multiple;
+ }
+
+}
diff --git a/src/java/org/apache/ivy/osgi/obr/xml/RequirementAdapter.java b/src/java/org/apache/ivy/osgi/obr/xml/RequirementAdapter.java
new file mode 100644
index 0000000..0f9a68d
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/obr/xml/RequirementAdapter.java
@@ -0,0 +1,184 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.obr.xml;
+
+import java.text.ParseException;
+
+import org.apache.ivy.osgi.core.BundleInfo;
+import org.apache.ivy.osgi.core.BundleRequirement;
+import org.apache.ivy.osgi.filter.AndFilter;
+import org.apache.ivy.osgi.filter.CompareFilter;
+import org.apache.ivy.osgi.filter.CompareFilter.Operator;
+import org.apache.ivy.osgi.filter.NotFilter;
+import org.apache.ivy.osgi.filter.OSGiFilter;
+import org.apache.ivy.osgi.util.Version;
+import org.apache.ivy.osgi.util.VersionRange;
+
+public class RequirementAdapter {
+
+ private Version startVersion = null;
+
+ private boolean startExclusive = false;
+
+ private Version endVersion = null;
+
+ private boolean endExclusive = false;
+
+ private String type = null;
+
+ private String name = null;
+
+ public static void adapt(BundleInfo info, Requirement requirement)
+ throws UnsupportedFilterException, ParseException {
+ RequirementAdapter adapter = new RequirementAdapter();
+ adapter.extractFilter(requirement.getFilter());
+ adapter.adapt(info, requirement.isOptional());
+ }
+
+ private void extractFilter(OSGiFilter filter) throws UnsupportedFilterException, ParseException {
+ if (filter instanceof AndFilter) {
+ AndFilter andFilter = (AndFilter) filter;
+ for (OSGiFilter subFilter : andFilter.getSubFilters()) {
+ extractFilter(subFilter);
+ }
+ } else if (filter instanceof CompareFilter) {
+ CompareFilter compareFilter = ((CompareFilter) filter);
+ parseCompareFilter(compareFilter, false);
+ } else if (filter instanceof NotFilter) {
+ NotFilter notFilter = ((NotFilter) filter);
+ if (notFilter.getSubFilter() instanceof CompareFilter) {
+ CompareFilter compareFilter = ((CompareFilter) notFilter.getSubFilter());
+ parseCompareFilter(compareFilter, true);
+ }
+ } else {
+ throw new UnsupportedFilterException("Unsupported filter: "
+ + filter.getClass().getName());
+ }
+ }
+
+ private void adapt(BundleInfo info, boolean optional) throws ParseException {
+ VersionRange range = getVersionRange();
+ String resolution = optional ? "optional" : null;
+ if (type == null) {
+ throw new ParseException("No requirement actually specified", 0);
+ }
+ BundleRequirement requirement = new BundleRequirement(type, name, range, resolution);
+ info.addRequirement(requirement);
+ if (BundleInfo.EXECUTION_ENVIRONMENT_TYPE.equals(type)) {
+ info.addExecutionEnvironment(name);
+ }
+ }
+
+ private VersionRange getVersionRange() {
+ VersionRange range = null;
+ if (startVersion != null || endVersion != null) {
+ range = new VersionRange(startExclusive, startVersion, endExclusive, endVersion);
+ }
+ return range;
+ }
+
+ private void parseCompareFilter(CompareFilter compareFilter, boolean not)
+ throws UnsupportedFilterException, ParseException {
+ String att = compareFilter.getLeftValue();
+ if (BundleInfo.PACKAGE_TYPE.equals(att) || BundleInfo.BUNDLE_TYPE.equals(att)
+ || BundleInfo.EXECUTION_ENVIRONMENT_TYPE.equals(att) || "symbolicname".equals(att)
+ || BundleInfo.SERVICE_TYPE.equals(att)) {
+ if (not) {
+ throw new UnsupportedFilterException(
+ "Not filter on requirement comparaison is not supported");
+ }
+ if (type != null) {
+ throw new UnsupportedFilterException("Multiple requirement type are not supported");
+ }
+ if ("symbolicname".equals(att)) {
+ type = BundleInfo.BUNDLE_TYPE;
+ } else {
+ type = att;
+ }
+ if (compareFilter.getOperator() != Operator.EQUALS) {
+ throw new UnsupportedFilterException(
+ "Filtering is only supported with the operator '='");
+ }
+ name = compareFilter.getRightValue();
+ } else if ("version".equals(att)) {
+ String v = compareFilter.getRightValue();
+ Version version;
+ try {
+ version = new Version(v);
+ } catch (ParseException e) {
+ throw new ParseException("Ill formed version: " + v, 0);
+ }
+ Operator operator = compareFilter.getOperator();
+ if (not) {
+ if (operator == Operator.EQUALS) {
+ throw new UnsupportedFilterException(
+ "Not filter on equals comparaison is not supported");
+ } else if (operator == Operator.GREATER_OR_EQUAL) {
+ operator = Operator.LOWER_THAN;
+ } else if (operator == Operator.GREATER_THAN) {
+ operator = Operator.LOWER_OR_EQUAL;
+ } else if (operator == Operator.LOWER_OR_EQUAL) {
+ operator = Operator.GREATER_THAN;
+ } else if (operator == Operator.LOWER_THAN) {
+ operator = Operator.GREATER_OR_EQUAL;
+ }
+ }
+ if (operator == Operator.EQUALS) {
+ if (startVersion != null || endVersion != null) {
+ throw new UnsupportedFilterException(
+ "Multiple version matching is not supported");
+ }
+ startVersion = version;
+ startExclusive = false;
+ endVersion = version;
+ endExclusive = false;
+ } else if (operator == Operator.GREATER_OR_EQUAL) {
+ if (startVersion != null) {
+ throw new UnsupportedFilterException(
+ "Multiple version matching is not supported");
+ }
+ startVersion = version;
+ startExclusive = false;
+ } else if (operator == Operator.GREATER_THAN) {
+ if (startVersion != null) {
+ throw new UnsupportedFilterException(
+ "Multiple version matching is not supported");
+ }
+ startVersion = version;
+ startExclusive = true;
+ } else if (operator == Operator.LOWER_OR_EQUAL) {
+ if (endVersion != null) {
+ throw new UnsupportedFilterException(
+ "Multiple version matching is not supported");
+ }
+ endVersion = version;
+ endExclusive = false;
+ } else if (operator == Operator.LOWER_THAN) {
+ if (endVersion != null) {
+ throw new UnsupportedFilterException(
+ "Multiple version matching is not supported");
+ }
+ endVersion = version;
+ endExclusive = true;
+ }
+ } else {
+ throw new UnsupportedFilterException("Unsupported attribute: " + att);
+ }
+
+ }
+}
diff --git a/src/java/org/apache/ivy/osgi/obr/xml/UnsupportedFilterException.java b/src/java/org/apache/ivy/osgi/obr/xml/UnsupportedFilterException.java
new file mode 100644
index 0000000..3218bca
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/obr/xml/UnsupportedFilterException.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.obr.xml;
+
+public class UnsupportedFilterException extends Exception {
+
+ public UnsupportedFilterException(String message) {
+ super(message);
+ }
+}
diff --git a/src/java/org/apache/ivy/osgi/p2/P2Artifact.java b/src/java/org/apache/ivy/osgi/p2/P2Artifact.java
new file mode 100644
index 0000000..60d402d
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/p2/P2Artifact.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.p2;
+
+import org.apache.ivy.osgi.util.Version;
+
+public class P2Artifact {
+
+ private String id;
+
+ private Version version;
+
+ private String classifier;
+
+ public P2Artifact(String id, Version version, String classifier) {
+ this.id = id;
+ this.version = version;
+ this.classifier = classifier;
+ }
+
+ public String getClassifier() {
+ return classifier;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public Version getVersion() {
+ return version;
+ }
+
+ @Override
+ public String toString() {
+ return id + "@" + version + " (" + classifier + ")";
+ }
+
+}
diff --git a/src/java/org/apache/ivy/osgi/p2/P2ArtifactParser.java b/src/java/org/apache/ivy/osgi/p2/P2ArtifactParser.java
new file mode 100644
index 0000000..71d4d75
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/p2/P2ArtifactParser.java
@@ -0,0 +1,248 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.p2;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.text.ParseException;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.apache.ivy.osgi.filter.OSGiFilter;
+import org.apache.ivy.osgi.filter.OSGiFilterParser;
+import org.apache.ivy.osgi.p2.PropertiesParser.PropertiesHandler;
+import org.apache.ivy.osgi.util.DelegatingHandler;
+import org.apache.ivy.osgi.util.Version;
+import org.apache.ivy.util.XMLHelper;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
+public class P2ArtifactParser implements XMLInputParser {
+
+ private final P2Descriptor p2Descriptor;
+
+ private final String repoUrl;
+
+ public P2ArtifactParser(P2Descriptor p2Descriptor, String repoUrl) {
+ this.p2Descriptor = p2Descriptor;
+ this.repoUrl = repoUrl;
+ }
+
+ public void parse(InputStream in) throws ParseException, IOException, SAXException {
+ RepositoryHandler handler = new RepositoryHandler(p2Descriptor, repoUrl);
+ try {
+ XMLHelper.parse(in, null, handler, null);
+ } catch (ParserConfigurationException e) {
+ throw new SAXException(e);
+ }
+ }
+
+ private static class RepositoryHandler extends DelegatingHandler {
+
+ private static final String REPOSITORY = "repository";
+
+ // private static final String NAME = "name";
+ //
+ // private static final String TYPE = "type";
+ //
+ // private static final String VERSION = "version";
+
+ private Map<OSGiFilter, String> artifactPatterns = new LinkedHashMap<OSGiFilter, String>();
+
+ public RepositoryHandler(final P2Descriptor p2Descriptor, String repoUrl) {
+ super(REPOSITORY);
+ // addChild(new PropertiesHandler(), new ChildElementHandler<PropertiesHandler>() {
+ // public void childHanlded(PropertiesHandler child) {
+ // }
+ // });
+ addChild(new MappingsHandler(), new ChildElementHandler<MappingsHandler>() {
+ public void childHanlded(MappingsHandler child) {
+ for (Entry<String, String> entry : child.outputByFilter.entrySet()) {
+ OSGiFilter filter;
+ try {
+ filter = OSGiFilterParser.parse(entry.getKey());
+ } catch (ParseException e) {
+ throw new IllegalStateException();
+ }
+ artifactPatterns.put(filter, entry.getValue());
+
+ }
+ }
+ });
+ addChild(new ArtifactsHandler(p2Descriptor, artifactPatterns, repoUrl),
+ new ChildElementHandler<ArtifactsHandler>() {
+ public void childHanlded(ArtifactsHandler child) {
+ // nothing to do
+ }
+ });
+ }
+ // protected void handleAttributes(Attributes atts) {
+ // String name = atts.getValue(NAME);
+ // String type = atts.getValue(TYPE);
+ // String version = atts.getValue(VERSION);
+ // }
+ }
+
+ private static class MappingsHandler extends DelegatingHandler {
+
+ private static final String MAPPINGS = "mappings";
+
+ private static final String SIZE = "size";
+
+ Map<String, String> outputByFilter;
+
+ public MappingsHandler() {
+ super(MAPPINGS);
+ addChild(new RuleHandler(), new ChildElementHandler<RuleHandler>() {
+ public void childHanlded(RuleHandler child) {
+ outputByFilter.put(child.filter, child.output);
+ }
+ });
+ }
+
+ protected void handleAttributes(Attributes atts) {
+ int size = Integer.parseInt(atts.getValue(SIZE));
+ outputByFilter = new LinkedHashMap<String, String>(size);
+ }
+
+ }
+
+ private static class RuleHandler extends DelegatingHandler {
+
+ private static final String RULE = "rule";
+
+ private static final String FILTER = "filter";
+
+ private static final String OUTPUT = "output";
+
+ private String filter;
+
+ private String output;
+
+ public RuleHandler() {
+ super(RULE);
+ }
+
+ protected void handleAttributes(Attributes atts) {
+ filter = atts.getValue(FILTER);
+ output = atts.getValue(OUTPUT);
+ }
+
+ }
+
+ private static class ArtifactsHandler extends DelegatingHandler {
+
+ private static final String ARTIFACTS = "artifacts";
+
+ // private static final String SIZE = "size";
+
+ public ArtifactsHandler(final P2Descriptor p2Descriptor,
+ final Map<OSGiFilter, String> artifactPatterns, final String repoUrl) {
+ super(ARTIFACTS);
+ addChild(new ArtifactHandler(), new ChildElementHandler<ArtifactHandler>() {
+ public void childHanlded(ArtifactHandler child) throws SAXParseException {
+ String url = getPattern(child.p2Artifact, child.properties);
+ if (url != null) {
+ url = url.replaceAll("\\$\\{repoUrl\\}", repoUrl);
+ url = url.replaceAll("\\$\\{id\\}", child.p2Artifact.getId());
+ url = url.replaceAll("\\$\\{version\\}", child.p2Artifact.getVersion()
+ .toString());
+ URI uri;
+ try {
+ uri = new URL(url).toURI();
+ } catch (MalformedURLException e) {
+ throw new SAXParseException("Incorrect artifact url '" + url + "' ("
+ + e.getMessage() + ")", getLocator(), e);
+ } catch (URISyntaxException e) {
+ throw new SAXParseException("Incorrect artifact url '" + url + "' ("
+ + e.getMessage() + ")", getLocator(), e);
+ }
+ p2Descriptor.addArtifactUrl(child.p2Artifact.getClassifier(),
+ child.p2Artifact.getId(), child.p2Artifact.getVersion(), uri,
+ child.properties.get("format"));
+ }
+ }
+
+ private String getPattern(P2Artifact p2Artifact, Map<String, String> properties) {
+ Map<String, String> props = new HashMap<String, String>(properties);
+ props.put("classifier", p2Artifact.getClassifier());
+ for (Entry<OSGiFilter, String> pattern : artifactPatterns.entrySet()) {
+ if (pattern.getKey().eval(props)) {
+ return pattern.getValue();
+ }
+ }
+ return null;
+ }
+ });
+ }
+
+ // protected void handleAttributes(Attributes atts) {
+ // int size = Integer.parseInt(atts.getValue(SIZE));
+ // artifacts = new ArrayList(size);
+ // }
+
+ }
+
+ private static class ArtifactHandler extends DelegatingHandler {
+
+ private static final String ARTIFACT = "artifact";
+
+ private static final String CLASSIFIER = "classifier";
+
+ private static final String ID = "id";
+
+ private static final String VERSION = "version";
+
+ private P2Artifact p2Artifact;
+
+ private Map<String, String> properties;
+
+ public ArtifactHandler() {
+ super(ARTIFACT);
+ addChild(new PropertiesHandler(), new ChildElementHandler<PropertiesHandler>() {
+ public void childHanlded(PropertiesHandler child) {
+ properties = child.properties;
+ }
+ });
+ }
+
+ protected void handleAttributes(Attributes atts) throws SAXException {
+ String id = atts.getValue(ID);
+ Version version;
+ try {
+ version = new Version(atts.getValue(VERSION));
+ } catch (ParseException e) {
+ throw new SAXException("Incorrect version attribute on artifact '" + id + "': "
+ + atts.getValue(VERSION) + " (" + e.getMessage() + ")");
+ }
+ String classifier = atts.getValue(CLASSIFIER);
+
+ p2Artifact = new P2Artifact(id, version, classifier);
+ }
+ }
+
+}
diff --git a/src/java/org/apache/ivy/osgi/p2/P2CompositeParser.java b/src/java/org/apache/ivy/osgi/p2/P2CompositeParser.java
new file mode 100644
index 0000000..8b1b041
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/p2/P2CompositeParser.java
@@ -0,0 +1,123 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.p2;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.apache.ivy.osgi.util.DelegatingHandler;
+import org.apache.ivy.util.XMLHelper;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+public class P2CompositeParser implements XMLInputParser {
+
+ private Set<String> childLocations = new LinkedHashSet<String>();
+
+ public Set<String> getChildLocations() {
+ return childLocations;
+ }
+
+ public void parse(InputStream in) throws ParseException, IOException, SAXException {
+ RepositoryHandler handler = new RepositoryHandler();
+ try {
+ XMLHelper.parse(in, null, handler, null);
+ } catch (ParserConfigurationException e) {
+ throw new SAXException(e);
+ }
+ childLocations.addAll(handler.childLocations);
+ }
+
+ private static class RepositoryHandler extends DelegatingHandler {
+
+ private static final String REPOSITORY = "repository";
+
+ // private static final String NAME = "name";
+ //
+ // private static final String TYPE = "type";
+ //
+ // private static final String VERSION = "version";
+
+ List<String> childLocations = Collections.emptyList();
+
+ public RepositoryHandler() {
+ super(REPOSITORY);
+ addChild(new ChildrenHandler(), new ChildElementHandler<ChildrenHandler>() {
+ public void childHanlded(ChildrenHandler child) {
+ childLocations = child.childLocations;
+ }
+ });
+ }
+ // protected void handleAttributes(Attributes atts) {
+ // String name = atts.getValue(NAME);
+ // String type = atts.getValue(TYPE);
+ // String version = atts.getValue(VERSION);
+ // }
+ }
+
+ private static class ChildrenHandler extends DelegatingHandler {
+
+ private static final String CHILDREN = "children";
+
+ private static final String SIZE = "size";
+
+ List<String> childLocations;
+
+ public ChildrenHandler() {
+ super(CHILDREN);
+ addChild(new ChildHandler(), new ChildElementHandler<ChildHandler>() {
+ public void childHanlded(ChildHandler child) {
+ childLocations.add(child.location);
+ }
+ });
+ }
+
+ protected void handleAttributes(Attributes atts) {
+ int size = Integer.parseInt(atts.getValue(SIZE));
+ childLocations = new ArrayList<String>(size);
+ }
+
+ }
+
+ private static class ChildHandler extends DelegatingHandler {
+
+ private static final String CHILD = "child";
+
+ private static final String LOCATION = "location";
+
+ String location;
+
+ public ChildHandler() {
+ super(CHILD);
+ }
+
+ protected void handleAttributes(Attributes atts) {
+ location = atts.getValue(LOCATION);
+ }
+
+ }
+
+}
diff --git a/src/java/org/apache/ivy/osgi/p2/P2Descriptor.java b/src/java/org/apache/ivy/osgi/p2/P2Descriptor.java
new file mode 100644
index 0000000..7dbfe32
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/p2/P2Descriptor.java
@@ -0,0 +1,152 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.p2;
+
+import java.net.URI;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.ivy.osgi.core.BundleArtifact;
+import org.apache.ivy.osgi.core.BundleInfo;
+import org.apache.ivy.osgi.core.ExecutionEnvironmentProfileProvider;
+import org.apache.ivy.osgi.repo.EditableRepoDescriptor;
+import org.apache.ivy.osgi.repo.ModuleDescriptorWrapper;
+import org.apache.ivy.osgi.util.Version;
+import org.apache.ivy.util.Message;
+
+public class P2Descriptor extends EditableRepoDescriptor {
+
+ private Map<String, Map<Version, BundleInfo>> sourceTargetBundles = new HashMap<String, Map<Version, BundleInfo>>();
+
+ private Map<String, Map<Version, BundleInfo>> sourceBundles = new HashMap<String, Map<Version, BundleInfo>>();
+
+ public P2Descriptor(URI repoUri, ExecutionEnvironmentProfileProvider profileProvider) {
+ super(repoUri, profileProvider);
+ }
+
+ public void addBundle(BundleInfo bundleInfo) {
+ if (bundleInfo.isSource()) {
+ if (bundleInfo.getSymbolicNameTarget() == null || bundleInfo.getVersionTarget() == null) {
+ if (getLogLevel() <= Message.MSG_VERBOSE) {
+ Message.verbose("The source bundle " + bundleInfo.getSymbolicName()
+ + " did not declare its target. Ignoring it");
+ }
+ return;
+ }
+ Map<Version, BundleInfo> byVersion = sourceBundles.get(bundleInfo.getSymbolicName());
+ if (byVersion == null) {
+ byVersion = new HashMap<Version, BundleInfo>();
+ sourceBundles.put(bundleInfo.getSymbolicName(), byVersion);
+ }
+ byVersion.put(bundleInfo.getVersion(), bundleInfo);
+
+ Map<Version, BundleInfo> byTargetVersion = sourceTargetBundles.get(bundleInfo
+ .getSymbolicNameTarget());
+ if (byTargetVersion == null) {
+ byTargetVersion = new HashMap<Version, BundleInfo>();
+ sourceTargetBundles.put(bundleInfo.getSymbolicNameTarget(), byTargetVersion);
+ }
+ BundleInfo old = byTargetVersion.put(bundleInfo.getVersionTarget(), bundleInfo);
+ if (old != null && !old.equals(bundleInfo)) {
+ if (getLogLevel() <= Message.MSG_VERBOSE) {
+ Message.verbose("Duplicate source for the bundle "
+ + bundleInfo.getSymbolicNameTarget() + "@"
+ + bundleInfo.getVersionTarget() + " : " + bundleInfo + " is replacing "
+ + old);
+ }
+ }
+ return;
+ }
+
+ super.addBundle(bundleInfo);
+ }
+
+ public void finish() {
+ sourceBundles = null;
+ Set<String> bundleIds = getCapabilityValues(BundleInfo.BUNDLE_TYPE);
+ if (bundleIds == null) {
+ return;
+ }
+ for (String bundleId : bundleIds) {
+ Set<ModuleDescriptorWrapper> modules = findModules(BundleInfo.BUNDLE_TYPE, bundleId);
+ for (ModuleDescriptorWrapper mdw : modules) {
+ String symbolicName = mdw.getBundleInfo().getSymbolicName();
+ Map<Version, BundleInfo> byVersion = sourceTargetBundles.get(symbolicName);
+ if (byVersion == null) {
+ continue;
+ }
+ BundleInfo source = byVersion.get(mdw.getBundleInfo().getVersion());
+ if (source == null) {
+ continue;
+ }
+ for (BundleArtifact artifact : source.getArtifacts()) {
+ mdw.getBundleInfo().addArtifact(artifact);
+ }
+ }
+ }
+ sourceTargetBundles = null;
+ }
+
+ public void addArtifactUrl(String classifier, String id, Version version, URI uri, String format) {
+ if (!classifier.equals("osgi.bundle")) {
+ // we only support OSGi bundle, no Eclipse feature or anything else
+ return;
+ }
+ ModuleDescriptorWrapper module = findModule(id, version);
+ if (module != null) {
+ addArtifact(module.getBundleInfo(), new BundleArtifact(false, uri, format));
+ return;
+ }
+
+ // not found in the regular bundle. Let's look up in the source ones
+ Map<Version, BundleInfo> byVersion = sourceBundles.get(id);
+ if (byVersion == null) {
+ return;
+ }
+ BundleInfo source = byVersion.get(version);
+ if (source == null) {
+ return;
+ }
+ addArtifact(source, new BundleArtifact(true, uri, format));
+ }
+
+ private void addArtifact(BundleInfo bundle, BundleArtifact artifact) {
+ // find an existing artifact that might be a duplicate
+ BundleArtifact same = null;
+ for (BundleArtifact a : bundle.getArtifacts()) {
+ if (a.isSource() == artifact.isSource()) {
+ same = a;
+ break;
+ }
+ }
+
+ BundleArtifact best = artifact;
+
+ if (same != null) {
+ // we have two artifacts for the same bundle, let's choose a "packed" one
+ if (artifact.getFormat() == null || same.getFormat() != null) {
+ // the new one cannot be better
+ return;
+ }
+ bundle.removeArtifact(same);
+ }
+
+ bundle.addArtifact(best);
+ }
+}
diff --git a/src/java/org/apache/ivy/osgi/p2/P2MetadataParser.java b/src/java/org/apache/ivy/osgi/p2/P2MetadataParser.java
new file mode 100644
index 0000000..42b4b84
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/p2/P2MetadataParser.java
@@ -0,0 +1,922 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.p2;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.apache.ivy.osgi.core.BundleCapability;
+import org.apache.ivy.osgi.core.BundleInfo;
+import org.apache.ivy.osgi.core.BundleRequirement;
+import org.apache.ivy.osgi.core.ExportPackage;
+import org.apache.ivy.osgi.core.ManifestParser;
+import org.apache.ivy.osgi.p2.PropertiesParser.PropertiesHandler;
+import org.apache.ivy.osgi.util.DelegatingHandler;
+import org.apache.ivy.osgi.util.Version;
+import org.apache.ivy.osgi.util.VersionRange;
+import org.apache.ivy.util.Message;
+import org.apache.ivy.util.XMLHelper;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
+public class P2MetadataParser implements XMLInputParser {
+
+ private final P2Descriptor p2Descriptor;
+
+ private int logLevel = Message.MSG_INFO;
+
+ public P2MetadataParser(P2Descriptor p2Descriptor) {
+ this.p2Descriptor = p2Descriptor;
+ }
+
+ public void setLogLevel(int logLevel) {
+ this.logLevel = logLevel;
+ }
+
+ public void parse(InputStream in) throws ParseException, IOException, SAXException {
+ RepositoryHandler handler = new RepositoryHandler(p2Descriptor);
+ try {
+ XMLHelper.parse(in, null, handler, null);
+ } catch (ParserConfigurationException e) {
+ throw new SAXException(e);
+ }
+ }
+
+ private class RepositoryHandler extends DelegatingHandler {
+
+ // private static final String P2_TIMESTAMP = "p2.timestamp";
+
+ private static final String REPOSITORY = "repository";
+
+ // private static final String NAME = "name";
+ //
+ // private static final String TYPE = "type";
+ //
+ // private static final String VERSION = "version";
+ //
+ // private static final String DESCRIPTION = "description";
+ //
+ // private static final String PROVIDER = "provider";
+
+ public RepositoryHandler(final P2Descriptor p2Descriptor) {
+ super(REPOSITORY);
+ // addChild(new PropertiesHandler(P2_TIMESTAMP),
+ // new ChildElementHandler<PropertiesHandler>() {
+ // public void childHanlded(PropertiesHandler child) {
+ // String timestamp = child.properties.get(P2_TIMESTAMP);
+ // if (timestamp != null) {
+ // p2Descriptor.setTimestamp(Long.parseLong(timestamp));
+ // }
+ // }
+ // });
+ addChild(new UnitsHandler(), new ChildElementHandler<UnitsHandler>() {
+ public void childHanlded(UnitsHandler child) {
+ for (BundleInfo bundle : child.bundles) {
+ p2Descriptor.addBundle(bundle);
+ }
+ }
+ });
+ addChild(new ReferencesHandler(), new ChildElementHandler<ReferencesHandler>() {
+ public void childHanlded(ReferencesHandler child) {
+ }
+ });
+ }
+
+ // protected void handleAttributes(Attributes atts) {
+ // String name = atts.getValue(NAME);
+ // String type = atts.getValue(TYPE);
+ // String version = atts.getValue(VERSION);
+ // String description = atts.getValue(DESCRIPTION);
+ // String provider = atts.getValue(PROVIDER);
+ // }
+ }
+
+ private class ReferencesHandler extends DelegatingHandler {
+
+ private static final String REFERENCES = "references";
+
+ private static final String SIZE = "size";
+
+ List<URI> repositoryUris;
+
+ public ReferencesHandler() {
+ super(REFERENCES);
+ addChild(new RepositoryReferenceHandler(),
+ new ChildElementHandler<RepositoryReferenceHandler>() {
+ public void childHanlded(RepositoryReferenceHandler child) {
+ repositoryUris.add(child.uri);
+ }
+ });
+ }
+
+ protected void handleAttributes(Attributes atts) throws SAXException {
+ int size = Integer.parseInt(atts.getValue(SIZE));
+ repositoryUris = new ArrayList<URI>(size);
+ }
+ }
+
+ private class RepositoryReferenceHandler extends DelegatingHandler {
+
+ private static final String REPOSITORY = "repository";
+
+ // private static final String TYPE = "type";
+ //
+ // private static final String OPTIONS = "options";
+ //
+ // private static final String NAME = "name";
+
+ private static final String URI = "uri";
+
+ private static final String URL = "url";
+
+ public RepositoryReferenceHandler() {
+ super(REPOSITORY);
+ }
+
+ // int type;
+ //
+ // int options;
+ //
+ // String name;
+
+ URI uri;
+
+ protected void handleAttributes(Attributes atts) throws SAXException {
+ // type = Integer.parseInt(atts.getValue(TYPE));
+ // options = Integer.parseInt(atts.getValue(OPTIONS));
+ // name = atts.getValue(NAME);
+
+ String uriAtt = atts.getValue(URI);
+ String urlAtt = atts.getValue(URL);
+
+ if (uriAtt != null) {
+ try {
+ uri = new URI(uriAtt);
+ } catch (URISyntaxException e) {
+ throw new SAXParseException("Invalid uri attribute " + uriAtt + "("
+ + e.getMessage() + ")", getLocator());
+ }
+ }
+ if (uri != null && urlAtt != null) {
+ try {
+ uri = new URI(urlAtt);
+ } catch (URISyntaxException e) {
+ throw new SAXParseException("Invalid url attribute " + urlAtt + "("
+ + e.getMessage() + ")", getLocator());
+ }
+ }
+ }
+ }
+
+ private class UnitsHandler extends DelegatingHandler {
+
+ private static final String UNITS = "units";
+
+ private static final String SIZE = "size";
+
+ List<BundleInfo> bundles;
+
+ public UnitsHandler() {
+ super(UNITS);
+ addChild(new UnitHandler(), new ChildElementHandler<UnitHandler>() {
+ public void childHanlded(UnitHandler child) {
+ if (child.bundleInfo != null && !child.bundleInfo.getCapabilities().isEmpty()) {
+ bundles.add(child.bundleInfo);
+ }
+ }
+ });
+ }
+
+ protected void handleAttributes(Attributes atts) {
+ int size = Integer.parseInt(atts.getValue(SIZE));
+ bundles = new ArrayList<BundleInfo>(size);
+ }
+
+ }
+
+ class UnitHandler extends DelegatingHandler {
+
+ private static final String CATEGORY_PROPERTY = "org.eclipse.equinox.p2.type.category";
+
+ private static final String UNIT = "unit";
+
+ private static final String ID = "id";
+
+ private static final String VERSION = "version";
+
+ // private static final String SINGLETON = "singleton";
+
+ BundleInfo bundleInfo;
+
+ public UnitHandler() {
+ super(UNIT);
+ // addChild(new UpdateHandler(), new ChildElementHandler() {
+ // public void childHanlded(DelegetingHandler child) {
+ // }
+ // });
+ addChild(new PropertiesHandler(CATEGORY_PROPERTY),
+ new ChildElementHandler<PropertiesHandler>() {
+ public void childHanlded(PropertiesHandler child) {
+ String category = child.properties.get(CATEGORY_PROPERTY);
+ if (category != null && Boolean.valueOf(category).booleanValue()) {
+ // this is a category definition, this is useless, skip this unit
+ child.getParent().skip();
+ bundleInfo = null;
+ }
+ }
+ });
+ addChild(new ProvidesHandler(), new ChildElementHandler<ProvidesHandler>() {
+ public void childHanlded(ProvidesHandler child) {
+ if ("source".equals(child.eclipseType)) {
+ // this is some source of some bundle
+ bundleInfo.setSource(true);
+ // we need to parse the manifest in the toupointData to figure out the
+ // targeted bundle
+ // in case we won't have the proper data in the manifest, prepare the source
+ // data from the convention
+ String symbolicName = bundleInfo.getSymbolicName();
+ if (symbolicName.endsWith(".source")) {
+ bundleInfo.setSymbolicNameTarget(symbolicName.substring(0,
+ symbolicName.length() - 7));
+ bundleInfo.setVersionTarget(bundleInfo.getVersion());
+ }
+ }
+ for (BundleCapability capability : child.capabilities) {
+ bundleInfo.addCapability(capability);
+ }
+ }
+ });
+ addChild(new FilterHandler(), new ChildElementHandler<FilterHandler>() {
+ public void childHanlded(FilterHandler child) {
+ }
+ });
+ addChild(new RequiresHandler(), new ChildElementHandler<RequiresHandler>() {
+ public void childHanlded(RequiresHandler child) {
+ for (BundleRequirement requirement : child.requirements) {
+ bundleInfo.addRequirement(requirement);
+ }
+ }
+ });
+ addChild(new HostRequirementsHandler(),
+ new ChildElementHandler<HostRequirementsHandler>() {
+ public void childHanlded(HostRequirementsHandler child) {
+ }
+ });
+ addChild(new MetaRequirementsHandler(),
+ new ChildElementHandler<MetaRequirementsHandler>() {
+ public void childHanlded(MetaRequirementsHandler child) {
+ }
+ });
+ addChild(new ArtifactsHandler(), new ChildElementHandler<ArtifactsHandler>() {
+ public void childHanlded(ArtifactsHandler child) {
+ }
+ });
+ // addChild(new TouchpointHandler(), new ChildElementHandler() {
+ // public void childHanlded(DelegetingHandler child) {
+ // }
+ // });
+ addChild(new TouchpointDataHandler(), new ChildElementHandler<TouchpointDataHandler>() {
+ public void childHanlded(TouchpointDataHandler child) throws SAXParseException {
+ if (child.zipped != null) {
+ bundleInfo.setHasInnerClasspath(child.zipped.booleanValue());
+ }
+ if (!bundleInfo.isSource()) {
+ // we only care about parsing the manifest if it is a source
+ return;
+ }
+ if (child.manifest != null) {
+ // Eclipse may have serialized a little bit weirdly
+ String manifest = ManifestParser.formatLines(child.manifest.trim());
+ BundleInfo embeddedInfo;
+ try {
+ embeddedInfo = ManifestParser.parseManifest(manifest);
+ } catch (IOException e) {
+ if (logLevel >= Message.MSG_VERBOSE) {
+ Message.verbose(
+ "The Manifest of the source bundle "
+ + bundleInfo.getSymbolicName() + " could not be parsed",
+ e);
+ }
+ return;
+ } catch (ParseException e) {
+ if (logLevel >= Message.MSG_VERBOSE) {
+ Message.verbose(
+ "The Manifest of the source bundle "
+ + bundleInfo.getSymbolicName() + " is ill formed", e);
+ }
+ return;
+ }
+ if (!embeddedInfo.isSource()) {
+ if (logLevel >= Message.MSG_VERBOSE) {
+ Message.verbose("The Manifest of the source bundle "
+ + bundleInfo.getSymbolicName()
+ + " is not declaring being a source.");
+ }
+ return;
+ }
+ String symbolicNameTarget = embeddedInfo.getSymbolicNameTarget();
+ if (symbolicNameTarget == null) {
+ if (logLevel >= Message.MSG_VERBOSE) {
+ Message.verbose("The Manifest of the source bundle "
+ + bundleInfo.getSymbolicName()
+ + " is not declaring a target symbolic name.");
+ }
+ return;
+ }
+ Version versionTarget = embeddedInfo.getVersionTarget();
+ if (versionTarget == null) {
+ if (logLevel >= Message.MSG_VERBOSE) {
+ Message.verbose("The Manifest of the source bundle "
+ + bundleInfo.getSymbolicName()
+ + " is not declaring a target version.");
+ }
+ return;
+ }
+ bundleInfo.setSymbolicNameTarget(symbolicNameTarget);
+ bundleInfo.setVersionTarget(versionTarget);
+ }
+ }
+ });
+ // addChild(new LicensesHandler(), new ChildElementHandler() {
+ // public void childHanlded(DelegetingHandler child) {
+ // }
+ // });
+ // addChild(new CopyrightHandler(), new ChildElementHandler() {
+ // public void childHanlded(DelegetingHandler child) {
+ // }
+ // });
+ // addChild(new ChangesHandler(), new ChildElementHandler() {
+ // public void childHanlded(DelegetingHandler child) {
+ // }
+ // });
+
+ }
+
+ protected void handleAttributes(Attributes atts) throws SAXException {
+ String id = atts.getValue(ID);
+ String version = atts.getValue(VERSION);
+ // Boolean singleton = Boolean.valueOf(atts.getValue(SINGLETON));
+ try {
+ bundleInfo = new BundleInfo(id, new Version(version));
+ } catch (ParseException e) {
+ throw new SAXException("Incorrect version on bundle '" + id + "': " + version
+ + " (" + e.getMessage() + ")");
+ }
+ }
+
+ }
+
+ // static class UpdateHandler extends DelegetingHandler {
+ //
+ // private static final String UPDATE = "update";
+ //
+ // private static final String ID = "id";
+ //
+ // private static final String RANGE = "range";
+ //
+ // private static final String SEVERITY = "severity";
+ //
+ // public UpdateHandler() {
+ // super(UPDATE);
+ // }
+ //
+ // protected void handleAttributes(Attributes atts) {
+ // String id = atts.getValue(ID);
+ // String range = atts.getValue(RANGE);
+ // String severity = atts.getValue(SEVERITY);
+ // }
+ //
+ // }
+
+ private static class FilterHandler extends DelegatingHandler {
+
+ private static final String FILTER = "filter";
+
+ public FilterHandler() {
+ super(FILTER);
+ setBufferingChar(true);
+ }
+
+ }
+
+ private static String namespace2Type(String namespace) {
+ if (namespace.equals("java.package")) {
+ return BundleInfo.PACKAGE_TYPE;
+ }
+ if (namespace.equals("osgi.bundle")) {
+ return BundleInfo.BUNDLE_TYPE;
+ }
+ return null;
+ }
+
+ private class ProvidesHandler extends DelegatingHandler {
+
+ private static final String PROVIDES = "provides";
+
+ private static final String SIZE = "size";
+
+ List<BundleCapability> capabilities;
+
+ String eclipseType;
+
+ public ProvidesHandler() {
+ super(PROVIDES);
+ addChild(new ProvidedHandler(), new ChildElementHandler<ProvidedHandler>() {
+ public void childHanlded(ProvidedHandler child) {
+ if (child.namespace.equals("org.eclipse.equinox.p2.eclipse.type")) {
+ eclipseType = child.name;
+ } else {
+ String type = namespace2Type(child.namespace);
+ if (type == null) {
+ if (logLevel >= Message.MSG_DEBUG) {
+ Message.debug("Unsupported provided capability " + child.namespace
+ + " " + child.name + " " + child.version);
+ }
+ return;
+ }
+ BundleCapability capability;
+ if (type == BundleInfo.PACKAGE_TYPE) {
+ capability = new ExportPackage(child.name, child.version);
+ } else {
+ capability = new BundleCapability(type, child.name, child.version);
+ }
+ capabilities.add(capability);
+ }
+ }
+ });
+ }
+
+ protected void handleAttributes(Attributes atts) {
+ int size = Integer.parseInt(atts.getValue(SIZE));
+ capabilities = new ArrayList<BundleCapability>(size);
+ }
+
+ }
+
+ private static class ProvidedHandler extends DelegatingHandler {
+
+ private static final String PROVIDED = "provided";
+
+ private static final String NAMESPACE = "namespace";
+
+ private static final String NAME = "name";
+
+ private static final String VERSION = "version";
+
+ String namespace;
+
+ String name;
+
+ Version version;
+
+ public ProvidedHandler() {
+ super(PROVIDED);
+ }
+
+ protected void handleAttributes(Attributes atts) throws SAXException {
+ namespace = atts.getValue(NAMESPACE);
+ name = atts.getValue(NAME);
+ try {
+ version = new Version(atts.getValue(VERSION));
+ } catch (ParseException e) {
+ throw new SAXException("Incorrect version on provided capability: " + version
+ + " (" + e.getMessage() + ")");
+ }
+ }
+
+ }
+
+ abstract class AbstractRequirementHandler extends DelegatingHandler {
+
+ private static final String SIZE = "size";
+
+ List<BundleRequirement> requirements;
+
+ public AbstractRequirementHandler(String name) {
+ super(name);
+ addChild(new RequiredHandler(), new ChildElementHandler<RequiredHandler>() {
+ public void childHanlded(RequiredHandler child) {
+ String name = child.name;
+ VersionRange range = child.range;
+ String type = namespace2Type(child.namespace);
+ if (type == null) {
+ if (logLevel >= Message.MSG_DEBUG) {
+ Message.debug("Unsupported required capability " + child.namespace
+ + " " + name + " " + range);
+ }
+ } else {
+ String resolution = child.optional ? "optional" : null;
+ requirements.add(new BundleRequirement(type, name, range, resolution));
+ }
+ }
+ });
+ }
+
+ protected void handleAttributes(Attributes atts) {
+ int size = Integer.parseInt(atts.getValue(SIZE));
+ requirements = new ArrayList<BundleRequirement>(size);
+ }
+
+ }
+
+ private class RequiresHandler extends AbstractRequirementHandler {
+
+ private static final String REQUIRES = "requires";
+
+ public RequiresHandler() {
+ super(REQUIRES);
+ }
+
+ }
+
+ private class RequiredHandler extends DelegatingHandler {
+
+ private static final String REQUIRED = "required";
+
+ private static final String NAMESPACE = "namespace";
+
+ private static final String NAME = "name";
+
+ private static final String RANGE = "range";
+
+ private static final String OPTIONAL = "optional";
+
+ // private static final String GREEDY = "greedy";
+
+ String namespace;
+
+ String name;
+
+ VersionRange range;
+
+ // String filter;
+ //
+ // boolean greedy;
+
+ boolean optional;
+
+ public RequiredHandler() {
+ super(REQUIRED);
+ // addChild(new FilterHandler(), new ChildElementHandler<FilterHandler>() {
+ // public void childHanlded(FilterHandler child) {
+ // filter = child.getBufferedChars().trim();
+ // }
+ // });
+ }
+
+ protected void handleAttributes(Attributes atts) throws SAXParseException {
+ namespace = atts.getValue(NAMESPACE);
+ name = atts.getValue(NAME);
+ try {
+ range = new VersionRange(atts.getValue(RANGE));
+ } catch (ParseException e) {
+ throw new RuntimeException(e);
+ }
+ // greedy = getOptionalBooleanAttribute(atts, GREEDY, Boolean.TRUE).booleanValue();
+ optional = getOptionalBooleanAttribute(atts, OPTIONAL, Boolean.FALSE).booleanValue();
+ }
+
+ }
+
+ private class HostRequirementsHandler extends AbstractRequirementHandler {
+
+ private static final String HOST_REQUIREMENTS = "hostRequirements";
+
+ public HostRequirementsHandler() {
+ super(HOST_REQUIREMENTS);
+ }
+
+ }
+
+ private class MetaRequirementsHandler extends AbstractRequirementHandler {
+
+ private static final String META_REQUIREMENTS = "metaRequirements";
+
+ public MetaRequirementsHandler() {
+ super(META_REQUIREMENTS);
+ }
+
+ }
+
+ private class ArtifactsHandler extends DelegatingHandler {
+
+ private static final String ARTIFACTS = "artifacts";
+
+ private static final String SIZE = "size";
+
+ List<P2Artifact> artifacts;
+
+ public ArtifactsHandler() {
+ super(ARTIFACTS);
+ addChild(new ArtifactHandler(), new ChildElementHandler<ArtifactHandler>() {
+ public void childHanlded(ArtifactHandler child) {
+ artifacts.add(child.artifact);
+ }
+ });
+ }
+
+ protected void handleAttributes(Attributes atts) {
+ int size = Integer.parseInt(atts.getValue(SIZE));
+ artifacts = new ArrayList<P2Artifact>(size);
+ }
+
+ }
+
+ private class ArtifactHandler extends DelegatingHandler {
+
+ private static final String ARTIFACT = "artifact";
+
+ private static final String ID = "id";
+
+ private static final String VERSION = "version";
+
+ private static final String CLASSIFIER = "classifier";
+
+ P2Artifact artifact;
+
+ public ArtifactHandler() {
+ super(ARTIFACT);
+ }
+
+ protected void handleAttributes(Attributes atts) throws SAXException {
+ String id = atts.getValue(ID);
+ String version = atts.getValue(VERSION);
+ String classifier = atts.getValue(CLASSIFIER);
+ try {
+ artifact = new P2Artifact(id, new Version(version), classifier);
+ } catch (ParseException e) {
+ throw new SAXException("Incorrect version on artifact '" + id + "': " + version
+ + " (" + e.getMessage() + ")");
+ }
+ }
+
+ }
+
+ // private static class TouchpointHandler extends DelegetingHandler {
+ //
+ // private static final String TOUCHPOINT = "touchpoint";
+ //
+ // private static final String ID = "id";
+ //
+ // private static final String VERSION = "version";
+ //
+ // public TouchpointHandler() {
+ // super(TOUCHPOINT);
+ // }
+ //
+ // protected void handleAttributes(Attributes atts) {
+ // String id = atts.getValue(ID);
+ // String version = atts.getValue(VERSION);
+ // }
+ //
+ // }
+
+ private class TouchpointDataHandler extends DelegatingHandler {
+
+ private static final String TOUCHPOINTDATA = "touchpointData";
+
+ // private static final String SIZE = "size";
+
+ String manifest;
+
+ Boolean zipped;
+
+ public TouchpointDataHandler() {
+ super(TOUCHPOINTDATA);
+ addChild(new InstructionsHandler(), new ChildElementHandler<InstructionsHandler>() {
+ public void childHanlded(InstructionsHandler child) {
+ manifest = child.manifest;
+ zipped = child.zipped;
+ }
+ });
+ }
+
+ protected void handleAttributes(Attributes atts) {
+ // String size = atts.getValue(SIZE);
+ }
+
+ }
+
+ private class InstructionsHandler extends DelegatingHandler {
+
+ private static final String INSTRUCTIONS = "instructions";
+
+ // private static final String SIZE = "size";
+
+ String manifest;
+
+ Boolean zipped;
+
+ public InstructionsHandler() {
+ super(INSTRUCTIONS);
+ addChild(new InstructionHandler(), new ChildElementHandler<InstructionHandler>() {
+ public void childHanlded(InstructionHandler child) {
+ manifest = null;
+ zipped = null;
+ String buffer = child.getBufferedChars().trim();
+ if ("manifest".equals(child.key)) {
+ manifest = buffer;
+ } else if ("zipped".equals(child.key) && buffer.length() != 0) {
+ zipped = Boolean.valueOf(buffer);
+ }
+ }
+ });
+ }
+
+ protected void handleAttributes(Attributes atts) {
+ // String size = atts.getValue(SIZE);
+ }
+
+ }
+
+ private class InstructionHandler extends DelegatingHandler {
+
+ private static final String INSTRUCTION = "instruction";
+
+ private static final String KEY = "key";
+
+ String key;
+
+ public InstructionHandler() {
+ super(INSTRUCTION);
+ setBufferingChar(true);
+ }
+
+ protected void handleAttributes(Attributes atts) {
+ key = atts.getValue(KEY);
+ }
+
+ }
+
+ // private static class LicensesHandler extends DelegetingHandler {
+ //
+ // private static final String LICENSES = "licenses";
+ //
+ // private static final String SIZE = "size";
+ //
+ // public LicensesHandler() {
+ // super(LICENSES);
+ // addChild(new LicenseHandler(), new ChildElementHandler() {
+ // public void childHanlded(DelegetingHandler child) {
+ // }
+ // });
+ // }
+ //
+ // protected void handleAttributes(Attributes atts) {
+ // String size = atts.getValue(SIZE);
+ // }
+ //
+ // }
+
+ // private static class LicenseHandler extends DelegetingHandler {
+ //
+ // private static final String LICENSE = "license";
+ //
+ // private static final String URI = "uri";
+ //
+ // private static final String URL = "url";
+ //
+ // public LicenseHandler() {
+ // super(LICENSE);
+ // setBufferingChar(true);
+ // }
+ //
+ // protected void handleAttributes(Attributes atts) {
+ // String uri = atts.getValue(URI);
+ // String url = atts.getValue(URL);
+ // }
+ //
+ // }
+
+ // private static class CopyrightHandler extends DelegetingHandler {
+ //
+ // private static final String COPYRIGHT = "copyright";
+ //
+ // private static final String URI = "uri";
+ //
+ // private static final String URL = "url";
+ //
+ // public CopyrightHandler() {
+ // super(COPYRIGHT);
+ // }
+ //
+ // protected void handleAttributes(Attributes atts) {
+ // String uri = atts.getValue(URI);
+ // String url = atts.getValue(URL);
+ // }
+ //
+ // }
+
+ // private class ChangesHandler extends DelegetingHandler {
+ //
+ // private static final String CHANGES = "changes";
+ //
+ // private static final String SIZE = "size";
+ //
+ // public ChangesHandler() {
+ // super(CHANGES);
+ // addChild(new ChangeHandler(), new ChildElementHandler<ChangeHandler>() {
+ // public void childHanlded(ChangeHandler child) {
+ // }
+ // });
+ // }
+ //
+ // protected void handleAttributes(Attributes atts) {
+ // int size = Integer.parseInt(atts.getValue(SIZE));
+ // }
+ // }
+
+ // private class ChangeHandler extends DelegetingHandler {
+ //
+ // private static final String CHANGE = "change";
+ //
+ // public ChangeHandler() {
+ // super(CHANGE);
+ // }
+ // }
+
+ // private class FromHandler extends AbstractRequirementHandler {
+ //
+ // private static final String FROM = "from";
+ //
+ // public FromHandler() {
+ // super(FROM);
+ // }
+ //
+ // }
+
+ // private class ToHandler extends AbstractRequirementHandler {
+ //
+ // private static final String TO = "to";
+ //
+ // public ToHandler() {
+ // super(TO);
+ // }
+ //
+ // }
+
+ // private class PatchScopeHandler extends DelegetingHandler {
+ //
+ // private static final String PATCH_SCOPE = "patchScope";
+ //
+ // private static final String SIZE = "size";
+ //
+ // public PatchScopeHandler() {
+ // super(PATCH_SCOPE);
+ // addChild(new PatchScopeHandler(), new ChildElementHandler<PatchScopeHandler>() {
+ // public void childHanlded(PatchScopeHandler child) {
+ // }
+ // });
+ // }
+ //
+ // protected void handleAttributes(Attributes atts) {
+ // int size = Integer.parseInt(atts.getValue(SIZE));
+ // }
+ // }
+
+ // private class ScopeHandler extends DelegetingHandler {
+ //
+ // private static final String SCOPE = "scope";
+ //
+ // public ScopeHandler() {
+ // super(SCOPE);
+ // addChild(new RequiresHandler(), new ChildElementHandler<RequiresHandler>() {
+ // public void childHanlded(RequiresHandler child) {
+ // }
+ // });
+ // }
+ // }
+
+ // private class LifeCycleHandler extends AbstractRequirementHandler {
+ //
+ // private static final String LIFE_CYCLE = "lifeCycle";
+ //
+ // public LifeCycleHandler() {
+ // super(LIFE_CYCLE);
+ // }
+ // }
+
+}
diff --git a/src/java/org/apache/ivy/osgi/p2/PropertiesParser.java b/src/java/org/apache/ivy/osgi/p2/PropertiesParser.java
new file mode 100644
index 0000000..60cb96b
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/p2/PropertiesParser.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.p2;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.ivy.osgi.util.DelegatingHandler;
+import org.xml.sax.Attributes;
+
+public class PropertiesParser {
+
+ static class PropertiesHandler extends DelegatingHandler {
+
+ private static final String PROPERTIES = "properties";
+
+ private static final String SIZE = "size";
+
+ Map<String, String> properties;
+
+ public PropertiesHandler(String... props) {
+ super(PROPERTIES);
+ final List<String> propList = Arrays.asList(props);
+ addChild(new PropertyHandler(), new ChildElementHandler<PropertyHandler>() {
+ public void childHanlded(PropertyHandler child) {
+ if (propList.isEmpty() || propList.contains(child.name)) {
+ properties.put(child.name, child.value);
+ }
+ }
+ });
+ }
+
+ protected void handleAttributes(Attributes atts) {
+ int size = Integer.parseInt(atts.getValue(SIZE));
+ properties = new HashMap<String, String>(size);
+ }
+
+ }
+
+ static class PropertyHandler extends DelegatingHandler {
+
+ private static final String PROPERTY = "property";
+
+ private static final String NAME = "name";
+
+ private static final String VALUE = "value";
+
+ String name;
+
+ String value;
+
+ public PropertyHandler() {
+ super(PROPERTY);
+ }
+
+ protected void handleAttributes(Attributes atts) {
+ name = atts.getValue(NAME);
+ value = atts.getValue(VALUE);
+ }
+
+ }
+
+}
diff --git a/src/java/org/apache/ivy/osgi/p2/XMLInputParser.java b/src/java/org/apache/ivy/osgi/p2/XMLInputParser.java
new file mode 100644
index 0000000..4d79f41
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/p2/XMLInputParser.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.p2;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.text.ParseException;
+
+import org.xml.sax.SAXException;
+
+public interface XMLInputParser {
+
+ public void parse(InputStream in) throws ParseException, IOException, SAXException;
+
+}
diff --git a/src/java/org/apache/ivy/osgi/repo/AbstractFSManifestIterable.java b/src/java/org/apache/ivy/osgi/repo/AbstractFSManifestIterable.java
new file mode 100644
index 0000000..0318f67
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/repo/AbstractFSManifestIterable.java
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.repo;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.Stack;
+import java.util.jar.JarInputStream;
+import java.util.jar.Manifest;
+
+import org.apache.ivy.util.Message;
+
+// T is the type of the resource "path"
+public abstract class AbstractFSManifestIterable<T> implements Iterable<ManifestAndLocation> {
+
+ private final T root;
+
+ public AbstractFSManifestIterable(T root) {
+ this.root = root;
+ }
+
+ public Iterator<ManifestAndLocation> iterator() {
+ return new FSManifestIterator();
+ }
+
+ abstract protected List<T> listBundleFiles(T dir) throws IOException;
+
+ abstract protected List<T> listDirs(T dir) throws IOException;
+
+ abstract protected InputStream getInputStream(T f) throws IOException;
+
+ abstract protected URI buildBundleURI(T location) throws IOException;
+
+ class FSManifestIterator implements Iterator<ManifestAndLocation> {
+
+ private ManifestAndLocation next = null;
+
+ /**
+ * Stack of list of directories. An iterator in the stack represents the current directory
+ * being lookup. The first element in the stack is the root directory. The next element in
+ * the stack is an iterator on the children on the root. The last iterator in the stack
+ * points to {@link #currentDir}.
+ */
+ private Stack<Iterator<T>> dirs = new Stack<Iterator<T>>();
+
+ /**
+ * The bundles files being lookup.
+ */
+ private Iterator<T> bundleCandidates = null;
+
+ private T currentDir = null;
+
+ FSManifestIterator() {
+ dirs.add(Collections.<T> singleton(root).iterator());
+ }
+
+ /**
+ * Deep first tree lookup for the directories and the bundles are searched on each found
+ * directory.
+ */
+ public boolean hasNext() {
+ while (next == null) {
+ // no current directory
+ if (currentDir == null) {
+ // so get the next one
+ if (dirs.peek().hasNext()) {
+ currentDir = dirs.peek().next();
+ try {
+ bundleCandidates = listBundleFiles(currentDir).iterator();
+ } catch (IOException e) {
+ Message.warn("Unlistable dir: " + currentDir, e);
+ currentDir = null;
+ }
+ } else if (dirs.size() <= 1) {
+ // no next directory, but we are at the root: finished
+ return false;
+ } else {
+ // remove the top of the stack and continue with a sibling.
+ dirs.pop();
+ }
+ } else if (bundleCandidates.hasNext()) {
+ T bundleCandidate = bundleCandidates.next();
+ JarInputStream in = null;
+ try {
+ in = new JarInputStream(getInputStream(bundleCandidate));
+ Manifest manifest = in.getManifest();
+ if (manifest != null) {
+ next = new ManifestAndLocation(manifest,
+ buildBundleURI(bundleCandidate), null);
+ } else {
+ Message.debug("No manifest in jar: " + bundleCandidate);
+ }
+ } catch (FileNotFoundException e) {
+ Message.debug("Jar file just removed: " + bundleCandidate, e);
+ } catch (IOException e) {
+ Message.warn("Unreadable jar: " + bundleCandidate, e);
+ } finally {
+ if (in != null) {
+ try {
+ in.close();
+ } catch (IOException e) {
+ // Don't care
+ }
+ }
+ }
+ } else {
+ // no more candidate on the current directory
+ // so lookup in the children directories
+ try {
+ dirs.add(listDirs(currentDir).iterator());
+ } catch (IOException e) {
+ Message.warn("Unlistable dir: " + currentDir + " (" + e + ")");
+ dirs.add(Collections.<T> emptyList().iterator());
+ }
+ currentDir = null;
+ }
+ }
+ return true;
+ }
+
+ public ManifestAndLocation next() {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+ ManifestAndLocation manifest = next;
+ next = null;
+ return manifest;
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ }
+}
diff --git a/src/java/org/apache/ivy/osgi/repo/AbstractOSGiResolver.java b/src/java/org/apache/ivy/osgi/repo/AbstractOSGiResolver.java
new file mode 100644
index 0000000..45e92ed
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/repo/AbstractOSGiResolver.java
@@ -0,0 +1,540 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.repo;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.apache.ivy.core.IvyPatternHelper;
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.Configuration;
+import org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.report.DownloadStatus;
+import org.apache.ivy.core.report.MetadataArtifactDownloadReport;
+import org.apache.ivy.core.resolve.IvyNode;
+import org.apache.ivy.core.resolve.ResolveData;
+import org.apache.ivy.core.resolve.ResolvedModuleRevision;
+import org.apache.ivy.osgi.core.BundleInfo;
+import org.apache.ivy.osgi.core.BundleInfoAdapter;
+import org.apache.ivy.osgi.util.Version;
+import org.apache.ivy.plugins.repository.Resource;
+import org.apache.ivy.plugins.repository.url.URLRepository;
+import org.apache.ivy.plugins.repository.url.URLResource;
+import org.apache.ivy.plugins.resolver.BasicResolver;
+import org.apache.ivy.plugins.resolver.util.MDResolvedResource;
+import org.apache.ivy.plugins.resolver.util.ResolvedResource;
+import org.apache.ivy.plugins.resolver.util.ResourceMDParser;
+import org.apache.ivy.util.Message;
+
+public abstract class AbstractOSGiResolver extends BasicResolver {
+
+ private static final String CAPABILITY_EXTRA_ATTR = "osgi_bundle";
+
+ protected static final RepoDescriptor FAILING_REPO_DESCRIPTOR = new EditableRepoDescriptor(
+ null, null);
+
+ private RepoDescriptor repoDescriptor = null;
+
+ private URLRepository repository = new URLRepository();
+
+ public static class RequirementStrategy {
+ // take the first matching
+ public static RequirementStrategy first = new RequirementStrategy();
+
+ // if there are any ambiguity, fail to resolve
+ public static RequirementStrategy noambiguity = new RequirementStrategy();
+
+ public static RequirementStrategy valueOf(String strategy) {
+ if (strategy.equals("first")) {
+ return first;
+ }
+ if (strategy.equals("noambiguity")) {
+ return noambiguity;
+ }
+ throw new IllegalStateException();
+ }
+ }
+
+ private RequirementStrategy requirementStrategy = RequirementStrategy.noambiguity;
+
+ public void setRequirementStrategy(RequirementStrategy importPackageStrategy) {
+ this.requirementStrategy = importPackageStrategy;
+ }
+
+ public void setRequirementStrategy(String strategy) {
+ setRequirementStrategy(RequirementStrategy.valueOf(strategy));
+ }
+
+ protected void setRepoDescriptor(RepoDescriptor repoDescriptor) {
+ this.repoDescriptor = repoDescriptor;
+ }
+
+ public URLRepository getRepository() {
+ return repository;
+ }
+
+ protected void ensureInit() {
+ if (repoDescriptor == null) {
+ try {
+ init();
+ } catch (Exception e) {
+ repoDescriptor = FAILING_REPO_DESCRIPTOR;
+ throw new RuntimeException("Error while loading the OSGi repo descriptor"
+ + e.getMessage() + " (" + e.getClass().getName() + ")", e);
+ }
+ } else if (repoDescriptor == FAILING_REPO_DESCRIPTOR) {
+ throw new RuntimeException("The repository " + getName() + " already failed to load");
+ }
+ }
+
+ abstract protected void init();
+
+ public RepoDescriptor getRepoDescriptor() {
+ ensureInit();
+ return repoDescriptor;
+ }
+
+ @Override
+ public boolean isAllownomd() {
+ // this a repo based resolver, we always have md
+ return false;
+ }
+
+ public ResolvedResource findIvyFileRef(DependencyDescriptor dd, ResolveData data) {
+ ModuleRevisionId mrid = dd.getDependencyRevisionId();
+
+ String osgiType = mrid.getOrganisation();
+ if (osgiType == null) {
+ throw new RuntimeException("Unsupported OSGi module Id: " + mrid.getModuleId());
+ }
+ String id = mrid.getName();
+ Collection<ModuleDescriptor> mds = ModuleDescriptorWrapper.unwrap(getRepoDescriptor()
+ .findModules(osgiType, id));
+ if (mds == null || mds.isEmpty()) {
+ Message.verbose("\t " + id + " not found.");
+ return null;
+ }
+
+ ResolvedResource[] ret;
+ if (BundleInfo.BUNDLE_TYPE.equals(osgiType)) {
+ ret = findBundle(dd, data, mds);
+ } else {
+ ret = findCapability(dd, data, mds);
+ }
+
+ ResolvedResource found = findResource(ret, getDefaultRMDParser(dd.getDependencyId()), mrid,
+ data.getDate());
+ if (found == null) {
+ Message.debug("\t" + getName() + ": no resource found for " + mrid);
+ }
+ return found;
+ }
+
+ public ResolvedResource[] findBundle(DependencyDescriptor dd, ResolveData data,
+ Collection<ModuleDescriptor> mds) {
+ ResolvedResource[] ret = new ResolvedResource[mds.size()];
+ int i = 0;
+ for (ModuleDescriptor md : mds) {
+ MetadataArtifactDownloadReport report = new MetadataArtifactDownloadReport(null);
+ report.setDownloadStatus(DownloadStatus.NO);
+ report.setSearched(true);
+ ResolvedModuleRevision rmr = new ResolvedModuleRevision(this, this, md, report);
+ MDResolvedResource mdrr = new MDResolvedResource(null, md.getRevision(), rmr);
+ ret[i++] = mdrr;
+ }
+ return ret;
+ }
+
+ public ResolvedResource[] findCapability(DependencyDescriptor dd, ResolveData data,
+ Collection<ModuleDescriptor> mds) {
+ ResolvedResource[] ret = new ResolvedResource[mds.size()];
+ int i = 0;
+ for (ModuleDescriptor md : mds) {
+ IvyNode node = data.getNode(md.getModuleRevisionId());
+ if (node != null && node.getDescriptor() != null) {
+ // already resolved import, no need to go further
+ return new ResolvedResource[] {buildResolvedCapabilityMd(dd, node.getDescriptor())};
+ }
+ ret[i++] = buildResolvedCapabilityMd(dd, md);
+ }
+ return ret;
+ }
+
+ private MDResolvedResource buildResolvedCapabilityMd(DependencyDescriptor dd,
+ ModuleDescriptor md) {
+ String org = dd.getDependencyRevisionId().getOrganisation();
+ String name = dd.getDependencyRevisionId().getName();
+ String rev = md.getExtraInfoContentByTagName(BundleInfoAdapter.EXTRA_INFO_EXPORT_PREFIX
+ + name);
+ ModuleRevisionId capabilityRev = ModuleRevisionId.newInstance(org, name, rev,
+ Collections.singletonMap(CAPABILITY_EXTRA_ATTR, md.getModuleRevisionId().toString()));
+
+ DefaultModuleDescriptor capabilityMd = new DefaultModuleDescriptor(capabilityRev,
+ getSettings().getStatusManager().getDefaultStatus(), new Date());
+
+ String useConf = BundleInfoAdapter.CONF_USE_PREFIX + dd.getDependencyRevisionId().getName();
+
+ capabilityMd.addConfiguration(BundleInfoAdapter.CONF_DEFAULT);
+ capabilityMd.addConfiguration(BundleInfoAdapter.CONF_OPTIONAL);
+ capabilityMd.addConfiguration(BundleInfoAdapter.CONF_TRANSITIVE_OPTIONAL);
+ capabilityMd.addConfiguration(new Configuration(useConf));
+
+ DefaultDependencyDescriptor capabilityDD = new DefaultDependencyDescriptor(
+ md.getModuleRevisionId(), false);
+ capabilityDD.addDependencyConfiguration(BundleInfoAdapter.CONF_NAME_DEFAULT,
+ BundleInfoAdapter.CONF_NAME_DEFAULT);
+ capabilityDD.addDependencyConfiguration(BundleInfoAdapter.CONF_NAME_OPTIONAL,
+ BundleInfoAdapter.CONF_NAME_OPTIONAL);
+ capabilityDD.addDependencyConfiguration(BundleInfoAdapter.CONF_NAME_TRANSITIVE_OPTIONAL,
+ BundleInfoAdapter.CONF_NAME_TRANSITIVE_OPTIONAL);
+ capabilityDD.addDependencyConfiguration(useConf, useConf);
+ capabilityMd.addDependency(capabilityDD);
+
+ MetadataArtifactDownloadReport report = new MetadataArtifactDownloadReport(null);
+ report.setDownloadStatus(DownloadStatus.NO);
+ report.setSearched(true);
+ ResolvedModuleRevision rmr = new ResolvedModuleRevision(this, this, capabilityMd, report);
+ return new MDResolvedResource(null, capabilityMd.getRevision(), rmr);
+ }
+
+ @Override
+ public ResolvedResource findResource(ResolvedResource[] rress, ResourceMDParser rmdparser,
+ ModuleRevisionId mrid, Date date) {
+ ResolvedResource found = super.findResource(rress, rmdparser, mrid, date);
+ if (found == null) {
+ return null;
+ }
+
+ String osgiType = mrid.getOrganisation();
+ // for non bundle requirement : log the selected bundle
+ if (!BundleInfo.BUNDLE_TYPE.equals(osgiType)) {
+ // several candidates with different symbolic name : make an warning about the ambiguity
+ if (rress.length != 1) {
+ // several candidates with different symbolic name ?
+ Map<String, List<MDResolvedResource>> matching = new HashMap<String, List<MDResolvedResource>>();
+ for (int i = 0; i < rress.length; i++) {
+ String name = ((MDResolvedResource) rress[i]).getResolvedModuleRevision()
+ .getDescriptor().getExtraAttribute(CAPABILITY_EXTRA_ATTR);
+ List<MDResolvedResource> list = matching.get(name);
+ if (list == null) {
+ list = new ArrayList<MDResolvedResource>();
+ matching.put(name, list);
+ }
+ list.add((MDResolvedResource) rress[i]);
+ }
+ if (matching.keySet().size() != 1) {
+ if (requirementStrategy == RequirementStrategy.first) {
+ Message.warn("Ambiguity for the '" + osgiType + "' requirement "
+ + mrid.getName() + ";version=" + mrid.getRevision());
+ for (Entry<String, List<MDResolvedResource>> entry : matching.entrySet()) {
+ Message.warn("\t" + entry.getKey());
+ for (MDResolvedResource c : entry.getValue()) {
+ Message.warn("\t\t" + c.getRevision()
+ + (found == c ? " (selected)" : ""));
+ }
+ }
+ } else if (requirementStrategy == RequirementStrategy.noambiguity) {
+ Message.error("Ambiguity for the '" + osgiType + "' requirement "
+ + mrid.getName() + ";version=" + mrid.getRevision());
+ for (Entry<String, List<MDResolvedResource>> entry : matching.entrySet()) {
+ Message.error("\t" + entry.getKey());
+ for (MDResolvedResource c : entry.getValue()) {
+ Message.error("\t\t" + c.getRevision()
+ + (found == c ? " (best match)" : ""));
+ }
+ }
+ return null;
+ }
+ }
+ }
+ Message.info("'" + osgiType + "' requirement " + mrid.getName() + ";version="
+ + mrid.getRevision() + " satisfied by "
+ + ((MDResolvedResource) found).getResolvedModuleRevision().getId().getName()
+ + ";" + found.getRevision());
+ }
+
+ return found;
+ }
+
+ public ResolvedResource findArtifactRef(Artifact artifact, Date date) {
+ URL url = artifact.getUrl();
+ if (url == null) {
+ // not an artifact resolved by this resolver
+ return null;
+ }
+ Message.verbose("\tusing url for " + artifact + ": " + url);
+ logArtifactAttempt(artifact, url.toExternalForm());
+ Resource resource = new URLResource(url);
+ return new ResolvedResource(resource, artifact.getModuleRevisionId().getRevision());
+ }
+
+ @Override
+ protected void checkModuleDescriptorRevision(ModuleDescriptor systemMd,
+ ModuleRevisionId systemMrid) {
+ String osgiType = systemMrid.getOrganisation();
+ // only check revision if we're searching for a bundle (package and bundle have different
+ // version
+ if (osgiType == null || osgiType.equals(BundleInfo.BUNDLE_TYPE)) {
+ super.checkModuleDescriptorRevision(systemMd, systemMrid);
+ }
+ }
+
+ @Override
+ protected Collection/* <String> */filterNames(Collection/* <String> */names) {
+ getSettings().filterIgnore(names);
+ return names;
+ }
+
+ @Override
+ protected Collection findNames(Map tokenValues, String token) {
+ if (IvyPatternHelper.ORGANISATION_KEY.equals(token)) {
+ return getRepoDescriptor().getCapabilities();
+ }
+
+ String osgiType = (String) tokenValues.get(IvyPatternHelper.ORGANISATION_KEY);
+ if (osgiType == null || osgiType.length() == 0) {
+ return Collections.EMPTY_LIST;
+ }
+
+ if (IvyPatternHelper.MODULE_KEY.equals(token)) {
+ return getRepoDescriptor().getCapabilityValues(osgiType);
+ }
+
+ if (IvyPatternHelper.REVISION_KEY.equals(token)) {
+ String name = (String) tokenValues.get(IvyPatternHelper.MODULE_KEY);
+ List<String> versions = new ArrayList<String>();
+ Set<ModuleDescriptorWrapper> mds = getRepoDescriptor().findModules(osgiType, name);
+ if (mds != null) {
+ for (ModuleDescriptorWrapper md : mds) {
+ versions.add(md.getBundleInfo().getVersion().toString());
+ }
+ }
+ return versions;
+ }
+
+ if (IvyPatternHelper.CONF_KEY.equals(token)) {
+ String name = (String) tokenValues.get(IvyPatternHelper.MODULE_KEY);
+ if (name == null) {
+ return Collections.EMPTY_LIST;
+ }
+ if (osgiType.equals(BundleInfo.PACKAGE_TYPE)) {
+ return Collections.singletonList(BundleInfoAdapter.CONF_USE_PREFIX + name);
+ }
+ Collection<ModuleDescriptor> mds = ModuleDescriptorWrapper.unwrap(getRepoDescriptor()
+ .findModules(osgiType, name));
+ if (mds == null) {
+ return Collections.EMPTY_LIST;
+ }
+ String version = (String) tokenValues.get(IvyPatternHelper.REVISION_KEY);
+ if (version == null) {
+ return Collections.EMPTY_LIST;
+ }
+ ModuleDescriptor found = null;
+ for (ModuleDescriptor md : mds) {
+ if (md.getRevision().equals(version)) {
+ found = md;
+ }
+ }
+ if (found == null) {
+ return Collections.EMPTY_LIST;
+ }
+ List<String> confs = Arrays.asList(found.getConfigurationsNames());
+ return confs;
+ }
+ return Collections.EMPTY_LIST;
+ }
+
+ /**
+ * Populate capabilityValues with capability values for which at least one module match the
+ * expected revision
+ */
+ private void filterCapabilityValues(Set<String> capabilityValues,
+ Map<String, Set<ModuleDescriptor>> moduleByCapbilityValue,
+ Map<String, String> tokenValues, String rev) {
+ if (rev == null) {
+ // no revision, all match then
+ capabilityValues.addAll(moduleByCapbilityValue.keySet());
+ } else {
+ for (Entry<String, Set<ModuleDescriptor>> entry : moduleByCapbilityValue.entrySet()) {
+ Iterator<ModuleDescriptor> itModules = entry.getValue().iterator();
+ boolean moduleMatchRev = false;
+ while (!moduleMatchRev && itModules.hasNext()) {
+ ModuleDescriptor md = itModules.next();
+ moduleMatchRev = rev.equals(md.getRevision());
+ }
+ if (moduleMatchRev) {
+ // at least one module matched, the capability value is ok to add
+ capabilityValues.add(entry.getKey());
+ }
+ }
+ }
+ }
+
+ @Override
+ public Map[] listTokenValues(String[] tokens, Map criteria) {
+ Set<String> tokenSet = new HashSet<String>(Arrays.asList(tokens));
+ Set<Map<String, String>> listTokenValues = listTokenValues(tokenSet, criteria);
+ return listTokenValues.toArray(new Map[listTokenValues.size()]);
+ }
+
+ private Set<Map<String, String>> listTokenValues(Set<String> tokens,
+ Map<String, String> criteria) {
+ if (tokens.isEmpty()) {
+ return Collections.singleton(criteria);
+ }
+
+ Set<String> remainingTokens = new HashSet<String>(tokens);
+
+ Map<String, String> values = new HashMap<String, String>();
+
+ remainingTokens.remove(IvyPatternHelper.ORGANISATION_KEY);
+ String osgiType = criteria.get(IvyPatternHelper.ORGANISATION_KEY);
+ if (osgiType == null || osgiType.length() == 0) {
+ return Collections.emptySet();
+ }
+ values.put(IvyPatternHelper.ORGANISATION_KEY, osgiType);
+
+ if (osgiType == null) {
+ Set<Map<String, String>> tokenValues = new HashSet<Map<String, String>>();
+ Map<String, String> newCriteria = new HashMap<String, String>(criteria);
+ newCriteria.put(IvyPatternHelper.ORGANISATION_KEY, BundleInfo.BUNDLE_TYPE);
+ tokenValues.addAll(listTokenValues(remainingTokens, newCriteria));
+ newCriteria = new HashMap<String, String>(criteria);
+ newCriteria.put(IvyPatternHelper.ORGANISATION_KEY, BundleInfo.PACKAGE_TYPE);
+ tokenValues.addAll(listTokenValues(remainingTokens, newCriteria));
+ newCriteria = new HashMap<String, String>(criteria);
+ newCriteria.put(IvyPatternHelper.ORGANISATION_KEY, BundleInfo.SERVICE_TYPE);
+ tokenValues.addAll(listTokenValues(remainingTokens, newCriteria));
+ return tokenValues;
+ }
+
+ Set<String> capabilities = getRepoDescriptor().getCapabilityValues(osgiType);
+ if (capabilities == null || capabilities.isEmpty()) {
+ return Collections.emptySet();
+ }
+
+ remainingTokens.remove(IvyPatternHelper.MODULE_KEY);
+ String module = criteria.get(IvyPatternHelper.MODULE_KEY);
+ if (module == null) {
+ Set<Map<String, String>> tokenValues = new HashSet<Map<String, String>>();
+ for (String name : capabilities) {
+ Map<String, String> newCriteria = new HashMap<String, String>(criteria);
+ newCriteria.put(IvyPatternHelper.MODULE_KEY, name);
+ tokenValues.addAll(listTokenValues(remainingTokens, newCriteria));
+ }
+ return tokenValues;
+ }
+ values.put(IvyPatternHelper.MODULE_KEY, module);
+
+ remainingTokens.remove(IvyPatternHelper.REVISION_KEY);
+ String rev = criteria.get(IvyPatternHelper.REVISION_KEY);
+ if (rev == null) {
+ Set<ModuleDescriptorWrapper> mdws = getRepoDescriptor().findModules(osgiType, module);
+ if (mdws == null || mdws.isEmpty()) {
+ return Collections.emptySet();
+ }
+ Set<Map<String, String>> tokenValues = new HashSet<Map<String, String>>();
+ for (ModuleDescriptorWrapper mdw : mdws) {
+ Map<String, String> newCriteria = new HashMap<String, String>(criteria);
+ newCriteria.put(IvyPatternHelper.REVISION_KEY, mdw.getBundleInfo().getVersion()
+ .toString());
+ tokenValues.addAll(listTokenValues(remainingTokens, newCriteria));
+ }
+ return tokenValues;
+ }
+ values.put(IvyPatternHelper.REVISION_KEY, rev);
+
+ remainingTokens.remove(IvyPatternHelper.CONF_KEY);
+ String conf = criteria.get(IvyPatternHelper.CONF_KEY);
+ if (conf == null) {
+ if (osgiType.equals(BundleInfo.PACKAGE_TYPE)) {
+ values.put(IvyPatternHelper.CONF_KEY, BundleInfoAdapter.CONF_USE_PREFIX + module);
+ return Collections.singleton(values);
+ }
+ Set<ModuleDescriptorWrapper> bundles = getRepoDescriptor()
+ .findModules(osgiType, module);
+ if (bundles == null) {
+ return Collections.emptySet();
+ }
+ Version v;
+ try {
+ v = new Version(rev);
+ } catch (ParseException e) {
+ return Collections.emptySet();
+ }
+ ModuleDescriptorWrapper found = null;
+ for (ModuleDescriptorWrapper bundle : bundles) {
+ if (bundle.getBundleInfo().getVersion().equals(v)) {
+ found = bundle;
+ }
+ }
+ if (found == null) {
+ return Collections.emptySet();
+ }
+ Set<Map<String, String>> tokenValues = new HashSet<Map<String, String>>();
+ List<String> configurations = BundleInfoAdapter
+ .getConfigurations(found.getBundleInfo());
+ for (int i = 0; i < configurations.size(); i++) {
+ Map<String, String> newCriteria = new HashMap<String, String>(criteria);
+ newCriteria.put(IvyPatternHelper.CONF_KEY, configurations.get(i));
+ tokenValues.add(newCriteria);
+ }
+ return tokenValues;
+ }
+ values.put(IvyPatternHelper.CONF_KEY, conf);
+
+ return Collections.singleton(values);
+ }
+
+ protected long get(Resource resource, File dest) throws IOException {
+ Message.verbose("\t" + getName() + ": downloading " + resource.getName());
+ Message.debug("\t\tto " + dest);
+ if (dest.getParentFile() != null) {
+ dest.getParentFile().mkdirs();
+ }
+ getRepository().get(resource.getName(), dest);
+ return dest.length();
+ }
+
+ protected Resource getResource(String source) throws IOException {
+ return getRepository().getResource(source);
+ }
+
+ public void publish(Artifact artifact, File src, boolean overwrite) throws IOException {
+ throw new UnsupportedOperationException();
+ }
+
+}
diff --git a/src/java/org/apache/ivy/osgi/repo/AggregatedOSGiResolver.java b/src/java/org/apache/ivy/osgi/repo/AggregatedOSGiResolver.java
new file mode 100644
index 0000000..ad00cf4
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/repo/AggregatedOSGiResolver.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.repo;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class AggregatedOSGiResolver extends AbstractOSGiResolver {
+
+ private List<AbstractOSGiResolver> resolvers = new ArrayList<AbstractOSGiResolver>();
+
+ public void add(AbstractOSGiResolver resolver) {
+ resolvers.add(resolver);
+ }
+
+ @Override
+ protected void init() {
+ List<RepoDescriptor> repos = new ArrayList<RepoDescriptor>();
+ for (AbstractOSGiResolver resolver : resolvers) {
+ repos.add(resolver.getRepoDescriptor());
+ }
+ setRepoDescriptor(new AggregatedRepoDescriptor(repos));
+ }
+
+}
diff --git a/src/java/org/apache/ivy/osgi/repo/AggregatedRepoDescriptor.java b/src/java/org/apache/ivy/osgi/repo/AggregatedRepoDescriptor.java
new file mode 100644
index 0000000..0963bd7
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/repo/AggregatedRepoDescriptor.java
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.repo;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+public class AggregatedRepoDescriptor extends RepoDescriptor {
+
+ private List<RepoDescriptor> repos;
+
+ public AggregatedRepoDescriptor(List<RepoDescriptor> repos) {
+ this.repos = repos;
+ }
+
+ @Override
+ public Iterator<ModuleDescriptorWrapper> getModules() {
+ final Iterator<RepoDescriptor> itRepos = repos.iterator();
+ return new Iterator<ModuleDescriptorWrapper>() {
+
+ private Iterator<ModuleDescriptorWrapper> current = null;
+
+ private ModuleDescriptorWrapper next = null;
+
+ public boolean hasNext() {
+ while (next == null) {
+ if (current == null) {
+ if (!itRepos.hasNext()) {
+ return false;
+ }
+ RepoDescriptor repo = itRepos.next();
+ current = repo.getModules();
+ }
+ if (current.hasNext()) {
+ next = current.next();
+ } else {
+ current = null;
+ }
+ }
+ return true;
+ }
+
+ public ModuleDescriptorWrapper next() {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+ ModuleDescriptorWrapper ret = next;
+ next = null;
+ return ret;
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+
+ @Override
+ public Set<String> getCapabilities() {
+ Set<String> ret = new HashSet<String>();
+ for (RepoDescriptor repo : repos) {
+ Set<String> capabilities = repo.getCapabilities();
+ if (capabilities != null) {
+ ret.addAll(capabilities);
+ }
+ }
+ return ret;
+ }
+
+ @Override
+ public Set<ModuleDescriptorWrapper> findModules(String requirement, String value) {
+ Set<ModuleDescriptorWrapper> ret = new HashSet<ModuleDescriptorWrapper>();
+ for (RepoDescriptor repo : repos) {
+ Set<ModuleDescriptorWrapper> modules = repo.findModules(requirement, value);
+ if (modules != null) {
+ ret.addAll(modules);
+ }
+ }
+ return ret;
+ }
+
+ @Override
+ public Set<String> getCapabilityValues(String capabilityName) {
+ Set<String> ret = new HashSet<String>();
+ for (RepoDescriptor repo : repos) {
+ Set<String> capabilityValues = repo.getCapabilityValues(capabilityName);
+ if (capabilityValues != null) {
+ ret.addAll(capabilityValues);
+ }
+ }
+ return ret;
+ }
+
+}
diff --git a/src/java/org/apache/ivy/osgi/repo/ArtifactReportManifestIterable.java b/src/java/org/apache/ivy/osgi/repo/ArtifactReportManifestIterable.java
new file mode 100644
index 0000000..5b3ae2d
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/repo/ArtifactReportManifestIterable.java
@@ -0,0 +1,172 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.repo;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.jar.JarInputStream;
+import java.util.jar.Manifest;
+
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.report.ArtifactDownloadReport;
+import org.apache.ivy.util.Message;
+
+public class ArtifactReportManifestIterable implements Iterable<ManifestAndLocation> {
+
+ private final Map<ModuleRevisionId, List<ArtifactDownloadReport>> artifactReports = new HashMap<ModuleRevisionId, List<ArtifactDownloadReport>>();
+
+ private List<String> sourceTypes;
+
+ public ArtifactReportManifestIterable(List<ArtifactDownloadReport> reports,
+ List<String> sourceTypes) {
+ this.sourceTypes = sourceTypes;
+ for (ArtifactDownloadReport report : reports) {
+ ModuleRevisionId mrid = report.getArtifact().getModuleRevisionId();
+ List<ArtifactDownloadReport> moduleReports = artifactReports.get(mrid);
+ if (moduleReports == null) {
+ moduleReports = new ArrayList<ArtifactDownloadReport>();
+ artifactReports.put(mrid, moduleReports);
+ }
+ moduleReports.add(report);
+ }
+ }
+
+ public Iterator<ManifestAndLocation> iterator() {
+ return new ArtifactReportManifestIterator();
+ }
+
+ class ArtifactReportManifestIterator implements Iterator<ManifestAndLocation> {
+
+ private ManifestAndLocation next = null;
+
+ private Iterator<ModuleRevisionId> it;
+
+ public ArtifactReportManifestIterator() {
+ it = artifactReports.keySet().iterator();
+ }
+
+ public boolean hasNext() {
+ while (next == null && it.hasNext()) {
+ ModuleRevisionId mrid = it.next();
+ List<ArtifactDownloadReport> reports = artifactReports.get(mrid);
+ ArtifactDownloadReport jar = null;
+ ArtifactDownloadReport source = null;
+ for (ArtifactDownloadReport report : reports) {
+ if (sourceTypes != null && sourceTypes.contains(report.getArtifact().getType())) {
+ source = report;
+ } else {
+ jar = report;
+ }
+ }
+ if (jar == null) {
+ // didn't found any suitable jar
+ continue;
+ }
+ URI sourceURI = null;
+ if (source != null) {
+ if (source.getUnpackedLocalFile() != null) {
+ sourceURI = source.getUnpackedLocalFile().toURI();
+ } else {
+ sourceURI = source.getLocalFile().toURI();
+ }
+ }
+ if (jar.getUnpackedLocalFile() != null && jar.getUnpackedLocalFile().isDirectory()) {
+ FileInputStream in = null;
+ try {
+ in = new FileInputStream(new File(jar.getUnpackedLocalFile(),
+ "META-INF/MANIFEST.MF"));
+ next = new ManifestAndLocation(new Manifest(in), jar.getUnpackedLocalFile()
+ .toURI(), sourceURI);
+ return true;
+ } catch (FileNotFoundException e) {
+ Message.debug(
+ "Bundle directory file just removed: " + jar.getUnpackedLocalFile(), e);
+ } catch (IOException e) {
+ Message.debug("The Manifest in the bundle directory could not be read: "
+ + jar.getUnpackedLocalFile(), e);
+ } finally {
+ if (in != null) {
+ try {
+ in.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ }
+ } else {
+ File artifact;
+ if (jar.getUnpackedLocalFile() != null) {
+ artifact = jar.getUnpackedLocalFile();
+ } else {
+ artifact = jar.getLocalFile();
+ }
+ JarInputStream in = null;
+ try {
+ in = new JarInputStream(new FileInputStream(artifact));
+ Manifest manifest = in.getManifest();
+ if (manifest != null) {
+ next = new ManifestAndLocation(manifest, artifact.toURI(), sourceURI);
+ return true;
+ }
+ Message.debug("No manifest in jar: " + artifact);
+ } catch (FileNotFoundException e) {
+ Message.debug("Jar file just removed: " + artifact, e);
+ } catch (IOException e) {
+ Message.warn("Unreadable jar: " + artifact, e);
+ } finally {
+ if (in != null) {
+ try {
+ in.close();
+ } catch (IOException e) {
+ // Don't care
+ }
+ }
+ }
+ }
+ }
+ if (next == null) {
+ return false;
+ }
+ return true;
+ }
+
+ public ManifestAndLocation next() {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+ ManifestAndLocation manifest = next;
+ next = null;
+ return manifest;
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/src/java/org/apache/ivy/osgi/repo/BundleCapabilityAndLocation.java b/src/java/org/apache/ivy/osgi/repo/BundleCapabilityAndLocation.java
new file mode 100644
index 0000000..823245e
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/repo/BundleCapabilityAndLocation.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.repo;
+
+import org.apache.ivy.osgi.core.BundleInfo;
+import org.apache.ivy.osgi.util.Version;
+
+public class BundleCapabilityAndLocation {
+
+ private final String name;
+
+ private final Version version;
+
+ private final BundleInfo bundleInfo;
+
+ private final String type;
+
+ public BundleCapabilityAndLocation(String type, String name, Version version,
+ BundleInfo bundleInfo) {
+ this.type = type;
+ this.name = name;
+ this.version = version;
+ this.bundleInfo = bundleInfo;
+ }
+
+ public BundleInfo getBundleInfo() {
+ return bundleInfo;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public Version getVersion() {
+ return version;
+ }
+
+}
\ No newline at end of file
diff --git a/src/java/org/apache/ivy/osgi/repo/BundleRepoDescriptor.java b/src/java/org/apache/ivy/osgi/repo/BundleRepoDescriptor.java
new file mode 100644
index 0000000..90bb6ae
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/repo/BundleRepoDescriptor.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.repo;
+
+import java.net.URI;
+import java.text.ParseException;
+import java.util.Iterator;
+
+import org.apache.ivy.osgi.core.BundleArtifact;
+import org.apache.ivy.osgi.core.BundleInfo;
+import org.apache.ivy.osgi.core.ExecutionEnvironmentProfileProvider;
+import org.apache.ivy.osgi.core.ManifestParser;
+import org.apache.ivy.util.Message;
+
+public class BundleRepoDescriptor extends EditableRepoDescriptor {
+
+ private String name;
+
+ private String lastModified;
+
+ public BundleRepoDescriptor(URI baseUri, ExecutionEnvironmentProfileProvider profileProvider) {
+ super(baseUri, profileProvider);
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setLastModified(String lastModified) {
+ this.lastModified = lastModified;
+ }
+
+ public String getLastModified() {
+ return lastModified;
+ }
+
+ public void populate(Iterator<ManifestAndLocation> it) {
+ while (it.hasNext()) {
+ ManifestAndLocation manifestAndLocation = it.next();
+ try {
+ BundleInfo bundleInfo = ManifestParser.parseManifest(manifestAndLocation
+ .getManifest());
+ bundleInfo
+ .addArtifact(new BundleArtifact(false, manifestAndLocation.getUri(), null));
+ addBundle(bundleInfo);
+ } catch (ParseException e) {
+ Message.error("Rejected " + manifestAndLocation.getUri() + ": " + e.getMessage());
+ }
+ }
+ }
+
+}
diff --git a/src/java/org/apache/ivy/osgi/repo/EditableRepoDescriptor.java b/src/java/org/apache/ivy/osgi/repo/EditableRepoDescriptor.java
new file mode 100644
index 0000000..ae77af2
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/repo/EditableRepoDescriptor.java
@@ -0,0 +1,173 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.repo;
+
+import java.net.URI;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.ivy.osgi.core.BundleCapability;
+import org.apache.ivy.osgi.core.BundleInfo;
+import org.apache.ivy.osgi.core.ExecutionEnvironmentProfileProvider;
+import org.apache.ivy.osgi.util.Version;
+import org.apache.ivy.util.Message;
+
+public class EditableRepoDescriptor extends RepoDescriptor {
+
+ private final Map<String, Map<String, Set<ModuleDescriptorWrapper>>> moduleByCapabilities = new HashMap<String, Map<String, Set<ModuleDescriptorWrapper>>>();
+
+ private final Set<ModuleDescriptorWrapper> modules = new HashSet<ModuleDescriptorWrapper>();
+
+ private final ExecutionEnvironmentProfileProvider profileProvider;
+
+ private final URI baseUri;
+
+ private int logLevel = Message.MSG_INFO;
+
+ public EditableRepoDescriptor(URI baseUri, ExecutionEnvironmentProfileProvider profileProvider) {
+ this.baseUri = baseUri;
+ this.profileProvider = profileProvider;
+ }
+
+ public void setLogLevel(int logLevel) {
+ this.logLevel = logLevel;
+ }
+
+ public int getLogLevel() {
+ return logLevel;
+ }
+
+ public URI getBaseUri() {
+ return baseUri;
+ }
+
+ public Iterator<ModuleDescriptorWrapper> getModules() {
+ return modules.iterator();
+ }
+
+ public Set<String> getCapabilities() {
+ return moduleByCapabilities.keySet();
+ }
+
+ public Set<ModuleDescriptorWrapper> findModules(String requirement, String value) {
+ Map<String, Set<ModuleDescriptorWrapper>> modules = moduleByCapabilities.get(requirement);
+ if (modules == null) {
+ return null;
+ }
+ return modules.get(value);
+ }
+
+ public ModuleDescriptorWrapper findModule(String symbolicName, Version version) {
+ Set<ModuleDescriptorWrapper> modules = findModules(BundleInfo.BUNDLE_TYPE, symbolicName);
+ if (modules == null) {
+ return null;
+ }
+ for (ModuleDescriptorWrapper module : modules) {
+ if (module.getBundleInfo().getVersion().equals(version)) {
+ return module;
+ }
+ }
+ return null;
+ }
+
+ public Set<String> getCapabilityValues(String capabilityName) {
+ Map<String, Set<ModuleDescriptorWrapper>> modules = moduleByCapabilities
+ .get(capabilityName);
+ if (modules == null) {
+ return Collections.emptySet();
+ }
+ return modules.keySet();
+ }
+
+ private void add(String type, String value, ModuleDescriptorWrapper md) {
+ modules.add(md);
+ Map<String, Set<ModuleDescriptorWrapper>> map = moduleByCapabilities.get(type);
+ if (map == null) {
+ map = new HashMap<String, Set<ModuleDescriptorWrapper>>();
+ moduleByCapabilities.put(type, map);
+ }
+ Set<ModuleDescriptorWrapper> bundleReferences = map.get(value);
+ if (bundleReferences == null) {
+ bundleReferences = new HashSet<ModuleDescriptorWrapper>();
+ map.put(value, bundleReferences);
+ }
+ if (!bundleReferences.add(md)) {
+ if (logLevel <= Message.MSG_DEBUG) {
+ Message.debug("Duplicate module in the repo " + baseUri + " for " + type + " "
+ + value + ": " + md.getBundleInfo().getSymbolicName() + "#"
+ + md.getBundleInfo().getVersion());
+ }
+ }
+ }
+
+ public void addBundle(BundleInfo bundleInfo) {
+ ModuleDescriptorWrapper module = findModule(bundleInfo.getSymbolicName(),
+ bundleInfo.getVersion());
+ if (module != null) {
+ Message.debug("Duplicate module " + bundleInfo.getSymbolicName() + "@"
+ + bundleInfo.getVersion());
+ return;
+ }
+ ModuleDescriptorWrapper md = new ModuleDescriptorWrapper(bundleInfo, baseUri,
+ profileProvider);
+ add(BundleInfo.BUNDLE_TYPE, bundleInfo.getSymbolicName(), md);
+ for (BundleCapability capability : bundleInfo.getCapabilities()) {
+ add(capability.getType(), capability.getName(), md);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return modules.toString();
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((modules == null) ? 0 : modules.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ EditableRepoDescriptor other = (EditableRepoDescriptor) obj;
+ if (modules == null) {
+ if (other.modules != null) {
+ return false;
+ }
+ } else if (!modules.equals(other.modules)) {
+ return false;
+ }
+ return true;
+ }
+
+}
diff --git a/src/java/org/apache/ivy/osgi/repo/FSManifestIterable.java b/src/java/org/apache/ivy/osgi/repo/FSManifestIterable.java
new file mode 100644
index 0000000..fe997e2
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/repo/FSManifestIterable.java
@@ -0,0 +1,126 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.repo;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FilenameFilter;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class FSManifestIterable extends AbstractFSManifestIterable<File> {
+
+ /**
+ * List of directory name that usually contains jars but are not bundles
+ */
+ public static final Set<String> NON_BUNDLE_DIRS = new HashSet<String>(Arrays.asList("source",
+ "sources", "javadoc", "javadocs", "doc", "docs"));
+
+ /**
+ * Default directory filter that doesn't select .svn directories, neither the directories that
+ * match {@link #NON_BUNDLE_DIRS}.
+ */
+ public static final FilenameFilter DEFAULT_DIR_FILTER = new FilenameFilter() {
+ public boolean accept(File dir, String name) {
+ return !name.equals(".svn") && !NON_BUNDLE_DIRS.contains(name);
+ }
+ };
+
+ /**
+ * Default bundle filter that select only .jar files
+ */
+ public static final FilenameFilter DEFAULT_BUNLDE_FILTER = new FilenameFilter() {
+ public boolean accept(File dir, String name) {
+ return name.endsWith(".jar");
+ }
+ };
+
+ private FilenameFilter dirFilter = DEFAULT_DIR_FILTER;
+
+ private FilenameFilter bundleFilter = DEFAULT_BUNLDE_FILTER;
+
+ /**
+ * Default constructor
+ *
+ * @param root
+ * the root directory of the file system to lookup
+ */
+ public FSManifestIterable(File root) {
+ super(root);
+ }
+
+ public FilenameFilter getDirFilter() {
+ return dirFilter;
+ }
+
+ public void setDirFilter(FilenameFilter dirFilter) {
+ this.dirFilter = dirFilter;
+ }
+
+ public FilenameFilter getBundleFilter() {
+ return bundleFilter;
+ }
+
+ public void setBundleFilter(FilenameFilter bundleFilter) {
+ this.bundleFilter = bundleFilter;
+ }
+
+ protected URI buildBundleURI(File location) {
+ try {
+ return new URI(location.toURI().toURL().toExternalForm());
+ } catch (MalformedURLException e) {
+ throw new RuntimeException("Unexpected file to url conversion error", e);
+ } catch (URISyntaxException e) {
+ throw new RuntimeException("Unexpected url to uri conversion error", e);
+ }
+ }
+
+ protected InputStream getInputStream(File f) throws FileNotFoundException {
+ return new FileInputStream(f);
+ }
+
+ protected List<File> listBundleFiles(File dir) {
+ return Arrays.asList(dir.listFiles(new FileFilter() {
+ public boolean accept(File f) {
+ if (!f.isFile()) {
+ return false;
+ }
+ return bundleFilter.accept(f.getParentFile(), f.getName());
+ }
+ }));
+ }
+
+ protected List<File> listDirs(File dir) {
+ return Arrays.asList(dir.listFiles(new FileFilter() {
+ public boolean accept(File f) {
+ if (!f.isDirectory()) {
+ return false;
+ }
+ return dirFilter == null || dirFilter.accept(f.getParentFile(), f.getName());
+ }
+ }));
+ }
+}
\ No newline at end of file
diff --git a/src/java/org/apache/ivy/osgi/repo/ManifestAndLocation.java b/src/java/org/apache/ivy/osgi/repo/ManifestAndLocation.java
new file mode 100644
index 0000000..8416266
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/repo/ManifestAndLocation.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.repo;
+
+import java.net.URI;
+import java.util.jar.Manifest;
+
+public class ManifestAndLocation {
+
+ private final Manifest manifest;
+
+ /**
+ * location of the jar
+ */
+ private final URI uri;
+
+ /**
+ * location of the source jar
+ */
+ private final URI sourceURI;
+
+ public ManifestAndLocation(Manifest manifest, URI uri, URI sourceURI) {
+ this.manifest = manifest;
+ this.uri = uri;
+ this.sourceURI = sourceURI;
+ }
+
+ public URI getUri() {
+ return uri;
+ }
+
+ public Manifest getManifest() {
+ return manifest;
+ }
+
+ public URI getSourceURI() {
+ return sourceURI;
+ }
+}
diff --git a/src/java/org/apache/ivy/osgi/repo/ModuleDescriptorWrapper.java b/src/java/org/apache/ivy/osgi/repo/ModuleDescriptorWrapper.java
new file mode 100644
index 0000000..3707647
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/repo/ModuleDescriptorWrapper.java
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.repo;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.osgi.core.BundleInfo;
+import org.apache.ivy.osgi.core.BundleInfoAdapter;
+import org.apache.ivy.osgi.core.ExecutionEnvironmentProfileProvider;
+import org.apache.ivy.osgi.core.OSGiManifestParser;
+
+public class ModuleDescriptorWrapper {
+
+ private BundleInfo bundleInfo;
+
+ private DefaultModuleDescriptor md;
+
+ private URI baseUri;
+
+ private ExecutionEnvironmentProfileProvider profileProvider;
+
+ public ModuleDescriptorWrapper(BundleInfo bundleInfo, URI baseUri,
+ ExecutionEnvironmentProfileProvider profileProvider) {
+ this.bundleInfo = bundleInfo;
+ this.baseUri = baseUri;
+ this.profileProvider = profileProvider;
+ }
+
+ public BundleInfo getBundleInfo() {
+ return bundleInfo;
+ }
+
+ public DefaultModuleDescriptor getModuleDescriptor() {
+ if (md == null) {
+ synchronized (this) {
+ if (md != null) {
+ return md;
+ }
+ md = BundleInfoAdapter.toModuleDescriptor(OSGiManifestParser.getInstance(),
+ baseUri, bundleInfo, profileProvider);
+ }
+ }
+ return md;
+ }
+
+ public static Collection<ModuleDescriptor> unwrap(Collection<ModuleDescriptorWrapper> collection) {
+ if (collection == null) {
+ return null;
+ }
+ if (collection.isEmpty()) {
+ return Collections.emptyList();
+ }
+ List<ModuleDescriptor> unwrapped = new ArrayList<ModuleDescriptor>();
+ for (ModuleDescriptorWrapper wrapped : collection) {
+ unwrapped.add(wrapped.getModuleDescriptor());
+ }
+ return unwrapped;
+ }
+
+ @Override
+ public int hashCode() {
+ return bundleInfo.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null || !(obj instanceof ModuleDescriptorWrapper)) {
+ return false;
+ }
+ return bundleInfo.equals(((ModuleDescriptorWrapper) obj).bundleInfo);
+ }
+
+ @Override
+ public String toString() {
+ return getModuleDescriptor().toString();
+ }
+
+}
diff --git a/src/java/org/apache/ivy/osgi/repo/RelativeURLRepository.java b/src/java/org/apache/ivy/osgi/repo/RelativeURLRepository.java
new file mode 100644
index 0000000..29ab44a
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/repo/RelativeURLRepository.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.repo;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.ivy.plugins.repository.Resource;
+import org.apache.ivy.plugins.repository.url.URLRepository;
+import org.apache.ivy.plugins.repository.url.URLResource;
+
+public class RelativeURLRepository extends URLRepository {
+
+ private final URL baseUrl;
+
+ public RelativeURLRepository() {
+ super();
+ baseUrl = null;
+ }
+
+ public RelativeURLRepository(URL baseUrl) {
+ super();
+ this.baseUrl = baseUrl;
+ }
+
+ private Map<String, Resource> resourcesCache = new HashMap<String, Resource>();
+
+ public Resource getResource(String source) throws IOException {
+ source = encode(source);
+ Resource res = resourcesCache.get(source);
+ if (res == null) {
+ URI uri;
+ try {
+ uri = new URI(source);
+ } catch (URISyntaxException e) {
+ // very wierd URL, let's assume it is absolute
+ uri = null;
+ }
+ if (uri == null || uri.isAbsolute()) {
+ res = new URLResource(new URL(source));
+ } else {
+ res = new URLResource(new URL(baseUrl + source));
+ }
+ resourcesCache.put(source, res);
+ }
+ return res;
+ }
+
+ private static String encode(String source) {
+ // TODO: add some more URL encodings here
+ return source.trim().replaceAll(" ", "%20");
+ }
+
+}
diff --git a/src/java/org/apache/ivy/osgi/repo/RepoDescriptor.java b/src/java/org/apache/ivy/osgi/repo/RepoDescriptor.java
new file mode 100644
index 0000000..22a57d1
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/repo/RepoDescriptor.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.repo;
+
+import java.util.Iterator;
+import java.util.Set;
+
+public abstract class RepoDescriptor {
+
+ abstract public Iterator<ModuleDescriptorWrapper> getModules();
+
+ abstract public Set<String> getCapabilities();
+
+ abstract public Set<ModuleDescriptorWrapper> findModules(String requirement, String value);
+
+ abstract public Set<String> getCapabilityValues(String capabilityName);
+
+}
diff --git a/src/java/org/apache/ivy/osgi/repo/RepositoryManifestIterable.java b/src/java/org/apache/ivy/osgi/repo/RepositoryManifestIterable.java
new file mode 100644
index 0000000..b832c09
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/repo/RepositoryManifestIterable.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.repo;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.ivy.plugins.repository.Repository;
+import org.apache.ivy.plugins.repository.Resource;
+import org.apache.ivy.plugins.resolver.util.ResolverHelper;
+
+public class RepositoryManifestIterable extends AbstractFSManifestIterable<String> {
+
+ private final Repository repo;
+
+ /**
+ * Default constructor
+ *
+ * @param root
+ * the root directory of the file system to lookup
+ */
+ public RepositoryManifestIterable(Repository repo) {
+ super("");
+ this.repo = repo;
+ }
+
+ protected URI buildBundleURI(String location) throws IOException {
+ Resource resource = repo.getResource(location);
+ // We have a resource to transform into an URI, let's use some heuristics
+ try {
+ return new URI(resource.getName());
+ } catch (URISyntaxException e) {
+ return new File(resource.getName()).toURI();
+ }
+ }
+
+ protected InputStream getInputStream(String f) throws IOException {
+ return repo.getResource(f).openStream();
+ }
+
+ protected List<String> listBundleFiles(String dir) throws IOException {
+ return asList(ResolverHelper.listAll(repo, dir));
+ }
+
+ protected List<String> listDirs(String dir) throws IOException {
+ return asList(ResolverHelper.listAll(repo, dir));
+ }
+
+ private List<String> asList(String[] array) {
+ return array == null ? Collections.<String> emptyList() : Arrays.<String> asList(array);
+ }
+}
diff --git a/src/java/org/apache/ivy/osgi/repo/ResolverManifestIterable.java b/src/java/org/apache/ivy/osgi/repo/ResolverManifestIterable.java
new file mode 100644
index 0000000..fa1ddf8
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/repo/ResolverManifestIterable.java
@@ -0,0 +1,205 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.repo;
+
+import java.io.IOException;
+import java.net.URI;
+import java.text.ParseException;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.jar.JarInputStream;
+import java.util.jar.Manifest;
+
+import org.apache.ivy.core.event.EventManager;
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.resolve.ResolveData;
+import org.apache.ivy.core.resolve.ResolveEngine;
+import org.apache.ivy.core.resolve.ResolveOptions;
+import org.apache.ivy.core.resolve.ResolvedModuleRevision;
+import org.apache.ivy.core.search.ModuleEntry;
+import org.apache.ivy.core.search.OrganisationEntry;
+import org.apache.ivy.core.search.RevisionEntry;
+import org.apache.ivy.core.settings.IvySettings;
+import org.apache.ivy.core.sort.SortEngine;
+import org.apache.ivy.osgi.core.BundleInfoAdapter;
+import org.apache.ivy.plugins.resolver.BasicResolver;
+import org.apache.ivy.plugins.resolver.util.ResolvedResource;
+import org.apache.ivy.util.Message;
+
+public class ResolverManifestIterable implements Iterable<ManifestAndLocation> {
+
+ // We should support the interface DependencyResolver, but the API is not convenient to get
+ // references to artifact
+ private final BasicResolver resolver;
+
+ public ResolverManifestIterable(BasicResolver resolver) {
+ this.resolver = resolver;
+ }
+
+ public Iterator<ManifestAndLocation> iterator() {
+ return new ResolverManifestIterator();
+ }
+
+ class ResolverManifestIterator implements Iterator<ManifestAndLocation> {
+
+ private OrganisationEntry[] organisations;
+
+ private int indexOrganisation = 0;
+
+ private OrganisationEntry organisation;
+
+ private ModuleEntry[] modules;
+
+ private int indexModule = -1;
+
+ private ModuleEntry module;
+
+ private ManifestAndLocation next = null;
+
+ private RevisionEntry[] revisions;
+
+ private int indexRevision;
+
+ private RevisionEntry revision;
+
+ private Artifact[] artifacts;
+
+ private int indexArtifact;
+
+ private Artifact artifact;
+
+ private ModuleRevisionId mrid;
+
+ private ResolveData data;
+
+ public ResolverManifestIterator() {
+ organisations = resolver.listOrganisations();
+ IvySettings settings = new IvySettings();
+ ResolveEngine engine = new ResolveEngine(settings, new EventManager(), new SortEngine(
+ settings));
+ data = new ResolveData(engine, new ResolveOptions());
+ }
+
+ public boolean hasNext() {
+ while (next == null) {
+ if (organisation == null) {
+ if (indexOrganisation >= organisations.length) {
+ return false;
+ }
+ organisation = organisations[indexOrganisation++];
+ modules = resolver.listModules(organisation);
+ indexModule = 0;
+ module = null;
+ }
+ if (module == null) {
+ if (indexModule >= modules.length) {
+ organisation = null;
+ continue;
+ }
+ module = modules[indexModule++];
+ revisions = resolver.listRevisions(module);
+ indexRevision = 0;
+ revision = null;
+ }
+ if (revision == null) {
+ if (indexRevision >= revisions.length) {
+ module = null;
+ continue;
+ }
+ revision = revisions[indexRevision++];
+ mrid = ModuleRevisionId.newInstance(organisation.getOrganisation(),
+ module.getModule(), revision.getRevision());
+ DefaultDependencyDescriptor dd = new DefaultDependencyDescriptor(mrid, false);
+ ResolvedModuleRevision dependency;
+ try {
+ dependency = resolver.getDependency(dd, data);
+ } catch (ParseException e) {
+ Message.error("Error while resolving " + mrid + " : " + e.getMessage());
+ revision = null;
+ continue;
+ }
+ if (dependency == null) {
+ revision = null;
+ continue;
+ }
+ ModuleDescriptor md = dependency.getDescriptor();
+ mrid = md.getModuleRevisionId();
+ artifacts = md.getAllArtifacts();
+ indexArtifact = 0;
+ artifact = null;
+ }
+ if (artifact == null) {
+ if (indexArtifact >= artifacts.length) {
+ revision = null;
+ continue;
+ }
+ artifact = artifacts[indexArtifact++];
+ }
+ ResolvedResource resource = resolver.doFindArtifactRef(artifact, null);
+ if (resource == null) {
+ artifact = null;
+ continue;
+ }
+ JarInputStream in;
+ try {
+ in = new JarInputStream(resource.getResource().openStream());
+ } catch (IOException e) {
+ Message.warn("Unreadable jar " + resource.getResource().getName() + " ("
+ + e.getMessage() + ")");
+ artifact = null;
+ continue;
+ }
+ Manifest manifest;
+ try {
+ manifest = in.getManifest();
+ } finally {
+ try {
+ in.close();
+ } catch (IOException e) {
+ // don't care
+ }
+ }
+ if (manifest == null) {
+ Message.debug("No manifest on " + artifact);
+ } else {
+ URI uri = BundleInfoAdapter.buildIvyURI(artifact);
+ next = new ManifestAndLocation(manifest, uri, null);
+ }
+ artifact = null;
+ }
+ return true;
+ }
+
+ public ManifestAndLocation next() {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+ ManifestAndLocation manifest = next;
+ next = null;
+ return manifest;
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/java/org/apache/ivy/osgi/updatesite/PluginAdapter.java b/src/java/org/apache/ivy/osgi/updatesite/PluginAdapter.java
new file mode 100644
index 0000000..973c6f2
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/updatesite/PluginAdapter.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.updatesite;
+
+import java.net.URI;
+
+import org.apache.ivy.osgi.core.BundleArtifact;
+import org.apache.ivy.osgi.core.BundleInfo;
+import org.apache.ivy.osgi.core.BundleRequirement;
+import org.apache.ivy.osgi.updatesite.xml.EclipseFeature;
+import org.apache.ivy.osgi.updatesite.xml.EclipsePlugin;
+import org.apache.ivy.osgi.updatesite.xml.Require;
+import org.apache.ivy.osgi.util.VersionRange;
+
+public class PluginAdapter {
+
+ public static BundleInfo featureAsBundle(URI baseUri, EclipseFeature feature) {
+ BundleInfo b = new BundleInfo(feature.getId(), feature.getVersion());
+
+ URI uri;
+ if (feature.getUrl() == null) {
+ uri = baseUri.resolve("features/" + feature.getId() + '_' + feature.getVersion()
+ + ".jar");
+ } else {
+ uri = baseUri.resolve(feature.getUrl());
+ }
+ b.addArtifact(new BundleArtifact(false, uri, null));
+
+ b.setDescription(feature.getDescription());
+ b.setLicense(feature.getLicense());
+
+ for (EclipsePlugin plugin : feature.getPlugins()) {
+ BundleRequirement r = new BundleRequirement(BundleInfo.BUNDLE_TYPE, plugin.getId(),
+ new VersionRange(plugin.getVersion()), null);
+ b.addRequirement(r);
+ }
+
+ for (Require require : feature.getRequires()) {
+ String id;
+ if (require.getPlugin() != null) {
+ id = require.getPlugin();
+ } else {
+ id = require.getFeature();
+ }
+ VersionRange range;
+ if (require.getMatch().equals("greaterOrEqual")) {
+ range = new VersionRange(require.getVersion());
+ } else {
+ throw new IllegalStateException("unsupported match " + require.getMatch());
+ }
+ BundleRequirement r = new BundleRequirement(BundleInfo.BUNDLE_TYPE, id, range, null);
+ b.addRequirement(r);
+ }
+
+ return b;
+ }
+
+ public static BundleInfo pluginAsBundle(URI baseUri, EclipsePlugin plugin) {
+ BundleInfo b = new BundleInfo(plugin.getId(), plugin.getVersion());
+
+ URI uri = baseUri.resolve("plugins/" + plugin.getId() + '_' + plugin.getVersion() + ".jar");
+ b.addArtifact(new BundleArtifact(false, uri, null));
+
+ return b;
+ }
+}
diff --git a/src/java/org/apache/ivy/osgi/updatesite/UpdateSiteDescriptor.java b/src/java/org/apache/ivy/osgi/updatesite/UpdateSiteDescriptor.java
new file mode 100644
index 0000000..15b2291
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/updatesite/UpdateSiteDescriptor.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.updatesite;
+
+import java.net.URI;
+
+import org.apache.ivy.osgi.core.ExecutionEnvironmentProfileProvider;
+import org.apache.ivy.osgi.repo.EditableRepoDescriptor;
+import org.apache.ivy.osgi.updatesite.xml.EclipseFeature;
+import org.apache.ivy.osgi.updatesite.xml.EclipsePlugin;
+
+public class UpdateSiteDescriptor extends EditableRepoDescriptor {
+
+ public UpdateSiteDescriptor(URI baseUri, ExecutionEnvironmentProfileProvider profileProvider) {
+ super(baseUri, profileProvider);
+ }
+
+ public void addFeature(EclipseFeature feature) {
+ addBundle(PluginAdapter.featureAsBundle(getBaseUri(), feature));
+
+ for (EclipsePlugin plugin : feature.getPlugins()) {
+ addBundle(PluginAdapter.pluginAsBundle(getBaseUri(), plugin));
+ }
+ }
+
+}
diff --git a/src/java/org/apache/ivy/osgi/updatesite/UpdateSiteLoader.java b/src/java/org/apache/ivy/osgi/updatesite/UpdateSiteLoader.java
new file mode 100644
index 0000000..6e3f9c9
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/updatesite/UpdateSiteLoader.java
@@ -0,0 +1,338 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.updatesite;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.text.ParseException;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+import org.apache.ivy.core.cache.CacheResourceOptions;
+import org.apache.ivy.core.cache.RepositoryCacheManager;
+import org.apache.ivy.core.event.EventManager;
+import org.apache.ivy.core.report.ArtifactDownloadReport;
+import org.apache.ivy.core.report.DownloadStatus;
+import org.apache.ivy.osgi.core.ExecutionEnvironmentProfileProvider;
+import org.apache.ivy.osgi.p2.P2ArtifactParser;
+import org.apache.ivy.osgi.p2.P2CompositeParser;
+import org.apache.ivy.osgi.p2.P2Descriptor;
+import org.apache.ivy.osgi.p2.P2MetadataParser;
+import org.apache.ivy.osgi.p2.XMLInputParser;
+import org.apache.ivy.osgi.repo.RepoDescriptor;
+import org.apache.ivy.osgi.updatesite.xml.EclipseFeature;
+import org.apache.ivy.osgi.updatesite.xml.EclipseUpdateSiteParser;
+import org.apache.ivy.osgi.updatesite.xml.FeatureParser;
+import org.apache.ivy.osgi.updatesite.xml.UpdateSite;
+import org.apache.ivy.osgi.updatesite.xml.UpdateSiteDigestParser;
+import org.apache.ivy.plugins.repository.url.URLRepository;
+import org.apache.ivy.plugins.repository.url.URLResource;
+import org.apache.ivy.util.Message;
+import org.xml.sax.SAXException;
+
+public class UpdateSiteLoader {
+
+ private final RepositoryCacheManager repositoryCacheManager;
+
+ private final URLRepository urlRepository = new URLRepository();
+
+ private final CacheResourceOptions options;
+
+ private int logLevel = Message.MSG_INFO;
+
+ public UpdateSiteLoader(RepositoryCacheManager repositoryCacheManager,
+ EventManager eventManager, CacheResourceOptions options) {
+ this.repositoryCacheManager = repositoryCacheManager;
+ this.options = options;
+ if (eventManager != null) {
+ urlRepository.addTransferListener(eventManager);
+ }
+ }
+
+ public void setLogLevel(int logLevel) {
+ this.logLevel = logLevel;
+ }
+
+ public RepoDescriptor load(URI repoUri) throws IOException, ParseException, SAXException {
+ if (!repoUri.toString().endsWith("/")) {
+ try {
+ repoUri = new URI(repoUri.toString() + "/");
+ } catch (URISyntaxException e) {
+ throw new RuntimeException("Cannot make an uri for the repo");
+ }
+ }
+ Message.info("Loading the update site " + repoUri);
+ // first look for a p2 repository
+ RepoDescriptor repo = loadP2(repoUri);
+ if (repo != null) {
+ return repo;
+ }
+ Message.verbose("\tNo P2 artifacts, falling back on the old fashioned updatesite");
+ // then try the old update site
+ UpdateSite site = loadSite(repoUri);
+ if (site == null) {
+ return null;
+ }
+ repo = loadFromDigest(site);
+ if (repo != null) {
+ return repo;
+ }
+ return loadFromSite(site);
+ }
+
+ private P2Descriptor loadP2(URI repoUri) throws IOException, ParseException, SAXException {
+ P2Descriptor p2Descriptor = new P2Descriptor(repoUri,
+ ExecutionEnvironmentProfileProvider.getInstance());
+ p2Descriptor.setLogLevel(logLevel);
+ if (!populateP2Descriptor(repoUri, p2Descriptor)) {
+ return null;
+ }
+ p2Descriptor.finish();
+ return p2Descriptor;
+ }
+
+ private boolean populateP2Descriptor(URI repoUri, P2Descriptor p2Descriptor)
+ throws IOException, ParseException, SAXException {
+ Message.verbose("Loading P2 repository " + repoUri);
+ boolean contentExists = readContent(repoUri, p2Descriptor);
+ boolean artifactExists = readArtifacts(repoUri, p2Descriptor);
+ return artifactExists || contentExists;
+ }
+
+ private boolean readContent(URI repoUri, P2Descriptor p2Descriptor) throws IOException,
+ ParseException, SAXException {
+ boolean contentExists = readCompositeContent(repoUri, "compositeContent", p2Descriptor);
+ if (!contentExists) {
+ P2MetadataParser metadataParser = new P2MetadataParser(p2Descriptor);
+ metadataParser.setLogLevel(logLevel);
+ contentExists = readJarOrXml(repoUri, "content", metadataParser);
+ }
+ return contentExists;
+ }
+
+ private boolean readArtifacts(URI repoUri, P2Descriptor p2Descriptor) throws IOException,
+ ParseException, SAXException {
+ boolean artifactExists = readCompositeArtifact(repoUri, "compositeArtifacts", p2Descriptor);
+ if (!artifactExists) {
+ artifactExists = readJarOrXml(repoUri, "artifacts", new P2ArtifactParser(p2Descriptor,
+ repoUri.toURL().toExternalForm()));
+ }
+
+ return artifactExists;
+ }
+
+ private boolean readCompositeContent(URI repoUri, String name, P2Descriptor p2Descriptor)
+ throws IOException, ParseException, SAXException {
+ P2CompositeParser p2CompositeParser = new P2CompositeParser();
+ boolean exist = readJarOrXml(repoUri, name, p2CompositeParser);
+ if (exist) {
+ for (String childLocation : p2CompositeParser.getChildLocations()) {
+ if (!childLocation.endsWith("/")) {
+ childLocation += "/";
+ }
+ URI childUri = repoUri.resolve(childLocation);
+ readContent(childUri, p2Descriptor);
+ }
+ }
+ return exist;
+ }
+
+ private boolean readCompositeArtifact(URI repoUri, String name, P2Descriptor p2Descriptor)
+ throws IOException, ParseException, SAXException {
+ P2CompositeParser p2CompositeParser = new P2CompositeParser();
+ boolean exist = readJarOrXml(repoUri, name, p2CompositeParser);
+ if (exist) {
+ for (String childLocation : p2CompositeParser.getChildLocations()) {
+ if (!childLocation.endsWith("/")) {
+ childLocation += "/";
+ }
+ URI childUri = repoUri.resolve(childLocation);
+ readArtifacts(childUri, p2Descriptor);
+ }
+ }
+ return exist;
+ }
+
+ private boolean readJarOrXml(URI repoUri, String baseName, XMLInputParser reader)
+ throws IOException, ParseException, SAXException {
+ InputStream readIn = null; // the input stream from which the xml should be read
+
+ URL contentUrl = repoUri.resolve(baseName + ".jar").toURL();
+ URLResource res = new URLResource(contentUrl);
+
+ ArtifactDownloadReport report = repositoryCacheManager.downloadRepositoryResource(res,
+ baseName, baseName, "jar", options, urlRepository);
+
+ if (report.getDownloadStatus() == DownloadStatus.FAILED) {
+ // no jar file, try the xml one
+ contentUrl = repoUri.resolve(baseName + ".xml").toURL();
+ res = new URLResource(contentUrl);
+
+ report = repositoryCacheManager.downloadRepositoryResource(res, baseName, baseName,
+ "xml", options, urlRepository);
+
+ if (report.getDownloadStatus() == DownloadStatus.FAILED) {
+ // no xml either
+ return false;
+ }
+
+ readIn = new FileInputStream(report.getLocalFile());
+ } else {
+ InputStream in = new FileInputStream(report.getLocalFile());
+
+ try {
+ // compressed, let's get the pointer on the actual xml
+ readIn = findEntry(in, baseName + ".xml");
+ if (readIn == null) {
+ in.close();
+ return false;
+ }
+ } catch (IOException e) {
+ in.close();
+ throw e;
+ }
+
+ }
+
+ try {
+ reader.parse(readIn);
+ } finally {
+ readIn.close();
+ }
+
+ return true;
+ }
+
+ private UpdateSite loadSite(URI repoUri) throws IOException, ParseException, SAXException {
+ URI siteUri = normalizeSiteUri(repoUri, null);
+ URL u = siteUri.resolve("site.xml").toURL();
+
+ URLResource res = new URLResource(u);
+ ArtifactDownloadReport report = repositoryCacheManager.downloadRepositoryResource(res,
+ "site", "updatesite", "xml", options, urlRepository);
+ if (report.getDownloadStatus() == DownloadStatus.FAILED) {
+ return null;
+ }
+ InputStream in = new FileInputStream(report.getLocalFile());
+ try {
+ UpdateSite site = EclipseUpdateSiteParser.parse(in);
+ site.setUri(normalizeSiteUri(site.getUri(), siteUri));
+ return site;
+ } finally {
+ in.close();
+ }
+ }
+
+ private URI normalizeSiteUri(URI uri, URI defaultValue) {
+ if (uri == null) {
+ return defaultValue;
+ }
+ String uriString = uri.toString();
+ if (uriString.endsWith("site.xml")) {
+ try {
+ return new URI(uriString.substring(0, uriString.length() - 8));
+ } catch (URISyntaxException e) {
+ throw new RuntimeException("Illegal uri", e);
+ }
+ }
+ if (!uriString.endsWith("/")) {
+ try {
+ return new URI(uriString + "/");
+ } catch (URISyntaxException e) {
+ throw new RuntimeException("Illegal uri", e);
+ }
+ }
+ return uri;
+ }
+
+ private UpdateSiteDescriptor loadFromDigest(UpdateSite site) throws IOException,
+ ParseException, SAXException {
+ URI digestBaseUri = site.getDigestUri();
+ if (digestBaseUri == null) {
+ digestBaseUri = site.getUri();
+ } else if (!digestBaseUri.isAbsolute()) {
+ digestBaseUri = site.getUri().resolve(digestBaseUri);
+ }
+ URL digest = digestBaseUri.resolve("digest.zip").toURL();
+ Message.verbose("\tReading " + digest);
+
+ URLResource res = new URLResource(digest);
+ ArtifactDownloadReport report = repositoryCacheManager.downloadRepositoryResource(res,
+ "digest", "digest", "zip", options, urlRepository);
+ if (report.getDownloadStatus() == DownloadStatus.FAILED) {
+ return null;
+ }
+ InputStream in = new FileInputStream(report.getLocalFile());
+ try {
+ ZipInputStream zipped = findEntry(in, "digest.xml");
+ if (zipped == null) {
+ return null;
+ }
+ return UpdateSiteDigestParser.parse(zipped, site);
+ } finally {
+ in.close();
+ }
+ }
+
+ private UpdateSiteDescriptor loadFromSite(UpdateSite site) throws IOException, ParseException,
+ SAXException {
+ UpdateSiteDescriptor repoDescriptor = new UpdateSiteDescriptor(site.getUri(),
+ ExecutionEnvironmentProfileProvider.getInstance());
+
+ for (EclipseFeature feature : site.getFeatures()) {
+ URL url = site.getUri().resolve(feature.getUrl()).toURL();
+
+ URLResource res = new URLResource(url);
+ ArtifactDownloadReport report = repositoryCacheManager.downloadRepositoryResource(res,
+ feature.getId(), "feature", "jar", options, urlRepository);
+ if (report.getDownloadStatus() == DownloadStatus.FAILED) {
+ return null;
+ }
+ InputStream in = new FileInputStream(report.getLocalFile());
+ try {
+ ZipInputStream zipped = findEntry(in, "feature.xml");
+ if (zipped == null) {
+ return null;
+ }
+ EclipseFeature f = FeatureParser.parse(zipped);
+ f.setURL(feature.getUrl());
+ repoDescriptor.addFeature(f);
+ } finally {
+ in.close();
+ }
+ }
+
+ return repoDescriptor;
+ }
+
+ private ZipInputStream findEntry(InputStream in, String entryName) throws IOException {
+ ZipInputStream zipped = new ZipInputStream(in);
+ ZipEntry zipEntry = zipped.getNextEntry();
+ while (zipEntry != null && !zipEntry.getName().equals(entryName)) {
+ zipEntry = zipped.getNextEntry();
+ }
+ if (zipEntry == null) {
+ return null;
+ }
+ return zipped;
+ }
+}
diff --git a/src/java/org/apache/ivy/osgi/updatesite/UpdateSiteResolver.java b/src/java/org/apache/ivy/osgi/updatesite/UpdateSiteResolver.java
new file mode 100644
index 0000000..e8bf6d1
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/updatesite/UpdateSiteResolver.java
@@ -0,0 +1,140 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.updatesite;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.text.ParseException;
+
+import org.apache.ivy.core.cache.ArtifactOrigin;
+import org.apache.ivy.core.cache.CacheResourceOptions;
+import org.apache.ivy.core.cache.DownloadListener;
+import org.apache.ivy.core.cache.RepositoryCacheManager;
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.report.ArtifactDownloadReport;
+import org.apache.ivy.osgi.repo.AbstractOSGiResolver;
+import org.apache.ivy.osgi.repo.RepoDescriptor;
+import org.apache.ivy.plugins.resolver.util.ResolvedResource;
+import org.apache.ivy.util.Message;
+import org.xml.sax.SAXException;
+
+public class UpdateSiteResolver extends AbstractOSGiResolver {
+
+ private String url;
+
+ private Long metadataTtl;
+
+ private Boolean forceMetadataUpdate;
+
+ private String logLevel;
+
+ public void setUrl(String url) {
+ this.url = url;
+ }
+
+ public void setMetadataTtl(Long metadataTtl) {
+ this.metadataTtl = metadataTtl;
+ }
+
+ public void setForceMetadataUpdate(Boolean forceMetadataUpdate) {
+ this.forceMetadataUpdate = forceMetadataUpdate;
+ }
+
+ public void setLogLevel(String logLevel) {
+ this.logLevel = logLevel;
+ }
+
+ protected void init() {
+ if (url == null) {
+ throw new RuntimeException("Missing url");
+ }
+ CacheResourceOptions options = new CacheResourceOptions();
+ if (metadataTtl != null) {
+ options.setTtl(metadataTtl.longValue());
+ }
+ if (forceMetadataUpdate != null) {
+ options.setForce(forceMetadataUpdate.booleanValue());
+ }
+ final int log;
+ if (logLevel != null) {
+ if ("debug".equalsIgnoreCase(logLevel)) {
+ log = Message.MSG_DEBUG;
+ } else if ("verbose".equalsIgnoreCase(logLevel)) {
+ log = Message.MSG_VERBOSE;
+ } else if ("info".equalsIgnoreCase(logLevel)) {
+ log = Message.MSG_INFO;
+ } else if ("warn".equalsIgnoreCase(logLevel)) {
+ log = Message.MSG_WARN;
+ } else if ("error".equalsIgnoreCase(logLevel)) {
+ log = Message.MSG_ERR;
+ } else {
+ throw new RuntimeException("Unknown log level: " + logLevel);
+ }
+ } else {
+ log = Message.MSG_INFO;
+ }
+ options.setListener(new DownloadListener() {
+ public void startArtifactDownload(RepositoryCacheManager cache, ResolvedResource rres,
+ Artifact artifact, ArtifactOrigin origin) {
+ if (log <= Message.MSG_INFO) {
+ Message.info("\tdownloading " + rres.getResource().getName());
+ }
+ }
+
+ public void needArtifact(RepositoryCacheManager cache, Artifact artifact) {
+ if (log <= Message.MSG_VERBOSE) {
+ Message.verbose("\ttrying to download " + artifact);
+ }
+ }
+
+ public void endArtifactDownload(RepositoryCacheManager cache, Artifact artifact,
+ ArtifactDownloadReport adr, File archiveFile) {
+ if (log <= Message.MSG_VERBOSE) {
+ if (adr.isDownloaded()) {
+ Message.verbose("\tdownloaded to " + archiveFile.getAbsolutePath());
+ } else {
+ Message.verbose("\tnothing to download");
+ }
+ }
+ }
+ });
+ UpdateSiteLoader loader = new UpdateSiteLoader(getRepositoryCacheManager(),
+ getEventManager(), options);
+ loader.setLogLevel(log);
+ RepoDescriptor repoDescriptor;
+ try {
+ repoDescriptor = loader.load(new URI(url));
+ } catch (IOException e) {
+ throw new RuntimeException("IO issue while trying to read the update site ("
+ + e.getMessage() + ")");
+ } catch (ParseException e) {
+ throw new RuntimeException("Failed to parse the updatesite (" + e.getMessage() + ")", e);
+ } catch (SAXException e) {
+ throw new RuntimeException("Illformed updatesite (" + e.getMessage() + ")", e);
+ } catch (URISyntaxException e) {
+ throw new RuntimeException("Illformed url (" + e.getMessage() + ")", e);
+ }
+ if (repoDescriptor == null) {
+ setRepoDescriptor(FAILING_REPO_DESCRIPTOR);
+ throw new RuntimeException("No update site was found at the location: " + url);
+ }
+ setRepoDescriptor(repoDescriptor);
+ }
+}
diff --git a/src/java/org/apache/ivy/osgi/updatesite/xml/Archive.java b/src/java/org/apache/ivy/osgi/updatesite/xml/Archive.java
new file mode 100644
index 0000000..6215382
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/updatesite/xml/Archive.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.updatesite.xml;
+
+public class Archive {
+
+ public void setPath(String path) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void setURL(String url) {
+ // TODO Auto-generated method stub
+
+ }
+
+}
diff --git a/src/java/org/apache/ivy/osgi/updatesite/xml/CategoryDef.java b/src/java/org/apache/ivy/osgi/updatesite/xml/CategoryDef.java
new file mode 100644
index 0000000..c26f27f
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/updatesite/xml/CategoryDef.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.updatesite.xml;
+
+public class CategoryDef {
+
+ public void setLabel(String label) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void setName(String name) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void setDescription(String trim) {
+ // TODO Auto-generated method stub
+
+ }
+
+}
diff --git a/src/java/org/apache/ivy/osgi/updatesite/xml/EclipseFeature.java b/src/java/org/apache/ivy/osgi/updatesite/xml/EclipseFeature.java
new file mode 100644
index 0000000..2e21044
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/updatesite/xml/EclipseFeature.java
@@ -0,0 +1,176 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.updatesite.xml;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.ivy.osgi.util.Version;
+
+public class EclipseFeature {
+
+ private String id;
+
+ private Version version;
+
+ private List<EclipsePlugin> plugins = new ArrayList<EclipsePlugin>();
+
+ private List<Require> requires = new ArrayList<Require>();
+
+ private String url;
+
+ private String description;
+
+ private String license;
+
+ public EclipseFeature(String id, Version version) {
+ this.id = id;
+ this.version = version;
+ this.url = "features/" + id + '_' + version + ".jar";
+ }
+
+ public void setURL(String url) {
+ this.url = url;
+ }
+
+ public String getUrl() {
+ return url;
+ }
+
+ public void setType(String type) {
+ // TODO Auto-generated method stub
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public Version getVersion() {
+ return version;
+ }
+
+ public void setLabel(String label) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void setOS(String os) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void setWS(String ws) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void setNL(String nl) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void setArch(String arch) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void setPatch(String patch) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void addCategory(String name) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setCopyright(String trim) {
+ // not useful
+ }
+
+ public void setLicense(String license) {
+ this.license = license;
+ }
+
+ public String getLicense() {
+ return license;
+ }
+
+ public void addPlugin(EclipsePlugin plugin) {
+ plugins.add(plugin);
+ }
+
+ public List<EclipsePlugin> getPlugins() {
+ return plugins;
+ }
+
+ public void addRequire(Require require) {
+ requires.add(require);
+ }
+
+ public List<Require> getRequires() {
+ return requires;
+ }
+
+ public void setApplication(String value) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void setPlugin(String value) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void setExclusive(boolean booleanValue) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void setPrimary(boolean booleanValue) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void setColocationAffinity(String value) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void setProviderName(String value) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void setImage(String value) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public String toString() {
+ return id + "#" + version;
+ }
+}
diff --git a/src/java/org/apache/ivy/osgi/updatesite/xml/EclipsePlugin.java b/src/java/org/apache/ivy/osgi/updatesite/xml/EclipsePlugin.java
new file mode 100644
index 0000000..2226344
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/updatesite/xml/EclipsePlugin.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.updatesite.xml;
+
+import org.apache.ivy.osgi.util.Version;
+
+public class EclipsePlugin {
+
+ private String id;
+
+ private Version version;
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setVersion(Version version) {
+ this.version = version;
+ }
+
+ public Version getVersion() {
+ return version;
+ }
+
+ public void setUnpack(boolean valueOf) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void setFragment(String value) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void setFilter(String value) {
+ // TODO Auto-generated method stub
+
+ }
+
+}
diff --git a/src/java/org/apache/ivy/osgi/updatesite/xml/EclipseUpdateSiteParser.java b/src/java/org/apache/ivy/osgi/updatesite/xml/EclipseUpdateSiteParser.java
new file mode 100644
index 0000000..08f2fb9
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/updatesite/xml/EclipseUpdateSiteParser.java
@@ -0,0 +1,277 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.updatesite.xml;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.text.ParseException;
+
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.apache.ivy.osgi.util.DelegatingHandler;
+import org.apache.ivy.osgi.util.Version;
+import org.apache.ivy.util.XMLHelper;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+public class EclipseUpdateSiteParser {
+
+ public static UpdateSite parse(InputStream in) throws ParseException, IOException, SAXException {
+ SiteHandler handler = new SiteHandler();
+ try {
+ XMLHelper.parse(in, null, handler, null);
+ } catch (ParserConfigurationException e) {
+ throw new SAXException(e);
+ }
+ return handler.updatesite;
+ }
+
+ private static class SiteHandler extends DelegatingHandler {
+
+ private static final String SITE = "site";
+
+ private static final String URL = "url";
+
+ private static final String PACK200 = "pack200";
+
+ private static final String MIRRORS_URL = "mirrorsURL";
+
+ private static final String ASSOCIATE_SITES_URL = "associateSitesURL";
+
+ private static final String DIGEST_URL = "digestURL";
+
+ UpdateSite updatesite;
+
+ public SiteHandler() {
+ super(SITE);
+ // addChild(new DescriptionHandler(), new ChildElementHandler() {
+ // public void childHanlded(DelegetingHandler child) {
+ // updateSite.setDescription(child.getBufferedChars().trim());
+ // }
+ // });
+ addChild(new FeatureHandler(), new ChildElementHandler<FeatureHandler>() {
+ public void childHanlded(FeatureHandler child) {
+ updatesite.addFeature(child.feature);
+ }
+ });
+ // addChild(new ArchiveHandler(), new ChildElementHandler() {
+ // public void childHanlded(DelegetingHandler child) {
+ // updateSite.addArchive(((ArchiveHandler) child).archive);
+ // }
+ // });
+ // addChild(new CategoryDefHandler(), new ChildElementHandler() {
+ // public void childHanlded(DelegetingHandler child) {
+ // updateSite.addCategoryDef(((CategoryDefHandler) child).categoryDef);
+ // }
+ // });
+ }
+
+ protected void handleAttributes(Attributes atts) {
+ updatesite = new UpdateSite();
+
+ String url = atts.getValue(URL);
+ if (url != null && !("".equals(url.trim()))) {
+ if (!url.endsWith("/") && !url.endsWith(File.separator)) {
+ url += "/";
+ }
+ try {
+ updatesite.setUri(new URI(url));
+ } catch (URISyntaxException e) {
+ throw new RuntimeException("illegal url", e);
+ }
+ }
+
+ String mirrorsURL = atts.getValue(MIRRORS_URL);
+ if (mirrorsURL != null && mirrorsURL.trim().length() > 0) {
+ updatesite.setMirrorsURL(mirrorsURL);
+ }
+
+ String pack200 = atts.getValue(PACK200);
+ if (pack200 != null && new Boolean(pack200).booleanValue()) {
+ updatesite.setPack200(true);
+ }
+
+ String digestURL = atts.getValue(DIGEST_URL);
+ if (digestURL != null) {
+ try {
+ updatesite.setDigestUri(new URI(digestURL));
+ } catch (URISyntaxException e) {
+ throw new RuntimeException("illegal url", e);
+ }
+ }
+
+ String associateSitesURL = atts.getValue(ASSOCIATE_SITES_URL);
+ if (associateSitesURL != null) {
+ updatesite.setAssociateSitesURL(associateSitesURL);
+ }
+ }
+ }
+
+ // private static class DescriptionHandler extends DelegetingHandler {
+ //
+ // private static final String DESCRIPTION = "description";
+ //
+ // private static final String URL = "url";
+ //
+ // public DescriptionHandler() {
+ // super(DESCRIPTION);
+ // setBufferingChar(true);
+ // }
+ //
+ // protected void handleAttributes(Attributes atts) {
+ // String url = atts.getValue(URL);
+ // }
+ // }
+
+ private static class FeatureHandler extends DelegatingHandler {
+
+ private static final String FEATURE = "feature";
+
+ private static final String VERSION = "version";
+
+ private static final String ID = "id";
+
+ private static final String URL = "url";
+
+ private static final String PATCH = "patch";
+
+ private static final String ARCH = "arch";
+
+ private static final String NL = "nl";
+
+ private static final String WS = "ws";
+
+ private static final String OS = "os";
+
+ private static final String LABEL = "label";
+
+ private static final String TYPE = "type";
+
+ private EclipseFeature feature;
+
+ public FeatureHandler() {
+ super(FEATURE);
+ addChild(new CategoryHandler(), new ChildElementHandler<CategoryHandler>() {
+ public void childHanlded(CategoryHandler child) {
+ feature.addCategory(child.name);
+ }
+ });
+ }
+
+ protected void handleAttributes(Attributes atts) throws SAXException {
+ String id = atts.getValue(ID);
+ String version = atts.getValue(VERSION);
+ try {
+ feature = new EclipseFeature(id, new Version(version));
+ } catch (ParseException e) {
+ throw new SAXException("Incorrect version on the feature '" + id + "': " + version
+ + " (" + e.getMessage() + ")");
+ }
+
+ String url = atts.getValue(URL);
+ if (url != null) {
+ feature.setURL(url);
+ }
+ feature.setType(atts.getValue(TYPE));
+ feature.setLabel(atts.getValue(LABEL));
+ feature.setOS(atts.getValue(OS));
+ feature.setWS(atts.getValue(WS));
+ feature.setNL(atts.getValue(NL));
+ feature.setArch(atts.getValue(ARCH));
+ feature.setPatch(atts.getValue(PATCH));
+ }
+
+ }
+
+ private static class CategoryHandler extends DelegatingHandler {
+
+ private static final String CATEGORY = "category";
+
+ private static final String NAME = "name";
+
+ String name;
+
+ public CategoryHandler() {
+ super(CATEGORY);
+ }
+
+ protected void handleAttributes(Attributes atts) throws SAXException {
+ name = atts.getValue(NAME);
+ }
+ }
+
+ // private static class ArchiveHandler extends DelegetingHandler {
+ //
+ // private static final String ARCHIVE = "archive";
+ //
+ // private static final String URL = "url";
+ //
+ // private static final String PATH = "path";
+ //
+ // private Archive archive;
+ //
+ // public ArchiveHandler() {
+ // super(ARCHIVE);
+ // }
+ //
+ // protected void handleAttributes(Attributes atts) throws SAXException {
+ // archive = new Archive();
+ //
+ // String path = atts.getValue(PATH);
+ // archive.setPath(path);
+ //
+ // String url = atts.getValue(URL);
+ // archive.setURL(url);
+ //
+ // }
+ // }
+
+ // private static class CategoryDefHandler extends DelegetingHandler {
+ //
+ // private static final String CATEGORY_DEF = "category-def";
+ //
+ // private static final String NAME = "name";
+ //
+ // private static final String LABEL = "label";
+ //
+ // private CategoryDef categoryDef;
+ //
+ // public CategoryDefHandler() {
+ // super(CATEGORY_DEF);
+ // addChild(new DescriptionHandler(), new ChildElementHandler<DescriptionHandler>() {
+ // public void childHanlded(DescriptionHandler child) {
+ // categoryDef.setDescription(child.getBufferedChars().trim());
+ // }
+ // });
+ // }
+ //
+ // protected void handleAttributes(Attributes atts) throws SAXException {
+ // categoryDef = new CategoryDef();
+ //
+ // String name = atts.getValue(NAME);
+ // categoryDef.setName(name);
+ //
+ // String label = atts.getValue(LABEL);
+ // categoryDef.setLabel(label);
+ // }
+ // }
+
+}
diff --git a/src/java/org/apache/ivy/osgi/updatesite/xml/FeatureParser.java b/src/java/org/apache/ivy/osgi/updatesite/xml/FeatureParser.java
new file mode 100644
index 0000000..739b828
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/updatesite/xml/FeatureParser.java
@@ -0,0 +1,390 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.updatesite.xml;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.apache.ivy.osgi.util.DelegatingHandler;
+import org.apache.ivy.osgi.util.Version;
+import org.apache.ivy.util.XMLHelper;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+public class FeatureParser {
+
+ public static EclipseFeature parse(InputStream in) throws ParseException, IOException,
+ SAXException {
+ FeatureHandler handler = new FeatureHandler();
+ try {
+ XMLHelper.parse(in, null, handler, null);
+ } catch (ParserConfigurationException e) {
+ throw new SAXException(e);
+ }
+ return handler.feature;
+ }
+
+ static class FeatureHandler extends DelegatingHandler {
+
+ private static final String FEATURE = "feature";
+
+ private static final String COLOCATION_AFFINITY = "colocation-affinity";
+
+ private static final String PRIMARY = "primary";
+
+ private static final String EXCLUSIVE = "exclusive";
+
+ private static final String PLUGIN = "plugin";
+
+ private static final String APPLICATION = "application";
+
+ private static final String ARCH = "arch";
+
+ private static final String NL = "nl";
+
+ private static final String WS = "ws";
+
+ private static final String OS = "os";
+
+ private static final String VERSION = "version";
+
+ private static final String ID = "id";
+
+ private static final String PROVIDER_NAME = "provider-name";
+
+ private static final String LABEL = "label";
+
+ private static final String IMAGE = "image";
+
+ EclipseFeature feature;
+
+ public FeatureHandler() {
+ super(FEATURE);
+ addChild(new DescriptionHandler(), new ChildElementHandler<DescriptionHandler>() {
+ public void childHanlded(DescriptionHandler child) {
+ feature.setDescription(child.getBufferedChars().trim());
+ }
+ });
+ addChild(new LicenseHandler(), new ChildElementHandler<LicenseHandler>() {
+ public void childHanlded(LicenseHandler child) {
+ feature.setLicense(child.getBufferedChars().trim());
+ }
+ });
+ addChild(new CopyrightHandler(), new ChildElementHandler<CopyrightHandler>() {
+ public void childHanlded(CopyrightHandler child) {
+ feature.setCopyright(child.getBufferedChars().trim());
+ }
+ });
+ addChild(new PluginHandler(), new ChildElementHandler<PluginHandler>() {
+ public void childHanlded(PluginHandler child) {
+ feature.addPlugin(child.plugin);
+ }
+ });
+ addChild(new RequiresHandler(), new ChildElementHandler<RequiresHandler>() {
+ public void childHanlded(RequiresHandler child) {
+ for (Require require : child.requires) {
+ feature.addRequire(require);
+ }
+ }
+ });
+ // addChild(new UrlHandler(), new ChildElementHandler<UrlHandler>() {
+ // public void childHanlded(UrlHandler child) {
+ // }
+ // });
+ }
+
+ protected void handleAttributes(Attributes atts) throws SAXException {
+ String id = atts.getValue(ID);
+ String version = atts.getValue(VERSION);
+ try {
+ feature = new EclipseFeature(id, new Version(version));
+ } catch (ParseException e) {
+ throw new SAXException("Incorrect version on feature '" + id + "': " + version
+ + " (" + e.getMessage() + ")");
+ }
+
+ feature.setOS(atts.getValue(OS));
+ feature.setWS(atts.getValue(WS));
+ feature.setNL(atts.getValue(NL));
+ feature.setArch(atts.getValue(ARCH));
+ feature.setApplication(atts.getValue(APPLICATION));
+ feature.setPlugin(atts.getValue(PLUGIN));
+ feature.setExclusive(Boolean.valueOf(atts.getValue(EXCLUSIVE)).booleanValue());
+ feature.setPrimary(Boolean.valueOf(atts.getValue(PRIMARY)).booleanValue());
+ feature.setColocationAffinity(atts.getValue(COLOCATION_AFFINITY));
+ feature.setProviderName(atts.getValue(PROVIDER_NAME));
+ feature.setLabel(atts.getValue(LABEL));
+ feature.setImage(atts.getValue(IMAGE));
+ }
+
+ }
+
+ private static class PluginHandler extends DelegatingHandler {
+
+ private static final String PLUGIN = "plugin";
+
+ private static final String FILTER = "filter";
+
+ private static final String FRAGMENT = "fragment";
+
+ private static final String UNPACK = "unpack";
+
+ private static final String VERSION = "version";
+
+ private static final String ID = "id";
+
+ private EclipsePlugin plugin;
+
+ public PluginHandler() {
+ super(PLUGIN);
+ }
+
+ protected void handleAttributes(Attributes atts) throws SAXException {
+ plugin = new EclipsePlugin();
+
+ String id = atts.getValue(ID);
+ String version = atts.getValue(VERSION);
+
+ plugin.setId(id);
+ try {
+ plugin.setVersion(new Version(version));
+ } catch (ParseException e) {
+ throw new SAXException("Incorrect version on feature's plugin '" + id + "': "
+ + version + " (" + e.getMessage() + ")");
+ }
+ plugin.setUnpack(Boolean.valueOf(atts.getValue(UNPACK)).booleanValue());
+ plugin.setFragment(atts.getValue(FRAGMENT));
+ plugin.setFilter(atts.getValue(FILTER));
+ }
+ }
+
+ private static class DescriptionHandler extends DelegatingHandler {
+
+ private static final String DESCRIPTION = "description";
+
+ // private static final String URL = "url";
+
+ public DescriptionHandler() {
+ super(DESCRIPTION);
+ setBufferingChar(true);
+ }
+
+ protected void handleAttributes(Attributes atts) throws SAXException {
+ // String url = atts.getValue(URL);
+ }
+ }
+
+ private static class LicenseHandler extends DelegatingHandler {
+
+ private static final String LICENSE = "license";
+
+ // private static final String URL = "url";
+
+ public LicenseHandler() {
+ super(LICENSE);
+ setBufferingChar(true);
+ }
+
+ protected void handleAttributes(Attributes atts) throws SAXException {
+ // String url = atts.getValue(URL);
+ }
+
+ }
+
+ private static class CopyrightHandler extends DelegatingHandler {
+
+ private static final String COPYRIGHT = "copyright";
+
+ // private static final String URL = "url";
+
+ public CopyrightHandler() {
+ super(COPYRIGHT);
+ setBufferingChar(true);
+ }
+
+ protected void handleAttributes(Attributes atts) throws SAXException {
+ // String url = atts.getValue(URL);
+ }
+ }
+
+ static class RequiresHandler extends DelegatingHandler {
+
+ private static final String REQUIRES = "requires";
+
+ List<Require> requires = new ArrayList<Require>();
+
+ public RequiresHandler() {
+ super(REQUIRES);
+ addChild(new ImportHandler(), new ChildElementHandler<ImportHandler>() {
+ public void childHanlded(ImportHandler child) {
+ requires.add(child.require);
+ }
+ });
+ }
+ }
+
+ private static class ImportHandler extends DelegatingHandler {
+
+ Require require;
+
+ private static final String IMPORT = "import";
+
+ private static final String FILTER = "filter";
+
+ private static final String MATCH = "match";
+
+ private static final String VERSION = "version";
+
+ private static final String PLUGIN = "plugin";
+
+ private static final String FEATURE = "feature";
+
+ public ImportHandler() {
+ super(IMPORT);
+ }
+
+ protected void handleAttributes(Attributes atts) throws SAXException {
+ require = new Require();
+
+ String version = atts.getValue(VERSION);
+
+ require.setFeature(atts.getValue(FEATURE));
+ require.setPlugin(atts.getValue(PLUGIN));
+ try {
+ require.setVersion(new Version(version));
+ } catch (ParseException e) {
+ throw new SAXException("Incorrect version on feature's import: " + version + " ("
+ + e.getMessage() + ")");
+ }
+ require.setMatch(atts.getValue(MATCH));
+ require.setFilter(atts.getValue(FILTER));
+ }
+ }
+
+ // private static class IncludesHandler extends DelegetingHandler {
+ //
+ // private static final String INCLUDES = "includes";
+ //
+ // private static final String FILTER = "filter";
+ //
+ // private static final String OPTIONAL = "optional";
+ //
+ // private static final String VERSION = "version";
+ //
+ // private static final String ID = "id";
+ //
+ // public IncludesHandler() {
+ // super(INCLUDES);
+ // }
+ //
+ // protected void handleAttributes(Attributes atts) throws SAXException {
+ // String id = atts.getValue(ID);
+ // String version = atts.getValue(VERSION);
+ // String optional = atts.getValue(OPTIONAL);
+ // String filter = atts.getValue(FILTER);
+ // }
+ //
+ // }
+
+ // private static class InstallHandlerHandler extends DelegetingHandler {
+ //
+ // private static final String INSTALL_HANDLER = "install-handler";
+ //
+ // private static final String URL = "url";
+ //
+ // private static final String LIBRARY = "library";
+ //
+ // private static final String HANDLER = "handler";
+ //
+ // public InstallHandlerHandler() {
+ // super(INSTALL_HANDLER);
+ // }
+ //
+ // protected void handleAttributes(Attributes atts) throws SAXException {
+ // String handler = atts.getValue(HANDLER);
+ // String library = atts.getValue(LIBRARY);
+ // String url = atts.getValue(URL);
+ // }
+ //
+ // }
+
+ // private static class UrlHandler extends DelegetingHandler {
+ //
+ // private static final String URL = "url";
+ //
+ // public UrlHandler() {
+ // super(URL);
+ // addChild(new UpdateHandler(), new ChildElementHandler<UpdateHandler>() {
+ // public void childHanlded(UpdateHandler child) {
+ // }
+ // });
+ // addChild(new DiscoveryHandler(), new ChildElementHandler<DiscoveryHandler>() {
+ // public void childHanlded(DiscoveryHandler child) {
+ // }
+ // });
+ // }
+ //
+ // }
+
+ // private static class UpdateHandler extends DelegetingHandler {
+ //
+ // private static final String UPDATE = "update";
+ //
+ // private static final String LABEL = "label";
+ //
+ // private static final String URL = "url";
+ //
+ // public UpdateHandler() {
+ // super(UPDATE);
+ // }
+ //
+ // protected void handleAttributes(Attributes atts) throws SAXException {
+ // String label = atts.getValue(LABEL);
+ // String url = atts.getValue(URL);
+ // }
+ //
+ // }
+
+ // private static class DiscoveryHandler extends DelegetingHandler {
+ //
+ // private static final String DISCOVERY = "discovery";
+ //
+ // private static final String URL = "url";
+ //
+ // private static final String LABEL = "label";
+ //
+ // private static final String TYPE = "type";
+ //
+ // public DiscoveryHandler() {
+ // super(DISCOVERY);
+ // }
+ //
+ // protected void handleAttributes(Attributes atts) throws SAXException {
+ // String type = atts.getValue(TYPE);
+ // String label = atts.getValue(LABEL);
+ // String url = atts.getValue(URL);
+ // }
+ //
+ // }
+
+}
diff --git a/src/java/org/apache/ivy/osgi/updatesite/xml/Require.java b/src/java/org/apache/ivy/osgi/updatesite/xml/Require.java
new file mode 100644
index 0000000..701bdc8
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/updatesite/xml/Require.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.updatesite.xml;
+
+import org.apache.ivy.osgi.util.Version;
+
+public class Require {
+
+ private String plugin;
+
+ private String feature;
+
+ private Version version;
+
+ private String match;
+
+ private String filter;
+
+ public void setFeature(String feature) {
+ this.feature = feature;
+ }
+
+ public String getFeature() {
+ return feature;
+ }
+
+ public void setPlugin(String plugin) {
+ this.plugin = plugin;
+ }
+
+ public String getPlugin() {
+ return plugin;
+ }
+
+ public void setVersion(Version version) {
+ this.version = version;
+ }
+
+ public Version getVersion() {
+ return version;
+ }
+
+ public void setMatch(String match) {
+ this.match = match;
+ }
+
+ public String getMatch() {
+ return match;
+ }
+
+ public void setFilter(String filter) {
+ this.filter = filter;
+ }
+
+ public String getFilter() {
+ return filter;
+ }
+}
diff --git a/src/java/org/apache/ivy/osgi/updatesite/xml/UpdateSite.java b/src/java/org/apache/ivy/osgi/updatesite/xml/UpdateSite.java
new file mode 100644
index 0000000..de1f84d
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/updatesite/xml/UpdateSite.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.updatesite.xml;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+
+public class UpdateSite {
+
+ private URI uri;
+
+ private String mirrorsURL;
+
+ private boolean pack200;
+
+ private URI digestUri;
+
+ private List<EclipseFeature> features = new ArrayList<EclipseFeature>();
+
+ public void setUri(URI uri) {
+ this.uri = uri;
+ }
+
+ public URI getUri() {
+ return uri;
+ }
+
+ public void setMirrorsURL(String mirrorsURL) {
+ this.mirrorsURL = mirrorsURL;
+ }
+
+ public void setPack200(boolean pack200) {
+ this.pack200 = pack200;
+ }
+
+ public void setDigestUri(URI digestUri) {
+ this.digestUri = digestUri;
+ }
+
+ public URI getDigestUri() {
+ return digestUri;
+ }
+
+ public void addFeature(EclipseFeature feature) {
+ features.add(feature);
+ }
+
+ public List<EclipseFeature> getFeatures() {
+ return features;
+ }
+
+ public void setAssociateSitesURL(String associateSitesURL) {
+ // TODO what's that ?
+ }
+
+}
diff --git a/src/java/org/apache/ivy/osgi/updatesite/xml/UpdateSiteDigestParser.java b/src/java/org/apache/ivy/osgi/updatesite/xml/UpdateSiteDigestParser.java
new file mode 100644
index 0000000..681823b
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/updatesite/xml/UpdateSiteDigestParser.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.updatesite.xml;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.text.ParseException;
+
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.apache.ivy.osgi.core.ExecutionEnvironmentProfileProvider;
+import org.apache.ivy.osgi.updatesite.UpdateSiteDescriptor;
+import org.apache.ivy.osgi.updatesite.xml.FeatureParser.FeatureHandler;
+import org.apache.ivy.osgi.util.DelegatingHandler;
+import org.apache.ivy.util.XMLHelper;
+import org.xml.sax.SAXException;
+
+public class UpdateSiteDigestParser {
+
+ public static UpdateSiteDescriptor parse(InputStream in, UpdateSite site)
+ throws ParseException, IOException, SAXException {
+ DigestHandler handler = new DigestHandler(site);
+ try {
+ XMLHelper.parse(in, null, handler, null);
+ } catch (ParserConfigurationException e) {
+ throw new SAXException(e);
+ }
+ return handler.repoDescriptor;
+ }
+
+ static class DigestHandler extends DelegatingHandler {
+
+ private static final String DIGEST = "digest";
+
+ UpdateSiteDescriptor repoDescriptor;
+
+ public DigestHandler(final UpdateSite site) {
+ super(DIGEST);
+ repoDescriptor = new UpdateSiteDescriptor(site.getUri(),
+ ExecutionEnvironmentProfileProvider.getInstance());
+ addChild(new FeatureHandler(), new ChildElementHandler<FeatureHandler>() {
+ public void childHanlded(FeatureHandler child) {
+ repoDescriptor.addFeature(child.feature);
+ }
+ });
+ }
+
+ }
+
+}
diff --git a/src/java/org/apache/ivy/osgi/util/DelegatingHandler.java b/src/java/org/apache/ivy/osgi/util/DelegatingHandler.java
new file mode 100644
index 0000000..05fb53a
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/util/DelegatingHandler.java
@@ -0,0 +1,636 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.util;
+
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.ivy.util.Message;
+import org.xml.sax.Attributes;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.DTDHandler;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.helpers.DefaultHandler;
+
+public class DelegatingHandler extends DefaultHandler implements DTDHandler, ContentHandler,
+ ErrorHandler {
+
+ private DelegatingHandler delegate = null;
+
+ DelegatingHandler parent;
+
+ private final Map<String, DelegatingHandler> saxHandlerMapping = new HashMap<String, DelegatingHandler>();
+
+ private final Map<String, ChildElementHandler<?>> childHandlerMapping = new HashMap<String, DelegatingHandler.ChildElementHandler<?>>();
+
+ private final String tagName;
+
+ private boolean started = false;
+
+ private boolean skip = false;
+
+ private boolean skipOnError = false;
+
+ private StringBuffer charBuffer = new StringBuffer();
+
+ private boolean bufferingChar = false;
+
+ private Locator locator;
+
+ public DelegatingHandler(String name) {
+ this.tagName = name;
+ charBuffer.setLength(0);
+ }
+
+ protected <DH extends DelegatingHandler> void addChild(DH saxHandler,
+ ChildElementHandler<DH> elementHandler) {
+ saxHandlerMapping.put(saxHandler.getName(), saxHandler);
+ childHandlerMapping.put(saxHandler.getName(), elementHandler);
+ saxHandler.parent = this;
+ }
+
+ public String getName() {
+ return tagName;
+ }
+
+ public DelegatingHandler getParent() {
+ return parent;
+ }
+
+ public void setBufferingChar(boolean bufferingChar) {
+ this.bufferingChar = bufferingChar;
+ }
+
+ public void setSkipOnError(boolean skipOnError) {
+ this.skipOnError = skipOnError;
+ }
+
+ public boolean isBufferingChar() {
+ return bufferingChar;
+ }
+
+ public String getBufferedChars() {
+ return charBuffer.toString();
+ }
+
+ public void setDocumentLocator(Locator locator) {
+ this.locator = locator;
+ for (DelegatingHandler subHandler : saxHandlerMapping.values()) {
+ subHandler.setDocumentLocator(locator);
+ }
+ }
+
+ public Locator getLocator() {
+ return locator;
+ }
+
+ /**
+ * Return an sort of identifier of the current element being parsed. It will only be used for
+ * logging purpose.
+ *
+ * @return an empty string by default
+ */
+ protected String getCurrentElementIdentifier() {
+ return "";
+ }
+
+ public void skip() {
+ skip = true;
+ for (DelegatingHandler subHandler : saxHandlerMapping.values()) {
+ subHandler.stopDelegating();
+ }
+ }
+
+ protected void stopDelegating() {
+ parent.delegate = null;
+ skip = false;
+ started = false;
+ for (DelegatingHandler/* <?> */subHandler : saxHandlerMapping.values()) {
+ subHandler.stopDelegating();
+ }
+ }
+
+ private interface SkipOnErrorCallback {
+ public void call() throws SAXException;
+ }
+
+ private void skipOnError(SkipOnErrorCallback callback) throws SAXException {
+ try {
+ callback.call();
+ } catch (SAXException e) {
+ if (skipOnError) {
+ skip();
+ log(Message.MSG_ERR, e.getMessage(), e);
+ } else {
+ throw e;
+ }
+ }
+ }
+
+ public final void startDocument() throws SAXException {
+ if (skip) {
+ return;
+ }
+ if (delegate != null) {
+ delegate.startDocument();
+ } else {
+ doStartDocument();
+ }
+ }
+
+ /**
+ * @throws SAXException
+ */
+ protected void doStartDocument() throws SAXException {
+ // by default do nothing
+ }
+
+ public final void endDocument() throws SAXException {
+ if (skip) {
+ return;
+ }
+ if (delegate != null) {
+ delegate.endDocument();
+ } else {
+ doEndDocument();
+ }
+ }
+
+ /**
+ * @throws SAXException
+ */
+ protected void doEndDocument() throws SAXException {
+ // by default do nothing
+ }
+
+ public final void startElement(final String uri, final String localName, final String n,
+ final Attributes atts) throws SAXException {
+ // reset the char buffer
+ charBuffer.setLength(0);
+ if (delegate != null) {
+ // we are already delegating, let's continue
+ skipOnError(new SkipOnErrorCallback() {
+ public void call() throws SAXException {
+ delegate.startElement(uri, localName, n, atts);
+ }
+ });
+ } else {
+ if (!started) { // first time called ?
+ // just for the root, check the expected element name
+ // not need to check the delegated as the mapping is already taking care of it
+ if (parent == null && !localName.equals(tagName)) {
+ // we are at the root and the saxed element doesn't match
+ throw new SAXException("The root element of the parsed document '" + localName
+ + "' didn't matched the expected one: '" + tagName + "'");
+ }
+ skipOnError(new SkipOnErrorCallback() {
+ public void call() throws SAXException {
+ handleAttributes(atts);
+ }
+ });
+ started = true;
+ } else {
+ if (skip) {
+ // we con't care anymore about that part of the xml tree
+ return;
+ }
+ // time now to delegate for a new element
+ delegate = saxHandlerMapping.get(localName);
+ if (delegate != null) {
+ skipOnError(new SkipOnErrorCallback() {
+ public void call() throws SAXException {
+ delegate.startElement(uri, localName, n, atts);
+ }
+ });
+ }
+ }
+ }
+ }
+
+ /**
+ * Called when the expected node is achieved
+ *
+ * @param atts
+ * the xml attributes attached to the expected node
+ * @exception SAXException
+ * in case the parsing should be completely stopped
+ */
+ protected void handleAttributes(Attributes atts) throws SAXException {
+ // nothing to do by default
+ }
+
+ /**
+ * @throws SAXException
+ */
+ protected void doStartElement(String uri, String localName, String name, Attributes atts)
+ throws SAXException {
+ // by default do nothing
+ }
+
+ public final void endElement(final String uri, final String localName, final String n)
+ throws SAXException {
+ if (delegate != null) {
+ final DelegatingHandler savedDelegate = delegate;
+ // we are already delegating, let's continue
+ skipOnError(new SkipOnErrorCallback() {
+ public void call() throws SAXException {
+ delegate.endElement(uri, localName, n);
+ }
+ });
+ if (delegate == null) {
+ // we just stopped delegating, it means that the child has ended
+ final ChildElementHandler<?> childHandler = childHandlerMapping.get(localName);
+ if (childHandler != null) {
+ skipOnError(new SkipOnErrorCallback() {
+ public void call() throws SAXException {
+ childHandler._childHanlded(savedDelegate);
+ }
+ });
+ }
+ }
+ } else {
+ if (!skip) {
+ doEndElement(uri, localName, n);
+ }
+ if (parent != null && tagName.equals(localName)) {
+ // the current element is closed, let's tell the parent to stop delegating
+ stopDelegating();
+ }
+ }
+ }
+
+ /**
+ * @throws SAXException
+ */
+ protected void doEndElement(String uri, String localName, String name) throws SAXException {
+ // by default do nothing
+ }
+
+ public static abstract class ChildElementHandler<DH extends DelegatingHandler> {
+
+ public abstract void childHanlded(DH child) throws SAXParseException;
+
+ // because we know what we're doing
+ @SuppressWarnings("unchecked")
+ private void _childHanlded(DelegatingHandler delegate) throws SAXParseException {
+ childHanlded((DH) delegate);
+ }
+
+ }
+
+ public final void characters(char[] ch, int start, int length) throws SAXException {
+ if (skip) {
+ return;
+ }
+ if (delegate != null) {
+ delegate.characters(ch, start, length);
+ } else {
+ doCharacters(ch, start, length);
+ }
+ }
+
+ /**
+ * @throws SAXException
+ */
+ protected void doCharacters(char[] ch, int start, int length) throws SAXException {
+ if (bufferingChar) {
+ charBuffer.append(ch, start, length);
+ }
+ }
+
+ public final void startPrefixMapping(String prefix, String uri) throws SAXException {
+ if (skip) {
+ return;
+ }
+ if (delegate != null) {
+ delegate.startPrefixMapping(prefix, uri);
+ } else {
+ doStartPrefixMapping(prefix, uri);
+ }
+ }
+
+ /**
+ * @throws SAXException
+ */
+ protected void doStartPrefixMapping(String prefix, String uri) throws SAXException {
+ // by default do nothing
+ }
+
+ public final void endPrefixMapping(String prefix) throws SAXException {
+ if (skip) {
+ return;
+ }
+ if (delegate != null) {
+ delegate.endPrefixMapping(prefix);
+ } else {
+ doEndPrefixMapping(prefix);
+ }
+ }
+
+ /**
+ * @throws SAXException
+ */
+ protected void doEndPrefixMapping(String prefix) throws SAXException {
+ // by default do nothing
+ }
+
+ public final void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {
+ if (skip) {
+ return;
+ }
+ if (delegate != null) {
+ delegate.ignorableWhitespace(ch, start, length);
+ } else {
+ doIgnorableWhitespace(ch, start, length);
+ }
+ }
+
+ /**
+ * @throws SAXException
+ */
+ protected void doIgnorableWhitespace(char[] ch, int start, int length) throws SAXException {
+ // by default do nothing
+ }
+
+ public final void notationDecl(String name, String publicId, String systemId)
+ throws SAXException {
+ if (skip) {
+ return;
+ }
+ if (delegate != null) {
+ delegate.notationDecl(name, publicId, systemId);
+ } else {
+ doNotationDecl(name, publicId, systemId);
+ }
+ }
+
+ /**
+ * @throws SAXException
+ */
+ protected void doNotationDecl(String name, String publicId, String systemId)
+ throws SAXException {
+ // by default do nothing
+ }
+
+ public final void processingInstruction(String target, String data) throws SAXException {
+ if (skip) {
+ return;
+ }
+ if (delegate != null) {
+ delegate.processingInstruction(target, data);
+ } else {
+ doProcessingInstruction(target, data);
+ }
+ }
+
+ /**
+ * @throws SAXException
+ */
+ protected void doProcessingInstruction(String target, String data) throws SAXException {
+ // by default do nothing
+ }
+
+ public final void skippedEntity(String name) throws SAXException {
+ if (skip) {
+ return;
+ }
+ if (delegate != null) {
+ delegate.skippedEntity(name);
+ } else {
+ doSkippedEntity(name);
+ }
+ }
+
+ /**
+ * @throws SAXException
+ */
+ protected void doSkippedEntity(String name) throws SAXException {
+ // by default do nothing
+ }
+
+ /**
+ * @throws SAXException
+ */
+ public final void unparsedEntityDecl(String name, String publicId, String systemId,
+ String notationName) throws SAXException {
+ if (skip) {
+ return;
+ }
+ if (delegate != null) {
+ delegate.unparsedEntityDecl(name, publicId, systemId, notationName);
+ } else {
+ doUnparsedEntityDecl(name, publicId, systemId, notationName);
+ }
+ }
+
+ /**
+ * @throws SAXException
+ */
+ protected void doUnparsedEntityDecl(String name, String publicId, String systemId,
+ String notationName) throws SAXException {
+ // by default do nothing
+ }
+
+ // ERROR HANDLING
+
+ public final void warning(SAXParseException exception) throws SAXException {
+ if (skip) {
+ return;
+ }
+ if (delegate != null) {
+ delegate.warning(exception);
+ } else {
+ doWarning(exception);
+ }
+ }
+
+ /**
+ * @throws SAXException
+ */
+ protected void doWarning(SAXParseException exception) throws SAXException {
+ // by default do nothing
+ }
+
+ public final void error(SAXParseException exception) throws SAXException {
+ if (skip) {
+ return;
+ }
+ if (delegate != null) {
+ delegate.error(exception);
+ } else {
+ doError(exception);
+ }
+ }
+
+ /**
+ * @throws SAXException
+ */
+ protected void doError(SAXParseException exception) throws SAXException {
+ // by default do nothing
+ }
+
+ public final void fatalError(SAXParseException exception) throws SAXException {
+ if (skip) {
+ return;
+ }
+ if (delegate != null) {
+ delegate.fatalError(exception);
+ } else {
+ doFatalError(exception);
+ }
+ }
+
+ /**
+ * @throws SAXException
+ */
+ protected void doFatalError(SAXParseException exception) throws SAXException {
+ // by default do nothing
+ }
+
+ // //////////////////////
+ // Functions related to error handling
+ // //////////////////////
+
+ protected void log(int logLevel, String message, Throwable t) {
+ Message.debug(t);
+ log(logLevel, message);
+ }
+
+ protected void log(int logLevel, String message) {
+ Message.log(logLevel, getLocation(getLocator()) + message);
+ }
+
+ protected static String getLocation(Locator locator) {
+ if (locator == null) {
+ return "";
+ }
+ return "[line " + locator.getLineNumber() + " col. " + locator.getColumnNumber() + "] ";
+ }
+
+ private void skipOnError(DelegatingHandler currentHandler,
+ Class<? extends DelegatingHandler> handlerClassToSkip, String message) {
+ DelegatingHandler handlerToSkip = currentHandler;
+ while (!(handlerClassToSkip.isAssignableFrom(handlerToSkip.getClass()))) {
+ handlerToSkip = handlerToSkip.getParent();
+ }
+ log(Message.MSG_ERR, message + ". The '" + handlerToSkip.getName() + "' element "
+ + getCurrentElementIdentifier() + " is then ignored.");
+ handlerToSkip.skip();
+ }
+
+ // //////////////////////
+ // Helpers to parse the attributes
+ // //////////////////////
+
+ protected String getRequiredAttribute(Attributes atts, String name) throws SAXParseException {
+ String value = atts.getValue(name);
+ if (value == null) {
+ throw new SAXParseException("Required attribute '" + name + "' not found", getLocator());
+ }
+ return value;
+ }
+
+ protected String getOptionalAttribute(Attributes atts, String name, String defaultValue) {
+ String value = atts.getValue(name);
+ if (value == null) {
+ return defaultValue;
+ }
+ return value;
+ }
+
+ protected int getRequiredIntAttribute(Attributes atts, String name, Integer logLevel)
+ throws SAXParseException {
+ return parseInt(name, getRequiredAttribute(atts, name));
+ }
+
+ protected Integer getOptionalIntAttribute(Attributes atts, String name, Integer defaultValue)
+ throws SAXParseException {
+ String value = atts.getValue(name);
+ if (value == null) {
+ return defaultValue;
+ }
+ return new Integer(parseInt(name, value));
+ }
+
+ private int parseInt(String name, String value) throws SAXParseException {
+ try {
+ return Integer.parseInt(value);
+ } catch (NumberFormatException e) {
+ throw new SAXParseException("Attribute '" + name
+ + "' is expected to be an integer but was '" + value + "' (" + e.getMessage()
+ + ")", getLocator());
+ }
+ }
+
+ protected long getRequiredLongAttribute(Attributes atts, String name) throws SAXParseException {
+ return parseLong(name, getRequiredAttribute(atts, name));
+ }
+
+ protected Long getOptionalLongAttribute(Attributes atts, String name, Long defaultValue)
+ throws SAXParseException {
+ String value = atts.getValue(name);
+ if (value == null) {
+ return defaultValue;
+ }
+ return new Long(parseLong(name, value));
+ }
+
+ private long parseLong(String name, String value) throws SAXParseException {
+ try {
+ return Long.parseLong(value);
+ } catch (NumberFormatException e) {
+ throw new SAXParseException("Attribute '" + name
+ + "' is expected to be an long but was '" + value + "' (" + e.getMessage()
+ + ")", getLocator());
+ }
+ }
+
+ protected boolean getRequiredBooleanAttribute(Attributes atts, String name)
+ throws SAXParseException {
+ return parseBoolean(name, getRequiredAttribute(atts, name));
+ }
+
+ protected Boolean getOptionalBooleanAttribute(Attributes atts, String name, Boolean defaultValue)
+ throws SAXParseException {
+ String value = atts.getValue(name);
+ if (value == null) {
+ return defaultValue;
+ }
+ return Boolean.valueOf(parseBoolean(name, value));
+ }
+
+ static final String TRUE = Boolean.TRUE.toString().toLowerCase(Locale.US);
+
+ static final String FALSE = Boolean.FALSE.toString().toLowerCase(Locale.US);
+
+ private boolean parseBoolean(String name, String value) throws SAXParseException {
+ String lowerValue = value.toLowerCase(Locale.US);
+ if (lowerValue.equals(TRUE)) {
+ return true;
+ }
+ if (lowerValue.equals(FALSE)) {
+ return false;
+ }
+ throw new SAXParseException("Attribute '" + name
+ + "' is expected to be a boolean but was '" + value + "'", getLocator());
+ }
+}
diff --git a/src/java/org/apache/ivy/osgi/util/ParseUtil.java b/src/java/org/apache/ivy/osgi/util/ParseUtil.java
new file mode 100644
index 0000000..bb31d0c
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/util/ParseUtil.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.util;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class ParseUtil {
+
+ /**
+ * Parses delimited string and returns an array containing the tokens. This parser obeys quotes,
+ * so the delimiter character will be ignored if it is inside of a quote. This method assumes
+ * that the quote character is not included in the set of delimiter characters.
+ *
+ * @param value
+ * the delimited string to parse.
+ * @param delim
+ * the characters delimiting the tokens.
+ * @return an array of string tokens or null if there were no tokens.
+ */
+ // method largely inspired by Apache Felix 1.0.4 ManifestParser method
+ public static String[] parseDelimitedString(String value, String delim) {
+ if (value == null) {
+ value = "";
+ }
+
+ final List<String> list = new ArrayList<String>();
+
+ final int CHAR = 1;
+ final int DELIMITER = 2;
+ final int STARTQUOTE = 4;
+ final int ENDQUOTE = 8;
+
+ final StringBuffer sb = new StringBuffer();
+
+ int expecting = (CHAR | DELIMITER | STARTQUOTE);
+
+ for (int i = 0; i < value.length(); i++) {
+ final char c = value.charAt(i);
+
+ final boolean isDelimiter = (delim.indexOf(c) >= 0);
+ final boolean isQuote = (c == '"');
+
+ if (isDelimiter && ((expecting & DELIMITER) > 0)) {
+ list.add(sb.toString().trim());
+ sb.delete(0, sb.length());
+ expecting = (CHAR | DELIMITER | STARTQUOTE);
+ } else if (isQuote && ((expecting & STARTQUOTE) > 0)) {
+ sb.append(c);
+ expecting = CHAR | ENDQUOTE;
+ } else if (isQuote && ((expecting & ENDQUOTE) > 0)) {
+ sb.append(c);
+ expecting = (CHAR | STARTQUOTE | DELIMITER);
+ } else if ((expecting & CHAR) > 0) {
+ sb.append(c);
+ } else {
+ throw new IllegalArgumentException("Invalid delimited string: " + value);
+ }
+ }
+
+ if (sb.length() > 0) {
+ list.add(sb.toString().trim());
+ }
+
+ return list.toArray(new String[list.size()]);
+ }
+}
diff --git a/src/java/org/apache/ivy/osgi/util/Version.java b/src/java/org/apache/ivy/osgi/util/Version.java
new file mode 100644
index 0000000..4db670e
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/util/Version.java
@@ -0,0 +1,223 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.util;
+
+import java.text.ParseException;
+
+/**
+ * Provides OSGi version support.
+ */
+public class Version implements Comparable<Version> {
+
+ private int major;
+
+ private int minor;
+
+ private int patch;
+
+ private String qualifier;
+
+ private String version;
+
+ private String input;
+
+ private boolean splitted = false;
+
+ private boolean toString = false;
+
+ public Version(String versionStr, String qualifier) throws ParseException {
+ this(qualifier == null ? versionStr : (versionStr + "." + qualifier));
+ }
+
+ public Version(String versionStr) throws ParseException {
+ this.input = versionStr;
+ splitted = false;
+ toString = false;
+ }
+
+ public Version(int major, int minor, int patch, String qualifier) {
+ this.major = major;
+ this.minor = minor;
+ this.patch = patch;
+ this.qualifier = qualifier;
+ splitted = true;
+ toString = false;
+ }
+
+ /**
+ * Build a version from another one while appending an extra qualifier
+ *
+ * @param baseVersion
+ * @param qualifier
+ */
+ public Version(Version baseVersion, String extraQualifier) {
+ this.major = baseVersion.major;
+ this.minor = baseVersion.minor;
+ this.patch = baseVersion.patch;
+ this.qualifier = baseVersion.qualifier == null ? extraQualifier
+ : (baseVersion.qualifier + extraQualifier);
+ splitted = true;
+ toString = false;
+ }
+
+ private void ensureSplitted() {
+ if (!splitted) {
+ synchronized (this) {
+ if (splitted) {
+ return;
+ }
+ String[] splits = input.split("\\.");
+ if (splits == null || splits.length == 0 || splits.length > 4) {
+ throw new RuntimeException(new ParseException("Ill formed OSGi version", 0));
+ }
+ try {
+ major = Integer.parseInt(splits[0]);
+ } catch (NumberFormatException e) {
+ throw new RuntimeException(new ParseException(
+ "Major part of an OSGi version should be an integer", 0));
+ }
+ try {
+ minor = splits.length >= 2 ? Integer.parseInt(splits[1]) : 0;
+ } catch (NumberFormatException e) {
+ throw new RuntimeException(new ParseException(
+ "Minor part of an OSGi version should be an integer", 0));
+ }
+ try {
+ patch = splits.length >= 3 ? Integer.parseInt(splits[2]) : 0;
+ } catch (NumberFormatException e) {
+ throw new RuntimeException(new ParseException(
+ "Patch part of an OSGi version should be an integer", 0));
+ }
+ qualifier = splits.length == 4 ? splits[3] : null;
+ splitted = true;
+ }
+ }
+ }
+
+ private void ensureToString() {
+ if (!toString) {
+ synchronized (this) {
+ if (toString) {
+ return;
+ }
+ ensureSplitted();
+ version = major + "." + minor + "." + patch
+ + (qualifier == null ? "" : "." + qualifier);
+ toString = true;
+ }
+ }
+ }
+
+ public String toString() {
+ ensureToString();
+ return version;
+ }
+
+ public int hashCode() {
+ ensureSplitted();
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + major;
+ result = prime * result + minor;
+ result = prime * result + patch;
+ result = prime * result + ((qualifier == null) ? 0 : qualifier.hashCode());
+ return result;
+ }
+
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (!(obj instanceof Version)) {
+ return false;
+ }
+ Version other = (Version) obj;
+ ensureSplitted();
+ other.ensureSplitted();
+ if (major != other.major) {
+ return false;
+ }
+ if (minor != other.minor) {
+ return false;
+ }
+ if (patch != other.patch) {
+ return false;
+ }
+ if (qualifier == null) {
+ if (other.qualifier != null) {
+ return false;
+ }
+ } else if (!qualifier.equals(other.qualifier)) {
+ return false;
+ }
+ return true;
+ }
+
+ public Version withNudgedPatch() {
+ ensureSplitted();
+ return new Version(major, minor, patch + 1, null);
+ }
+
+ public Version withoutQualifier() {
+ ensureSplitted();
+ return new Version(major, minor, patch, null);
+ }
+
+ public String qualifier() {
+ ensureSplitted();
+ return qualifier == null ? "" : qualifier;
+ }
+
+ public int compareUnqualified(Version other) {
+ ensureSplitted();
+ other.ensureSplitted();
+ int diff = major - other.major;
+ if (diff != 0) {
+ return diff;
+ }
+ diff = minor - other.minor;
+ if (diff != 0) {
+ return diff;
+ }
+ diff = patch - other.patch;
+ if (diff != 0) {
+ return diff;
+ }
+ return 0;
+ }
+
+ public int compareTo(Version other) {
+ ensureSplitted();
+ other.ensureSplitted();
+ int diff = compareUnqualified(other);
+ if (diff != 0) {
+ return diff;
+ }
+ if (qualifier == null) {
+ return other.qualifier != null ? -1 : 0;
+ }
+ if (other.qualifier == null) {
+ return 1;
+ }
+ return qualifier.compareTo(other.qualifier);
+ }
+
+}
diff --git a/src/java/org/apache/ivy/osgi/util/VersionComparator.java b/src/java/org/apache/ivy/osgi/util/VersionComparator.java
new file mode 100644
index 0000000..8872431
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/util/VersionComparator.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.util;
+
+import java.util.Comparator;
+
+public class VersionComparator implements Comparator<Version> {
+
+ public static final Comparator<Version> ASCENDING = new VersionComparator(false);
+
+ public static final Comparator<Version> DESCENDING = new VersionComparator(true);
+
+ public final boolean reverse;
+
+ private VersionComparator(boolean reverse) {
+ this.reverse = reverse;
+ }
+
+ public int compare(Version objA, Version objB) {
+ final int val = objA.compareTo(objB);
+ return (reverse ? -val : val);
+ }
+
+}
diff --git a/src/java/org/apache/ivy/osgi/util/VersionRange.java b/src/java/org/apache/ivy/osgi/util/VersionRange.java
new file mode 100644
index 0000000..575ff0a
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/util/VersionRange.java
@@ -0,0 +1,372 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.util;
+
+import java.text.ParseException;
+
+/**
+ * Provides version range support.
+ */
+public class VersionRange {
+
+ private boolean startExclusive;
+
+ private Version startVersion;
+
+ private boolean endExclusive;
+
+ private Version endVersion;
+
+ public VersionRange(String versionStr) throws ParseException {
+ if (versionStr == null || versionStr.length() == 0) {
+ startExclusive = false;
+ startVersion = new Version(0, 0, 0, null);
+ endExclusive = true;
+ endVersion = null;
+ } else {
+ new VersionRangeParser(versionStr).parse();
+ }
+ }
+
+ class VersionRangeParser {
+
+ /**
+ * value to parse
+ */
+ private final String version;
+
+ /**
+ * the length of the source
+ */
+ private int length;
+
+ /**
+ * position in the source
+ */
+ private int pos = 0;
+
+ /**
+ * last read character
+ */
+ private char c;
+
+ /**
+ * Default constructor
+ *
+ * @param header
+ * the header to parse
+ */
+ VersionRangeParser(String version) {
+ this.version = version;
+ this.length = version.length();
+ }
+
+ /**
+ * Do the parsing
+ *
+ * @throws ParseException
+ */
+ void parse() throws ParseException {
+ boolean range = parseStart();
+ startVersion = parseVersion();
+ if (startVersion == null) {
+ throw new ParseException("Expecting a number", pos);
+ }
+ if (parseVersionSeparator()) {
+ endVersion = parseVersion();
+ parseEnd();
+ } else if (range) {
+ throw new ParseException("Expecting ,", pos);
+ } else {
+ // simple number
+ endVersion = null;
+ startExclusive = false;
+ endExclusive = false;
+ }
+ }
+
+ private char readNext() {
+ if (pos == length) {
+ c = '\0';
+ } else {
+ c = version.charAt(pos++);
+ }
+ return c;
+ }
+
+ private void unread() {
+ if (pos > 0) {
+ pos--;
+ }
+ }
+
+ private boolean parseStart() {
+ skipWhiteSpace();
+ switch (readNext()) {
+ case '[':
+ startExclusive = false;
+ return true;
+ case '(':
+ startExclusive = true;
+ return true;
+ default:
+ unread();
+ return false;
+ }
+ }
+
+ private void skipWhiteSpace() {
+ do {
+ switch (readNext()) {
+ case ' ':
+ continue;
+ default:
+ unread();
+ return;
+ }
+ } while (pos < length);
+ }
+
+ private Version parseVersion() {
+ Integer major = parseNumber();
+ if (major == null) {
+ return null;
+ }
+ Integer minor = new Integer(0);
+ Integer patch = new Integer(0);
+ String qualififer = null;
+ if (parseNumberSeparator()) {
+ minor = parseNumber();
+ if (minor == null) {
+ minor = new Integer(0);
+ } else if (parseNumberSeparator()) {
+ patch = parseNumber();
+ if (patch == null) {
+ patch = new Integer(0);
+ } else if (parseNumberSeparator()) {
+ qualififer = parseQualifier();
+ }
+ }
+ }
+ return new Version(major.intValue(), minor.intValue(), patch.intValue(), qualififer);
+ }
+
+ private Integer parseNumber() {
+ skipWhiteSpace();
+ Integer n = null;
+ do {
+ switch (readNext()) {
+ case '\0':
+ return n;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ n = new Integer((n == null ? 0 : n.intValue() * 10) + c - '0');
+ break;
+ default:
+ unread();
+ return n;
+ }
+ } while (pos < length);
+ return n;
+ }
+
+ private boolean parseNumberSeparator() {
+ switch (readNext()) {
+ case '.':
+ return true;
+ default:
+ unread();
+ return false;
+ }
+ }
+
+ private boolean parseVersionSeparator() {
+ skipWhiteSpace();
+ switch (readNext()) {
+ case ',':
+ return true;
+ default:
+ unread();
+ return false;
+ }
+ }
+
+ private String parseQualifier() {
+ StringBuffer q = new StringBuffer();
+ do {
+ readNext();
+ if (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9'
+ || c == '-' || c == '_') {
+ q.append(c);
+ } else {
+ unread();
+ break;
+ }
+ } while (pos < length);
+ if (q.length() == 0) {
+ return null;
+ }
+ return q.toString();
+ }
+
+ private void parseEnd() throws ParseException {
+ skipWhiteSpace();
+ switch (readNext()) {
+ case ']':
+ endExclusive = false;
+ break;
+ case ')':
+ endExclusive = true;
+ break;
+ default:
+ unread();
+ throw new ParseException("Expexting ] or )", pos);
+ }
+ }
+ }
+
+ public VersionRange(boolean startExclusive, Version startVersion, boolean endExclusive,
+ Version endVersion) {
+ this.startExclusive = startExclusive;
+ this.startVersion = startVersion;
+ this.endExclusive = endExclusive;
+ this.endVersion = endVersion;
+ }
+
+ public VersionRange(Version startVersion) {
+ this.startExclusive = false;
+ this.startVersion = startVersion;
+ this.endExclusive = true;
+ this.endVersion = null;
+ }
+
+ public String toString() {
+ return (startExclusive ? "(" : "[") + startVersion + ","
+ + (endVersion == null ? "" : endVersion.toString()) + (endExclusive ? ")" : "]");
+ }
+
+ public String toIvyRevision() {
+ StringBuffer buffer = new StringBuffer();
+ buffer.append(startExclusive ? "(" : "[");
+ buffer.append(startVersion);
+ if (endVersion == null) {
+ buffer.append(",)");
+ } else if (!endExclusive || startVersion.equals(endVersion)) {
+ buffer.append(",");
+ buffer.append(endVersion.withNudgedPatch());
+ buffer.append(")");
+ } else {
+ buffer.append(",");
+ buffer.append(endVersion);
+ buffer.append(")");
+ }
+ return buffer.toString();
+ }
+
+ public boolean isEndExclusive() {
+ return this.endExclusive;
+ }
+
+ public Version getEndVersion() {
+ return this.endVersion;
+ }
+
+ public boolean isStartExclusive() {
+ return this.startExclusive;
+ }
+
+ public Version getStartVersion() {
+ return this.startVersion;
+ }
+
+ public boolean isClosedRange() {
+ return startVersion.equals(endVersion);
+ }
+
+ public boolean contains(String versionStr) throws ParseException {
+ return contains(new Version(versionStr));
+ }
+
+ public boolean contains(Version version) {
+ if (startExclusive ? version.compareUnqualified(startVersion) <= 0 : version
+ .compareUnqualified(startVersion) < 0) {
+ return false;
+ }
+ if (endVersion == null) {
+ return true;
+ }
+ if (endExclusive ? version.compareUnqualified(endVersion) >= 0 : version
+ .compareUnqualified(endVersion) > 0) {
+ return false;
+ }
+ return true;
+ }
+
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + (endExclusive ? 1231 : 1237);
+ result = prime * result + ((endVersion == null) ? 0 : endVersion.hashCode());
+ result = prime * result + (startExclusive ? 1231 : 1237);
+ result = prime * result + ((startVersion == null) ? 0 : startVersion.hashCode());
+ return result;
+ }
+
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (!(obj instanceof VersionRange)) {
+ return false;
+ }
+ VersionRange other = (VersionRange) obj;
+ if (endExclusive != other.endExclusive) {
+ return false;
+ }
+ if (endVersion == null) {
+ if (other.endVersion != null) {
+ return false;
+ }
+ } else if (!endVersion.equals(other.endVersion)) {
+ return false;
+ }
+ if (startExclusive != other.startExclusive) {
+ return false;
+ }
+ if (startVersion == null) {
+ if (other.startVersion != null) {
+ return false;
+ }
+ } else if (!startVersion.equals(other.startVersion)) {
+ return false;
+ }
+ return true;
+ }
+
+}
diff --git a/src/java/org/apache/ivy/osgi/util/ZipUtil.java b/src/java/org/apache/ivy/osgi/util/ZipUtil.java
new file mode 100644
index 0000000..92737c3
--- /dev/null
+++ b/src/java/org/apache/ivy/osgi/util/ZipUtil.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.osgi.util;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
+
+/**
+ * Class derived from code posted here: http://forums.sun.com/thread.jspa?messageID=2160923
+ */
+public class ZipUtil {
+ public static void zip(File sourceDir, OutputStream targetStream) throws IOException {
+ if (!sourceDir.isFile() && !sourceDir.isDirectory()) {
+ return;
+ }
+
+ final ZipOutputStream cpZipOutputStream = new ZipOutputStream(targetStream);
+ cpZipOutputStream.setLevel(9);
+ zipFiles(sourceDir, sourceDir, cpZipOutputStream);
+ cpZipOutputStream.finish();
+ cpZipOutputStream.close();
+ }
+
+ private static void zipFiles(File rootDir, File currDir, ZipOutputStream zos)
+ throws IOException {
+ if (currDir.isDirectory()) {
+ final File[] files = currDir.listFiles();
+ for (int i = 0; i < files.length; i++) {
+ zipFiles(rootDir, files[i], zos);
+ }
+ } else {
+ final String strAbsPath = currDir.getPath();
+ final String strZipEntryName = strAbsPath.substring(rootDir.getPath().length() + 1,
+ strAbsPath.length());
+
+ final byte[] b = new byte[(int) (currDir.length())];
+ final FileInputStream fis = new FileInputStream(currDir);
+ fis.read(b);
+ fis.close();
+
+ final ZipEntry entry = new ZipEntry(strZipEntryName);
+ zos.putNextEntry(entry);
+ zos.write(b, 0, (int) currDir.length());
+ zos.closeEntry();
+ }
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/IvySettingsAware.java b/src/java/org/apache/ivy/plugins/IvySettingsAware.java
new file mode 100644
index 0000000..3690614
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/IvySettingsAware.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins;
+
+import org.apache.ivy.core.settings.IvySettings;
+
+public interface IvySettingsAware {
+ void setSettings(IvySettings settings);
+}
diff --git a/src/java/org/apache/ivy/plugins/circular/AbstractCircularDependencyStrategy.java b/src/java/org/apache/ivy/plugins/circular/AbstractCircularDependencyStrategy.java
new file mode 100644
index 0000000..7b3ed10
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/circular/AbstractCircularDependencyStrategy.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.circular;
+
+public abstract class AbstractCircularDependencyStrategy implements CircularDependencyStrategy {
+ private String name;
+
+ protected AbstractCircularDependencyStrategy(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ public String toString() {
+ return getName();
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/circular/AbstractLogCircularDependencyStrategy.java b/src/java/org/apache/ivy/plugins/circular/AbstractLogCircularDependencyStrategy.java
new file mode 100644
index 0000000..4d42173
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/circular/AbstractLogCircularDependencyStrategy.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.circular;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+
+import org.apache.ivy.core.IvyContext;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.resolve.ResolveData;
+
+public abstract class AbstractLogCircularDependencyStrategy extends
+ AbstractCircularDependencyStrategy {
+
+ protected AbstractLogCircularDependencyStrategy(String name) {
+ super(name);
+ }
+
+ private Collection/* <String> */circularDependencies = new HashSet();
+
+ public void handleCircularDependency(ModuleRevisionId[] mrids) {
+ String circularDependencyId = getCircularDependencyId(mrids);
+ if (!circularDependencies.contains(circularDependencyId)) {
+ circularDependencies.add(circularDependencyId);
+ logCircularDependency(mrids);
+ }
+ }
+
+ protected abstract void logCircularDependency(ModuleRevisionId[] mrids);
+
+ protected String getCircularDependencyId(ModuleRevisionId[] mrids) {
+ String contextPrefix = "";
+ ResolveData data = IvyContext.getContext().getResolveData();
+ if (data != null) {
+ contextPrefix = data.getOptions().getResolveId() + " ";
+ }
+ return contextPrefix + Arrays.asList(mrids);
+ }
+
+}
diff --git a/src/java/org/apache/ivy/plugins/circular/CircularDependencyException.java b/src/java/org/apache/ivy/plugins/circular/CircularDependencyException.java
new file mode 100644
index 0000000..0b37dfd
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/circular/CircularDependencyException.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.circular;
+
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+
+/**
+ * Unchecked exception thrown when a circular dependency exists between projects.
+ */
+
+public class CircularDependencyException extends RuntimeException {
+
+ private ModuleRevisionId[] mrids;
+
+ /**
+ * @param descriptors
+ * module descriptors in order of circular dependency
+ */
+ public CircularDependencyException(final ModuleRevisionId[] mrids) {
+ super(CircularDependencyHelper.formatMessage(mrids));
+ this.mrids = mrids;
+ }
+
+ public ModuleRevisionId[] getPath() {
+ return this.mrids;
+ }
+
+}
diff --git a/src/java/org/apache/ivy/plugins/circular/CircularDependencyHelper.java b/src/java/org/apache/ivy/plugins/circular/CircularDependencyHelper.java
new file mode 100644
index 0000000..59d5595
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/circular/CircularDependencyHelper.java
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.circular;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+
+public final class CircularDependencyHelper {
+
+ /** CircularDependencyHelper is not designed to be an instance */
+ private CircularDependencyHelper() {
+
+ }
+
+ /**
+ * Returns a string representation of this circular dependency graph
+ *
+ * @param descriptors
+ * in order of circular dependency
+ * @return a string representation of this circular dependency graph
+ */
+ public static String formatMessage(final ModuleRevisionId[] mrids) {
+ Set alreadyAdded = new HashSet();
+ StringBuffer buff = new StringBuffer();
+ buff.append(mrids[0]);
+ alreadyAdded.add(mrids[0]);
+ for (int i = 1; i < mrids.length; i++) {
+ buff.append("->");
+ if (alreadyAdded.add(mrids[i])) {
+ buff.append(mrids[i]);
+ } else {
+ buff.append("...");
+ break;
+ }
+ }
+ return buff.toString();
+ }
+
+ public static String formatMessage(final ModuleDescriptor[] descriptors) {
+ return formatMessage(toMrids(descriptors));
+ }
+
+ /**
+ * @param loopElements
+ * a List<ModuleDescriptor>
+ */
+ public static String formatMessageFromDescriptors(List loopElements) {
+ ModuleRevisionId[] mrids = new ModuleRevisionId[loopElements.size()];
+ int pos = 0;
+ for (Iterator it = loopElements.iterator(); it.hasNext();) {
+ ModuleDescriptor descriptor = (ModuleDescriptor) it.next();
+ mrids[pos] = descriptor.getModuleRevisionId();
+ pos++;
+ }
+ return formatMessage(mrids);
+ }
+
+ public static ModuleRevisionId[] toMrids(ModuleDescriptor[] descriptors) {
+ ModuleRevisionId[] mrids = new ModuleRevisionId[descriptors.length];
+ for (int i = 0; i < descriptors.length; i++) {
+ mrids[i] = descriptors[i].getModuleRevisionId();
+ }
+ return mrids;
+ }
+
+}
diff --git a/src/java/org/apache/ivy/plugins/circular/CircularDependencyStrategy.java b/src/java/org/apache/ivy/plugins/circular/CircularDependencyStrategy.java
new file mode 100644
index 0000000..f1ae506
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/circular/CircularDependencyStrategy.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.circular;
+
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+
+/**
+ * A CircularDependencyStrategy indicates what ivy does when a circular dependency is detected. Ivy
+ * can ignore it, warn the user, or interupt the processing.
+ */
+public interface CircularDependencyStrategy {
+ String getName();
+
+ void handleCircularDependency(ModuleRevisionId[] mrids) throws CircularDependencyException;
+
+}
diff --git a/src/java/org/apache/ivy/plugins/circular/ErrorCircularDependencyStrategy.java b/src/java/org/apache/ivy/plugins/circular/ErrorCircularDependencyStrategy.java
new file mode 100644
index 0000000..93aa387
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/circular/ErrorCircularDependencyStrategy.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.circular;
+
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+
+public final class ErrorCircularDependencyStrategy extends AbstractCircularDependencyStrategy {
+
+ private static final CircularDependencyStrategy INSTANCE = new ErrorCircularDependencyStrategy();
+
+ public static CircularDependencyStrategy getInstance() {
+ return INSTANCE;
+ }
+
+ private ErrorCircularDependencyStrategy() {
+ super("error");
+ }
+
+ public void handleCircularDependency(ModuleRevisionId[] mrids)
+ throws CircularDependencyException {
+ throw new CircularDependencyException(mrids);
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/circular/IgnoreCircularDependencyStrategy.java b/src/java/org/apache/ivy/plugins/circular/IgnoreCircularDependencyStrategy.java
new file mode 100644
index 0000000..23c5950
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/circular/IgnoreCircularDependencyStrategy.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.circular;
+
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.util.Message;
+
+public final class IgnoreCircularDependencyStrategy extends AbstractLogCircularDependencyStrategy {
+
+ private static final CircularDependencyStrategy INSTANCE = new IgnoreCircularDependencyStrategy();
+
+ public static CircularDependencyStrategy getInstance() {
+ return INSTANCE;
+ }
+
+ private IgnoreCircularDependencyStrategy() {
+ super("ignore");
+ }
+
+ protected void logCircularDependency(ModuleRevisionId[] mrids) {
+ Message.verbose("circular dependency found: "
+ + CircularDependencyHelper.formatMessage(mrids));
+ }
+
+}
diff --git a/src/java/org/apache/ivy/plugins/circular/WarnCircularDependencyStrategy.java b/src/java/org/apache/ivy/plugins/circular/WarnCircularDependencyStrategy.java
new file mode 100644
index 0000000..c247947
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/circular/WarnCircularDependencyStrategy.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.circular;
+
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.util.Message;
+
+public final class WarnCircularDependencyStrategy extends AbstractLogCircularDependencyStrategy {
+
+ private static final CircularDependencyStrategy INSTANCE = new WarnCircularDependencyStrategy();
+
+ public static CircularDependencyStrategy getInstance() {
+ return INSTANCE;
+ }
+
+ private WarnCircularDependencyStrategy() {
+ super("warn");
+ }
+
+ protected void logCircularDependency(ModuleRevisionId[] mrids) {
+ Message.warn("circular dependency found: " + CircularDependencyHelper.formatMessage(mrids));
+ }
+
+}
diff --git a/src/java/org/apache/ivy/plugins/conflict/AbstractConflictManager.java b/src/java/org/apache/ivy/plugins/conflict/AbstractConflictManager.java
new file mode 100644
index 0000000..ee68337
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/conflict/AbstractConflictManager.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.conflict;
+
+import java.util.Collection;
+
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.settings.IvySettings;
+import org.apache.ivy.plugins.IvySettingsAware;
+
+public abstract class AbstractConflictManager implements ConflictManager, IvySettingsAware {
+ private String name;
+
+ private IvySettings settings;
+
+ public IvySettings getSettings() {
+ return settings;
+ }
+
+ public void setSettings(IvySettings settings) {
+ this.settings = settings;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String toString() {
+ return name;
+ }
+
+ public void handleAllBlacklistedRevisions(DependencyDescriptor dd, Collection foundBlacklisted) {
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/conflict/ConflictManager.java b/src/java/org/apache/ivy/plugins/conflict/ConflictManager.java
new file mode 100644
index 0000000..46c2060
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/conflict/ConflictManager.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.conflict;
+
+import java.util.Collection;
+
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.resolve.IvyNode;
+
+public interface ConflictManager {
+ /**
+ * Resolves the eventual conflicts found in the given collection of IvyNode. This method return
+ * a Collection of IvyNode which have not been evicted. The given conflicts Collection contains
+ * at least one IvyNode. This method can be called with IvyNodes which are not yet loaded. If
+ * this conflict manager is not able to resolve conflicts with the current data found in the
+ * IvyNodes and need them to be fully loaded, it will return null to indicate that no conflict
+ * resolution has been done.
+ *
+ * @param parent
+ * the ivy node parent for which the conflict is to be resolved
+ * @param conflicts
+ * the collection of IvyNode to check for conflicts
+ * @return a Collection of IvyNode which have not been evicted, or null if conflict management
+ * resolution is not possible yet
+ */
+ Collection resolveConflicts(IvyNode parent, Collection conflicts);
+
+ String getName();
+
+ /**
+ * Method called when all revisions available for a version constraint have been blacklisted,
+ * and thus the dependency can't be resolved.
+ * <p>
+ * This will never happen if the conflict manager doesn't blacklist any module, so providing an
+ * empty implementation in this case is fine.
+ * </p>
+ *
+ * @param dd
+ * the dependency descriptor for which all revisions are blacklisted.
+ * @param foundBlacklisted
+ * the list of all ModuleRevisionId found which are blacklisted
+ */
+ void handleAllBlacklistedRevisions(DependencyDescriptor dd,
+ Collection/* <ModuleRevisionId> */foundBlacklisted);
+}
diff --git a/src/java/org/apache/ivy/plugins/conflict/FixedConflictManager.java b/src/java/org/apache/ivy/plugins/conflict/FixedConflictManager.java
new file mode 100644
index 0000000..34815bf
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/conflict/FixedConflictManager.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.conflict;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.apache.ivy.core.resolve.IvyNode;
+
+public class FixedConflictManager extends AbstractConflictManager {
+ private Collection revisions;
+
+ public FixedConflictManager(String[] revs) {
+ revisions = Arrays.asList(revs);
+ setName("fixed" + revisions);
+ }
+
+ public Collection resolveConflicts(IvyNode parent, Collection conflicts) {
+ Collection resolved = new ArrayList(conflicts.size());
+ for (Iterator iter = conflicts.iterator(); iter.hasNext();) {
+ IvyNode node = (IvyNode) iter.next();
+ String revision = node.getResolvedId().getRevision();
+ if (revisions.contains(revision)) {
+ resolved.add(node);
+ }
+ }
+ return resolved;
+ }
+
+ public Collection getRevs() {
+ return revisions;
+ }
+
+}
diff --git a/src/java/org/apache/ivy/plugins/conflict/LatestCompatibleConflictManager.java b/src/java/org/apache/ivy/plugins/conflict/LatestCompatibleConflictManager.java
new file mode 100644
index 0000000..f4e775c
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/conflict/LatestCompatibleConflictManager.java
@@ -0,0 +1,312 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.conflict;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.Stack;
+
+import org.apache.ivy.core.IvyContext;
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.resolve.IvyNode;
+import org.apache.ivy.core.resolve.IvyNodeBlacklist;
+import org.apache.ivy.core.resolve.IvyNodeCallers.Caller;
+import org.apache.ivy.core.resolve.IvyNodeEviction.EvictionData;
+import org.apache.ivy.core.resolve.ResolveData;
+import org.apache.ivy.core.resolve.RestartResolveProcess;
+import org.apache.ivy.core.settings.IvySettings;
+import org.apache.ivy.plugins.latest.LatestStrategy;
+import org.apache.ivy.plugins.version.VersionMatcher;
+import org.apache.ivy.util.Message;
+
+/**
+ * This conflict manager can be used to allow only compatible dependencies to be used together (like
+ * the strict conflict manager), but it has the advantage of using a best effort algorithm to find a
+ * set of compatible dependencies, even if it requires stepping back to older revisions (as long as
+ * they are in the set of compatibility).
+ * <p>
+ * Here is an example of what this conflict manager is able to do:<br/>
+ * <b>Available Modules</b>:
+ *
+ * <pre>
+ * #A;2->{ #B;[1.0,1.5] #C;[2.0,2.5] }
+ * #B;1.4->#D;1.5
+ * #B;1.5->#D;2.0
+ * #C;2.5->#D;[1.0,1.6]
+ * </pre>
+ *
+ * <b>Result</b>: #B;1.4, #C;2.5, #D;1.5<br/>
+ * <b>Details</b>The conflict manager finds that the latest matching version of #B (1.5) depends on
+ * a version of #D incompatible with what is expected by the latest matching version of #C. Hence
+ * the conflict manager blacklists #B;1.5, and the version range [1.0,1.5] is resolved again to end
+ * up with #B;1.4 which depends on #D;1.5, which is fine to work with #C;2.5.
+ * </p>
+ */
+public class LatestCompatibleConflictManager extends LatestConflictManager {
+ public LatestCompatibleConflictManager() {
+ }
+
+ public LatestCompatibleConflictManager(String name, LatestStrategy strategy) {
+ super(name, strategy);
+ }
+
+ public Collection resolveConflicts(IvyNode parent, Collection conflicts) {
+ if (conflicts.size() < 2) {
+ return conflicts;
+ }
+ VersionMatcher versionMatcher = getSettings().getVersionMatcher();
+
+ Iterator iter = conflicts.iterator();
+ IvyNode node = (IvyNode) iter.next();
+ ModuleRevisionId mrid = node.getResolvedId();
+
+ if (versionMatcher.isDynamic(mrid)) {
+ while (iter.hasNext()) {
+ IvyNode other = (IvyNode) iter.next();
+ if (versionMatcher.isDynamic(other.getResolvedId())) {
+ // two dynamic versions in conflict, not enough information yet
+ return null;
+ } else if (!versionMatcher.accept(mrid, other.getResolvedId())) {
+ // incompatibility found
+ if (!handleIncompatibleConflict(parent, conflicts, node, other)) {
+ return null;
+ }
+ }
+ }
+ // no incompatibility nor dynamic version found, let's return the latest static version
+ if (conflicts.size() == 2) {
+ // very common special case of only two modules in conflict,
+ // let's return the second one (static)
+ Iterator it = conflicts.iterator();
+ it.next();
+ return Collections.singleton(it.next());
+ }
+ Collection newConflicts = new LinkedHashSet(conflicts);
+ newConflicts.remove(node);
+ return super.resolveConflicts(parent, newConflicts);
+ } else {
+ // the first node is a static revision, let's see if all other versions match
+ while (iter.hasNext()) {
+ IvyNode other = (IvyNode) iter.next();
+ if (!versionMatcher.accept(other.getResolvedId(), mrid)) {
+ // incompatibility found
+ if (!handleIncompatibleConflict(parent, conflicts, node, other)) {
+ return null;
+ }
+ }
+ }
+ // no incompatibility found, let's return this static version
+ return Collections.singleton(node);
+ }
+ }
+
+ /**
+ * Handles an incompatible conflict
+ * <p>
+ * An incompatible conflicts is handled with this pseudo algorithm:
+ *
+ * <pre>
+ * take latest among two nodes in conflict
+ * for all callers
+ * if dependency is a version constraint (dynamic)
+ * blacklist the mapped version
+ * else
+ * recurse for all callers
+ * if a version constraint has been found
+ * restart resolve
+ * else
+ * throw strict conflict exception
+ * </pre>
+ *
+ * </p>
+ *
+ * @param parent
+ * the parent node of nodes in conflict
+ * @param conflicts
+ * all the nodes in conflict
+ * @param node
+ * one of the two incompatible nodes
+ * @param other
+ * the other incompatible node
+ * @return true if the incompatible conflict has been handled, false otherwise (in which case
+ * resolveConflicts should return null)
+ */
+ private boolean handleIncompatibleConflict(IvyNode parent, Collection conflicts, IvyNode node,
+ IvyNode other) {
+ // we never actually return anything else than false or throw an exception,
+ // but returning a boolean make the calling code cleaner
+ try {
+ IvyNodeArtifactInfo latest = (IvyNodeArtifactInfo) getStrategy().findLatest(
+ toArtifactInfo(Arrays.asList(new IvyNode[] {node, other})), null);
+ if (latest != null) {
+ IvyNode latestNode = latest.getNode();
+ IvyNode oldestNode = latestNode == node ? other : node;
+ blackListIncompatibleCallerAndRestartResolveIfPossible(getSettings(), parent,
+ oldestNode, latestNode);
+ // if we arrive here, we haven't managed to blacklist all paths to the latest
+ // node, we try with the oldest
+ blackListIncompatibleCallerAndRestartResolveIfPossible(getSettings(), parent,
+ latestNode, oldestNode);
+ // still not possible, we aren't able to find a solution to the incompatibility
+ handleUnsolvableConflict(parent, conflicts, node, other);
+
+ return true; // never actually reached
+ } else {
+ return false;
+ }
+ } catch (NoConflictResolvedYetException ex) {
+ // we have not enough informations in the nodes to resolve conflict
+ // according to the resolveConflicts contract, resolveConflicts must return null
+ return false;
+ }
+ }
+
+ private void blackListIncompatibleCallerAndRestartResolveIfPossible(IvySettings settings,
+ IvyNode parent, IvyNode selected, IvyNode evicted) {
+ Stack callerStack = new Stack();
+ callerStack.push(evicted);
+ final Collection toBlacklist = blackListIncompatibleCaller(settings.getVersionMatcher(),
+ parent, selected, evicted, callerStack);
+ if (toBlacklist != null) {
+ final StringBuffer blacklisted = new StringBuffer();
+ for (Iterator iterator = toBlacklist.iterator(); iterator.hasNext();) {
+ IvyNodeBlacklist blacklist = (IvyNodeBlacklist) iterator.next();
+ blacklist.getBlacklistedNode().blacklist(blacklist);
+ blacklisted.append(blacklist.getBlacklistedNode());
+ if (iterator.hasNext()) {
+ blacklisted.append(" ");
+ }
+ }
+
+ String rootModuleConf = parent.getData().getReport().getConfiguration();
+ evicted.markEvicted(new EvictionData(rootModuleConf, parent, this, Collections
+ .singleton(selected), "with blacklisting of " + blacklisted));
+
+ if (settings.debugConflictResolution()) {
+ Message.debug("evicting " + evicted + " by "
+ + evicted.getEvictedData(rootModuleConf));
+ }
+ throw new RestartResolveProcess("trying to handle incompatibilities between "
+ + selected + " and " + evicted);
+ }
+ }
+
+ private boolean handleIncompatibleCaller(Stack callerStack, IvyNode node, IvyNode callerNode,
+ IvyNode conflictParent, IvyNode selectedNode, IvyNode evictedNode,
+ Collection blacklisted, VersionMatcher versionMatcher) {
+ if (callerStack.subList(0, callerStack.size() - 1).contains(node)) {
+ // circular dependency found and handled: the current top of the stack (node)
+ // was already contained in the rest of the stack, the circle is closed, nothing
+ // else to do
+ return true;
+ } else {
+ callerStack.push(callerNode);
+ Collection sub = blackListIncompatibleCaller(versionMatcher, conflictParent,
+ selectedNode, evictedNode, callerStack);
+ callerStack.pop();
+ if (sub == null) {
+ // propagate the fact that a path with unblacklistable caller has been found
+ return false;
+ } else {
+ blacklisted.addAll(sub);
+ return true;
+ }
+ }
+ }
+
+ /**
+ * Tries to blacklist exactly one version for all callers paths.
+ *
+ * @param versionMatcher
+ * the version matcher to use to interpret versions
+ * @param conflictParent
+ * the node in which the conflict is occurring
+ * @param selectedNode
+ * the node in favor of which the conflict is resolved
+ * @param evictedNode
+ * the node which will be evicted if we are able to blacklist all paths
+ * @param node
+ * the node for which callers should be considered
+ * @return the collection of blacklisting to do, null if a blacklist is not possible in at least
+ * one caller path
+ */
+ private Collection/* <IvyNodeBlacklist> */blackListIncompatibleCaller(
+ VersionMatcher versionMatcher, IvyNode conflictParent, IvyNode selectedNode,
+ IvyNode evictedNode, Stack/* <IvyNode> */callerStack) {
+ Collection/* <IvyNodeBlacklist> */blacklisted = new ArrayList/* <IvyNodeBlacklist> */();
+ IvyNode node = (IvyNode) callerStack.peek();
+ String rootModuleConf = conflictParent.getData().getReport().getConfiguration();
+ Caller[] callers = node.getCallers(rootModuleConf);
+ for (int i = 0; i < callers.length; i++) {
+ IvyNode callerNode = node.findNode(callers[i].getModuleRevisionId());
+ if (callerNode.isBlacklisted(rootModuleConf)) {
+ continue;
+ }
+ if (versionMatcher.isDynamic(callers[i].getAskedDependencyId(node.getData()))) {
+ blacklisted.add(new IvyNodeBlacklist(conflictParent, selectedNode, evictedNode,
+ node, rootModuleConf));
+ if (node.isEvicted(rootModuleConf)
+ && !handleIncompatibleCaller(callerStack, node, callerNode, conflictParent,
+ selectedNode, evictedNode, blacklisted, versionMatcher)) {
+ return null;
+ }
+ } else if (!handleIncompatibleCaller(callerStack, node, callerNode, conflictParent,
+ selectedNode, evictedNode, blacklisted, versionMatcher)) {
+ return null;
+ }
+ }
+ if (blacklisted.isEmpty() && !callerStack.subList(0, callerStack.size() - 1).contains(node)) {
+ return null;
+ }
+ return blacklisted;
+ }
+
+ protected void handleUnsolvableConflict(IvyNode parent, Collection conflicts, IvyNode node1,
+ IvyNode node2) {
+ throw new StrictConflictException(node1, node2);
+ }
+
+ public void handleAllBlacklistedRevisions(DependencyDescriptor dd,
+ Collection/* <ModuleRevisionId> */foundBlacklisted) {
+ ResolveData resolveData = IvyContext.getContext().getResolveData();
+ Collection/* <IvyNode> */blacklisted = new HashSet();
+ for (Iterator iterator = foundBlacklisted.iterator(); iterator.hasNext();) {
+ ModuleRevisionId mrid = (ModuleRevisionId) iterator.next();
+ blacklisted.add(resolveData.getNode(mrid));
+ }
+
+ for (Iterator iterator = blacklisted.iterator(); iterator.hasNext();) {
+ IvyNode node = (IvyNode) iterator.next();
+ IvyNodeBlacklist bdata = node.getBlacklistData(resolveData.getReport()
+ .getConfiguration());
+ handleUnsolvableConflict(bdata.getConflictParent(),
+ Arrays.asList(new Object[] {bdata.getEvictedNode(), bdata.getSelectedNode()}),
+ bdata.getEvictedNode(), bdata.getSelectedNode());
+ }
+ }
+
+ public String toString() {
+ return getName();
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/conflict/LatestConflictManager.java b/src/java/org/apache/ivy/plugins/conflict/LatestConflictManager.java
new file mode 100644
index 0000000..aafe069
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/conflict/LatestConflictManager.java
@@ -0,0 +1,171 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.conflict;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.resolve.IvyNode;
+import org.apache.ivy.plugins.latest.ArtifactInfo;
+import org.apache.ivy.plugins.latest.LatestStrategy;
+import org.apache.ivy.util.Message;
+
+public class LatestConflictManager extends AbstractConflictManager {
+ public static class NoConflictResolvedYetException extends RuntimeException {
+ }
+
+ protected static final class IvyNodeArtifactInfo implements ArtifactInfo {
+ private final IvyNode node;
+
+ private IvyNodeArtifactInfo(IvyNode dep) {
+ node = dep;
+ }
+
+ public long getLastModified() {
+ long lastModified = node.getLastModified();
+ if (lastModified == 0) {
+ // if the last modified timestamp is unknown, we can't resolve
+ // the conflicts now, and trigger an exception which will be catched
+ // in the main resolveConflicts method
+ throw new NoConflictResolvedYetException();
+ } else {
+ return lastModified;
+ }
+ }
+
+ public String getRevision() {
+ return node.getResolvedId().getRevision();
+ }
+
+ public IvyNode getNode() {
+ return node;
+ }
+ }
+
+ private LatestStrategy strategy;
+
+ private String strategyName;
+
+ public LatestConflictManager() {
+ }
+
+ public LatestConflictManager(LatestStrategy strategy) {
+ this.strategy = strategy;
+ }
+
+ public LatestConflictManager(String name, LatestStrategy strategy) {
+ setName(name);
+ this.strategy = strategy;
+ }
+
+ public Collection resolveConflicts(IvyNode parent, Collection conflicts) {
+ if (conflicts.size() < 2) {
+ return conflicts;
+ }
+ for (Iterator iter = conflicts.iterator(); iter.hasNext();) {
+ IvyNode node = (IvyNode) iter.next();
+ DependencyDescriptor dd = node.getDependencyDescriptor(parent);
+ if (dd != null && dd.isForce()
+ && parent.getResolvedId().equals(dd.getParentRevisionId())) {
+ return Collections.singleton(node);
+ }
+ }
+
+ /*
+ * If the list of conflicts contains dynamic revisions, delay the conflict calculation until
+ * they are resolved. TODO: we probably could already evict some of the dynamic revisions!
+ */
+ for (Iterator iter = conflicts.iterator(); iter.hasNext();) {
+ IvyNode node = (IvyNode) iter.next();
+ ModuleRevisionId modRev = node.getResolvedId();
+ if (getSettings().getVersionMatcher().isDynamic(modRev)) {
+ return null;
+ }
+ }
+
+ ArrayList unevicted = new ArrayList();
+ for (Iterator iter = conflicts.iterator(); iter.hasNext();) {
+ IvyNode node = (IvyNode) iter.next();
+ if (!node.isCompletelyEvicted())
+ unevicted.add(node);
+ }
+ if (unevicted.size() > 0) {
+ conflicts = unevicted;
+ }
+
+ try {
+ IvyNodeArtifactInfo latest = (IvyNodeArtifactInfo) getStrategy().findLatest(
+ toArtifactInfo(conflicts), null);
+ if (latest != null) {
+ return Collections.singleton(latest.getNode());
+ } else {
+ return conflicts;
+ }
+ } catch (NoConflictResolvedYetException ex) {
+ // we have not enough informations in the nodes to resolve conflict
+ // according to the resolveConflicts contract, we must return null
+ return null;
+ }
+ }
+
+ protected ArtifactInfo[] toArtifactInfo(Collection conflicts) {
+ List artifacts = new ArrayList(conflicts.size());
+ for (Iterator iter = conflicts.iterator(); iter.hasNext();) {
+ IvyNode node = (IvyNode) iter.next();
+ artifacts.add(new IvyNodeArtifactInfo(node));
+ }
+ return (ArtifactInfo[]) artifacts.toArray(new ArtifactInfo[artifacts.size()]);
+ }
+
+ public LatestStrategy getStrategy() {
+ if (strategy == null) {
+ if (strategyName != null) {
+ strategy = getSettings().getLatestStrategy(strategyName);
+ if (strategy == null) {
+ Message.error("unknown latest strategy: " + strategyName);
+ strategy = getSettings().getDefaultLatestStrategy();
+ }
+ } else {
+ strategy = getSettings().getDefaultLatestStrategy();
+ }
+ }
+ return strategy;
+ }
+
+ /**
+ * To conform to configurator API
+ *
+ * @param latestStrategy
+ */
+ public void setLatest(String strategyName) {
+ this.strategyName = strategyName;
+ }
+
+ public void setStrategy(LatestStrategy strategy) {
+ this.strategy = strategy;
+ }
+
+ public String toString() {
+ return strategy != null ? String.valueOf(strategy) : strategyName;
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/conflict/NoConflictManager.java b/src/java/org/apache/ivy/plugins/conflict/NoConflictManager.java
new file mode 100644
index 0000000..cd6f5ce
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/conflict/NoConflictManager.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.conflict;
+
+import java.util.Collection;
+
+import org.apache.ivy.core.resolve.IvyNode;
+
+public class NoConflictManager extends AbstractConflictManager {
+ public NoConflictManager() {
+ setName("all");
+ }
+
+ public Collection resolveConflicts(IvyNode parent, Collection conflicts) {
+ return conflicts;
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/conflict/RegexpConflictManager.java b/src/java/org/apache/ivy/plugins/conflict/RegexpConflictManager.java
new file mode 100644
index 0000000..b003cbf
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/conflict/RegexpConflictManager.java
@@ -0,0 +1,120 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.conflict;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.ivy.core.resolve.IvyNode;
+import org.apache.ivy.util.Message;
+
+/**
+ * A ConflictManager that can be used to resolve conflicts based on regular expressions of the
+ * revision of the module. The conflict manager is added like this:
+ *
+ * <pre>
+ * <!-- Match all revisions, but ignore the last dot(.) and the character after it.
+ * Used to match api changes in out milestones. -->
+ * <conflict-managers>
+ * <regexp-cm name="regexp"
+ * regexp="(.*)\..$" ignoreNonMatching="true"/>
+ * </conflict-managers>
+ * </pre>
+ *
+ * The regular expression must contain a capturing group. The group will be used to resolve the
+ * conflicts by an String.equals() test. If ignoreNonMatching is false non matching modules will
+ * result in an exception. If it is true they will be compaired by their full revision.
+ */
+public class RegexpConflictManager extends AbstractConflictManager {
+ private Pattern pattern = Pattern.compile("(.*)");
+
+ private boolean mIgnoreNonMatching;
+
+ public RegexpConflictManager() {
+ }
+
+ public void setRegexp(String regexp) {
+ pattern = Pattern.compile(regexp);
+ Matcher matcher = pattern.matcher("abcdef");
+ if (matcher.groupCount() != 1) {
+ String message = "Pattern does not contain ONE (capturing group): '" + pattern + "'";
+ Message.error(message);
+ throw new IllegalArgumentException(message);
+ }
+ }
+
+ public void setIgnoreNonMatching(boolean ignoreNonMatching) {
+ mIgnoreNonMatching = ignoreNonMatching;
+ }
+
+ public Collection resolveConflicts(IvyNode parent, Collection conflicts) {
+ IvyNode lastNode = null;
+ for (Iterator iter = conflicts.iterator(); iter.hasNext();) {
+ IvyNode node = (IvyNode) iter.next();
+
+ if (lastNode != null && !matchEquals(node, lastNode)) {
+ String msg = lastNode + ":" + getMatch(lastNode) + " (needed by "
+ + Arrays.asList(lastNode.getAllRealCallers()) + ") conflicts with " + node
+ + ":" + getMatch(node) + " (needed by "
+ + Arrays.asList(node.getAllRealCallers()) + ")";
+ throw new StrictConflictException(msg);
+ }
+ if (lastNode == null || nodeIsGreater(node, lastNode)) {
+ lastNode = node;
+ }
+ }
+
+ return Collections.singleton(lastNode);
+ }
+
+ private boolean nodeIsGreater(IvyNode node, IvyNode lastNode) {
+ return getMatch(node).compareTo(getMatch(lastNode)) > 0;
+ }
+
+ private boolean matchEquals(IvyNode lastNode, IvyNode node) {
+ return getMatch(lastNode).equals(getMatch(node));
+ }
+
+ private String getMatch(IvyNode node) {
+ String revision = node.getId().getRevision();
+ Matcher matcher = pattern.matcher(revision);
+ if (matcher.matches()) {
+ String match = matcher.group(1);
+ if (match != null) {
+ return match;
+ }
+ warnOrThrow("First group of pattern: '" + pattern + "' does not match: " + revision
+ + " " + node);
+ } else {
+ warnOrThrow("Pattern: '" + pattern + "' does not match: " + revision + " " + node);
+ }
+ return revision;
+ }
+
+ private void warnOrThrow(String message) {
+ if (mIgnoreNonMatching) {
+ Message.warn(message);
+ } else {
+ throw new StrictConflictException(message);
+ }
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/conflict/StrictConflictException.java b/src/java/org/apache/ivy/plugins/conflict/StrictConflictException.java
new file mode 100644
index 0000000..b2e0441
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/conflict/StrictConflictException.java
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.conflict;
+
+import java.util.Arrays;
+
+import org.apache.ivy.core.resolve.IvyNode;
+import org.apache.ivy.core.resolve.ResolveProcessException;
+
+public class StrictConflictException extends ResolveProcessException {
+
+ public StrictConflictException() {
+ super();
+ }
+
+ public StrictConflictException(IvyNode node1, IvyNode node2) {
+ super(node1 + " (needed by " + Arrays.asList(node1.getAllRealCallers())
+ + ") conflicts with " + node2 + " (needed by "
+ + Arrays.asList(node2.getAllRealCallers()) + ")");
+ }
+
+ public StrictConflictException(String msg) {
+ super(msg);
+ }
+
+ public StrictConflictException(Throwable t) {
+ super(t);
+ }
+
+ public StrictConflictException(String msg, Throwable t) {
+ super(msg, t);
+ }
+
+}
diff --git a/src/java/org/apache/ivy/plugins/conflict/StrictConflictManager.java b/src/java/org/apache/ivy/plugins/conflict/StrictConflictManager.java
new file mode 100644
index 0000000..7d3fde4
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/conflict/StrictConflictManager.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.conflict;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+
+import org.apache.ivy.core.resolve.IvyNode;
+import org.apache.ivy.plugins.version.VersionMatcher;
+
+public class StrictConflictManager extends AbstractConflictManager {
+
+ public StrictConflictManager() {
+ }
+
+ public Collection resolveConflicts(IvyNode parent, Collection conflicts) {
+ VersionMatcher versionMatcher = getSettings().getVersionMatcher();
+
+ IvyNode lastNode = null;
+ for (Iterator iter = conflicts.iterator(); iter.hasNext();) {
+ IvyNode node = (IvyNode) iter.next();
+
+ if (versionMatcher.isDynamic(node.getResolvedId())) {
+ // dynamic revision, not enough information to resolve conflict
+ return null;
+ }
+
+ if (lastNode != null && !lastNode.equals(node)) {
+ throw new StrictConflictException(lastNode, node);
+ }
+ lastNode = node;
+ }
+
+ return Collections.singleton(lastNode);
+ }
+
+}
diff --git a/src/java/org/apache/ivy/plugins/latest/AbstractLatestStrategy.java b/src/java/org/apache/ivy/plugins/latest/AbstractLatestStrategy.java
new file mode 100644
index 0000000..9c35d51
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/latest/AbstractLatestStrategy.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.latest;
+
+import java.util.Date;
+import java.util.List;
+import java.util.ListIterator;
+
+public abstract class AbstractLatestStrategy implements LatestStrategy {
+ private String name;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String toString() {
+ return name;
+ }
+
+ public ArtifactInfo findLatest(ArtifactInfo[] infos, Date date) {
+ List l = sort(infos);
+
+ // the latest revision comes last, use a ListIterator to iterate the
+ // sorted list in the reverse direction.
+ for (ListIterator iter = l.listIterator(l.size()); iter.hasPrevious();) {
+ ArtifactInfo info = (ArtifactInfo) iter.previous();
+ if (date == null || info.getLastModified() < date.getTime()) {
+ return info;
+ }
+ }
+ return null;
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/latest/ArtifactInfo.java b/src/java/org/apache/ivy/plugins/latest/ArtifactInfo.java
new file mode 100644
index 0000000..f76bc13
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/latest/ArtifactInfo.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.latest;
+
+public interface ArtifactInfo {
+ String getRevision();
+
+ long getLastModified();
+}
diff --git a/src/java/org/apache/ivy/plugins/latest/ComparatorLatestStrategy.java b/src/java/org/apache/ivy/plugins/latest/ComparatorLatestStrategy.java
new file mode 100644
index 0000000..8ad6677
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/latest/ComparatorLatestStrategy.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.latest;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+public class ComparatorLatestStrategy extends AbstractLatestStrategy {
+
+ private Comparator comparator;
+
+ public ComparatorLatestStrategy() {
+ }
+
+ public ComparatorLatestStrategy(Comparator comparator) {
+ this.comparator = comparator;
+ }
+
+ public List sort(ArtifactInfo[] infos) {
+ List ret = new ArrayList(Arrays.asList(infos));
+ Collections.sort(ret, comparator);
+ return ret;
+ }
+
+ public Comparator getComparator() {
+ return comparator;
+ }
+
+ public void setComparator(Comparator comparator) {
+ this.comparator = comparator;
+ }
+
+}
diff --git a/src/java/org/apache/ivy/plugins/latest/LatestLexicographicStrategy.java b/src/java/org/apache/ivy/plugins/latest/LatestLexicographicStrategy.java
new file mode 100644
index 0000000..b33bce8
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/latest/LatestLexicographicStrategy.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.latest;
+
+import java.util.Comparator;
+
+public class LatestLexicographicStrategy extends ComparatorLatestStrategy {
+ /**
+ * Compares two revisions. Revisions are compared lexicographically unless a 'latest' revision
+ * is found. If the latest revision found is an absolute latest (latest. like), then it is
+ * assumed to be the greater. If a partial latest is found, then it is assumed to be greater
+ * than any matching fixed revision.
+ */
+ private static final Comparator COMPARATOR = new Comparator() {
+ public int compare(Object o1, Object o2) {
+ String rev1 = ((ArtifactInfo) o1).getRevision();
+ String rev2 = ((ArtifactInfo) o2).getRevision();
+ if (rev1.startsWith("latest")) {
+ return 1;
+ }
+ if (rev1.endsWith("+") && rev2.startsWith(rev1.substring(0, rev1.length() - 1))) {
+ return 1;
+ }
+ if (rev2.startsWith("latest")) {
+ return -1;
+ }
+ if (rev2.endsWith("+") && rev1.startsWith(rev2.substring(0, rev2.length() - 1))) {
+ return -1;
+ }
+ return rev1.compareTo(rev2);
+ }
+
+ };
+
+ public LatestLexicographicStrategy() {
+ super(COMPARATOR);
+ setName("latest-lexico");
+ }
+
+}
diff --git a/src/java/org/apache/ivy/plugins/latest/LatestRevisionStrategy.java b/src/java/org/apache/ivy/plugins/latest/LatestRevisionStrategy.java
new file mode 100644
index 0000000..51d72d1
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/latest/LatestRevisionStrategy.java
@@ -0,0 +1,198 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.latest;
+
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+import org.apache.ivy.core.IvyContext;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.plugins.version.VersionMatcher;
+
+public class LatestRevisionStrategy extends ComparatorLatestStrategy {
+ /**
+ * Compares two ModuleRevisionId by their revision. Revisions are compared using an algorithm
+ * inspired by PHP version_compare one.
+ */
+ final class MridComparator implements Comparator {
+ public int compare(Object o1, Object o2) {
+ String rev1 = ((ModuleRevisionId) o1).getRevision();
+ String rev2 = ((ModuleRevisionId) o2).getRevision();
+
+ rev1 = rev1.replaceAll("([a-zA-Z])(\\d)", "$1.$2");
+ rev1 = rev1.replaceAll("(\\d)([a-zA-Z])", "$1.$2");
+ rev2 = rev2.replaceAll("([a-zA-Z])(\\d)", "$1.$2");
+ rev2 = rev2.replaceAll("(\\d)([a-zA-Z])", "$1.$2");
+
+ String[] parts1 = rev1.split("[\\._\\-\\+]");
+ String[] parts2 = rev2.split("[\\._\\-\\+]");
+
+ int i = 0;
+ for (; i < parts1.length && i < parts2.length; i++) {
+ if (parts1[i].equals(parts2[i])) {
+ continue;
+ }
+ boolean is1Number = isNumber(parts1[i]);
+ boolean is2Number = isNumber(parts2[i]);
+ if (is1Number && !is2Number) {
+ return 1;
+ }
+ if (is2Number && !is1Number) {
+ return -1;
+ }
+ if (is1Number && is2Number) {
+ return Long.valueOf(parts1[i]).compareTo(Long.valueOf(parts2[i]));
+ }
+ // both are strings, we compare them taking into account special meaning
+ Map specialMeanings = getSpecialMeanings();
+ Integer sm1 = (Integer) specialMeanings.get(parts1[i].toLowerCase(Locale.US));
+ Integer sm2 = (Integer) specialMeanings.get(parts2[i].toLowerCase(Locale.US));
+ if (sm1 != null) {
+ sm2 = sm2 == null ? new Integer(0) : sm2;
+ return sm1.compareTo(sm2);
+ }
+ if (sm2 != null) {
+ return new Integer(0).compareTo(sm2);
+ }
+ return parts1[i].compareTo(parts2[i]);
+ }
+ if (i < parts1.length) {
+ return isNumber(parts1[i]) ? 1 : -1;
+ }
+ if (i < parts2.length) {
+ return isNumber(parts2[i]) ? -1 : 1;
+ }
+ return 0;
+ }
+
+ private boolean isNumber(String str) {
+ return str.matches("\\d+");
+ }
+ }
+
+ /**
+ * Compares two ArtifactInfo by their revision. Revisions are compared using an algorithm
+ * inspired by PHP version_compare one, unless a dynamic revision is given, in which case the
+ * version matcher is used to perform the comparison.
+ */
+ final class ArtifactInfoComparator implements Comparator {
+ public int compare(Object o1, Object o2) {
+ String rev1 = ((ArtifactInfo) o1).getRevision();
+ String rev2 = ((ArtifactInfo) o2).getRevision();
+
+ /*
+ * The revisions can still be not resolved, so we use the current version matcher to
+ * know if one revision is dynamic, and in this case if it should be considered greater
+ * or lower than the other one. Note that if the version matcher compare method returns
+ * 0, it's because it's not possible to know which revision is greater. In this case we
+ * consider the dynamic one to be greater, because most of the time it will then be
+ * actually resolved and a real comparison will occur.
+ */
+ VersionMatcher vmatcher = IvyContext.getContext().getSettings().getVersionMatcher();
+ ModuleRevisionId mrid1 = ModuleRevisionId.newInstance("", "", rev1);
+ ModuleRevisionId mrid2 = ModuleRevisionId.newInstance("", "", rev2);
+
+ if (vmatcher.isDynamic(mrid1)) {
+ int c = vmatcher.compare(mrid1, mrid2, mridComparator);
+ return c >= 0 ? 1 : -1;
+ } else if (vmatcher.isDynamic(mrid2)) {
+ int c = vmatcher.compare(mrid2, mrid1, mridComparator);
+ return c >= 0 ? -1 : 1;
+ }
+
+ return mridComparator.compare(mrid1, mrid2);
+ }
+ }
+
+ public static class SpecialMeaning {
+ private String name;
+
+ private Integer value;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public Integer getValue() {
+ return value;
+ }
+
+ public void setValue(Integer value) {
+ this.value = value;
+ }
+
+ public void validate() {
+ if (name == null) {
+ throw new IllegalStateException("a special meaning should have a name");
+ }
+ if (value == null) {
+ throw new IllegalStateException("a special meaning should have a value");
+ }
+ }
+ }
+
+ private static final Map DEFAULT_SPECIAL_MEANINGS;
+ static {
+ DEFAULT_SPECIAL_MEANINGS = new HashMap();
+ DEFAULT_SPECIAL_MEANINGS.put("dev", new Integer(-1));
+ DEFAULT_SPECIAL_MEANINGS.put("rc", new Integer(1));
+ DEFAULT_SPECIAL_MEANINGS.put("final", new Integer(2));
+ }
+
+ private final Comparator mridComparator = new MridComparator();
+
+ private final Comparator artifactInfoComparator = new ArtifactInfoComparator();
+
+ private Map specialMeanings = null;
+
+ private boolean usedefaultspecialmeanings = true;
+
+ public LatestRevisionStrategy() {
+ setComparator(artifactInfoComparator);
+ setName("latest-revision");
+ }
+
+ public void addConfiguredSpecialMeaning(SpecialMeaning meaning) {
+ meaning.validate();
+ getSpecialMeanings().put(meaning.getName().toLowerCase(Locale.US), meaning.getValue());
+ }
+
+ public synchronized Map getSpecialMeanings() {
+ if (specialMeanings == null) {
+ specialMeanings = new HashMap();
+ if (isUsedefaultspecialmeanings()) {
+ specialMeanings.putAll(DEFAULT_SPECIAL_MEANINGS);
+ }
+ }
+ return specialMeanings;
+ }
+
+ public boolean isUsedefaultspecialmeanings() {
+ return usedefaultspecialmeanings;
+ }
+
+ public void setUsedefaultspecialmeanings(boolean usedefaultspecialmeanings) {
+ this.usedefaultspecialmeanings = usedefaultspecialmeanings;
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/latest/LatestStrategy.java b/src/java/org/apache/ivy/plugins/latest/LatestStrategy.java
new file mode 100644
index 0000000..3666d6a
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/latest/LatestStrategy.java
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.latest;
+
+import java.util.Date;
+import java.util.List;
+
+public interface LatestStrategy {
+ /**
+ * Finds the latest artifact among the given artifacts info. The definition of 'latest' depends
+ * on the strategy itself. Given artifacts info are all good candidate. If the given date is not
+ * null, then found artifact should not be later than this date.
+ *
+ * @param infos
+ * @param date
+ * @return the latest artifact among the given ones.
+ */
+ ArtifactInfo findLatest(ArtifactInfo[] infos, Date date);
+
+ /**
+ * Sorts the given artifacts info from the oldest one to the latest one. The definition of
+ * 'latest' depends on the strategy itself. Given artifacts info are all good candidate.
+ *
+ * @param infos
+ * @return
+ */
+ List sort(ArtifactInfo[] infos);
+
+ String getName();
+}
diff --git a/src/java/org/apache/ivy/plugins/latest/LatestTimeStrategy.java b/src/java/org/apache/ivy/plugins/latest/LatestTimeStrategy.java
new file mode 100644
index 0000000..e5f8ccf
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/latest/LatestTimeStrategy.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.latest;
+
+import java.util.Comparator;
+
+public class LatestTimeStrategy extends ComparatorLatestStrategy {
+ private static final Comparator COMPARATOR = new Comparator() {
+ public int compare(Object o1, Object o2) {
+ long d1 = ((ArtifactInfo) o1).getLastModified();
+ long d2 = ((ArtifactInfo) o2).getLastModified();
+ return new Long(d1).compareTo(new Long(d2));
+ }
+
+ };
+
+ public LatestTimeStrategy() {
+ super(COMPARATOR);
+ setName("latest-time");
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/lock/AbstractLockStrategy.java b/src/java/org/apache/ivy/plugins/lock/AbstractLockStrategy.java
new file mode 100644
index 0000000..6535b04
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/lock/AbstractLockStrategy.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.lock;
+
+public abstract class AbstractLockStrategy implements LockStrategy {
+ private String name;
+
+ private boolean debugLocking = false;
+
+ protected AbstractLockStrategy() {
+ }
+
+ protected AbstractLockStrategy(boolean debugLocking) {
+ this.debugLocking = debugLocking;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String toString() {
+ return name;
+ }
+
+ public boolean isDebugLocking() {
+ return debugLocking;
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/lock/ArtifactLockStrategy.java b/src/java/org/apache/ivy/plugins/lock/ArtifactLockStrategy.java
new file mode 100644
index 0000000..1752486
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/lock/ArtifactLockStrategy.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.lock;
+
+import java.io.File;
+
+import org.apache.ivy.core.module.descriptor.Artifact;
+
+public abstract class ArtifactLockStrategy extends FileBasedLockStrategy {
+
+ protected ArtifactLockStrategy(FileLocker locker, boolean debugLocking) {
+ super(locker, debugLocking);
+ }
+
+ public boolean lockArtifact(Artifact artifact, File artifactFileToDownload)
+ throws InterruptedException {
+ return acquireLock(new File(artifactFileToDownload.getAbsolutePath() + ".lck"));
+ }
+
+ public void unlockArtifact(Artifact artifact, File artifactFileToDownload) {
+ releaseLock(new File(artifactFileToDownload.getAbsolutePath() + ".lck"));
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/lock/CreateFileLockStrategy.java b/src/java/org/apache/ivy/plugins/lock/CreateFileLockStrategy.java
new file mode 100644
index 0000000..d8dc93f
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/lock/CreateFileLockStrategy.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.lock;
+
+public class CreateFileLockStrategy extends ArtifactLockStrategy {
+
+ public CreateFileLockStrategy(boolean debugLocking) {
+ super(new CreateFileLocker(debugLocking), debugLocking);
+ setName("artifact-lock");
+ }
+
+}
diff --git a/src/java/org/apache/ivy/plugins/lock/DeleteOnExitHook.java b/src/java/org/apache/ivy/plugins/lock/DeleteOnExitHook.java
new file mode 100644
index 0000000..112f760
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/lock/DeleteOnExitHook.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.lock;
+
+import java.io.File;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+
+class DeleteOnExitHook {
+
+ static {
+ Runtime.getRuntime().addShutdownHook(new Thread() {
+ public void run() {
+ runHook();
+ }
+ });
+ }
+
+ private static LinkedHashSet files = new LinkedHashSet();
+
+ private DeleteOnExitHook() {
+ }
+
+ static synchronized void add(File file) {
+ files.add(file);
+ }
+
+ static synchronized void remove(File file) {
+ files.remove(file);
+ }
+
+ static synchronized void runHook() {
+ Iterator itr = files.iterator();
+ while (itr.hasNext()) {
+ ((File) itr.next()).delete();
+ itr.remove();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/java/org/apache/ivy/plugins/lock/FileBasedLockStrategy.java b/src/java/org/apache/ivy/plugins/lock/FileBasedLockStrategy.java
new file mode 100644
index 0000000..637988f
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/lock/FileBasedLockStrategy.java
@@ -0,0 +1,356 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.lock;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.channels.FileLock;
+import java.util.Enumeration;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.apache.ivy.util.Message;
+
+public abstract class FileBasedLockStrategy extends AbstractLockStrategy {
+ private static final int SLEEP_TIME = 100;
+
+ private static final long DEFAULT_TIMEOUT = 2 * 60 * 1000;
+
+ /**
+ * The locker to use to make file lock attempts.
+ */
+ private FileLocker locker;
+
+ private long timeout = DEFAULT_TIMEOUT;
+
+ /**
+ * Lock counter list must be static: locks are implicitly shared to the entire process, so the
+ * list too much be.
+ */
+ private static ConcurrentMap/* <File, Map<Thread, Integer>> */currentLockHolders = new ConcurrentHashMap();
+
+ protected FileBasedLockStrategy() {
+ this(new CreateFileLocker(false), false);
+ }
+
+ protected FileBasedLockStrategy(boolean debugLocking) {
+ this(new CreateFileLocker(debugLocking), debugLocking);
+ }
+
+ protected FileBasedLockStrategy(FileLocker locker, boolean debugLocking) {
+ super(debugLocking);
+ this.locker = locker;
+ }
+
+ protected boolean acquireLock(File file) throws InterruptedException {
+ Thread currentThread = Thread.currentThread();
+ if (isDebugLocking()) {
+ debugLocking("acquiring lock on " + file);
+ }
+ long start = System.currentTimeMillis();
+ do {
+ synchronized (currentLockHolders) {
+ if (isDebugLocking()) {
+ debugLocking("entered synchronized area (locking)");
+ }
+ int lockCount = hasLock(file, currentThread);
+ if (isDebugLocking()) {
+ debugLocking("current status for " + file + " is " + lockCount
+ + " held locks: " + getCurrentLockHolderNames(file));
+ }
+ if (lockCount < 0) {
+ /* Another thread in this process holds the lock; we need to wait */
+ if (isDebugLocking()) {
+ debugLocking("waiting for another thread to release the lock: "
+ + getCurrentLockHolderNames(file));
+ }
+ } else if (lockCount > 0) {
+ int holdLocks = incrementLock(file, currentThread);
+ if (isDebugLocking()) {
+ debugLocking("reentrant lock acquired on " + file + " in "
+ + (System.currentTimeMillis() - start) + "ms" + " - hold locks = "
+ + holdLocks);
+ }
+ return true;
+ } else {
+ /* No prior lock on this file is held at all */
+ if (locker.tryLock(file)) {
+ if (isDebugLocking()) {
+ debugLocking("lock acquired on " + file + " in "
+ + (System.currentTimeMillis() - start) + "ms");
+ }
+ incrementLock(file, currentThread);
+ return true;
+ }
+ }
+ }
+ if (isDebugLocking()) {
+ debugLocking("failed to acquire lock; sleeping for retry...");
+ }
+ Thread.sleep(SLEEP_TIME);
+ } while (System.currentTimeMillis() - start < timeout);
+ return false;
+ }
+
+ protected void releaseLock(File file) {
+ Thread currentThread = Thread.currentThread();
+ if (isDebugLocking()) {
+ debugLocking("releasing lock on " + file);
+ }
+ synchronized (currentLockHolders) {
+ if (isDebugLocking()) {
+ debugLocking("entered synchronized area (unlocking)");
+ }
+ int holdLocks = decrementLock(file, currentThread);
+ if (holdLocks == 0) {
+ locker.unlock(file);
+ if (isDebugLocking()) {
+ debugLocking("lock released on " + file);
+ }
+ } else {
+ if (isDebugLocking()) {
+ debugLocking("reentrant lock released on " + file + " - hold locks = "
+ + holdLocks);
+ }
+ }
+ }
+ }
+
+ private static void debugLocking(String msg) {
+ Message.info(Thread.currentThread() + " " + System.currentTimeMillis() + " " + msg);
+ }
+
+ /**
+ * Determine the state of the lockfile.
+ *
+ * Must be called from within a synchronized block.
+ *
+ * Three possibilities exist: - The lock is held by the current thread (>0) - The lock is held
+ * by one or more different threads (-1) - The lock is not held at all (0).
+ *
+ * @param file
+ * file to lock
+ * @param forThread
+ * thread for which lock status is being queried
+ */
+ private int hasLock(File file, Thread forThread) {
+ Map locksPerThread = (Map) currentLockHolders.get(file);
+ if (locksPerThread == null) {
+ return 0;
+ }
+ if (locksPerThread.isEmpty()) {
+ return 0;
+ }
+ Integer counterObj = (Integer) locksPerThread.get(forThread);
+ int counter = counterObj == null ? 0 : counterObj.intValue();
+ if (counter > 0) {
+ return counter;
+ } else {
+ return -1;
+ }
+ }
+
+ /**
+ * Record that this thread holds the lock.
+ *
+ * Asserts that the lock has been previously grabbed by this thread. Must be called from a
+ * synchronized block in which the lock was grabbed.
+ *
+ * @param file
+ * file which has been locked
+ * @param forThread
+ * thread for which locking occurred
+ * @return number of times this thread has grabbed the lock
+ */
+ private int incrementLock(File file, Thread forThread) {
+ Map locksPerThread = (Map) currentLockHolders.get(file);
+ if (locksPerThread == null) {
+ locksPerThread = new ConcurrentHashMap();
+ currentLockHolders.put(file, locksPerThread);
+ }
+ Integer c = (Integer) locksPerThread.get(forThread);
+ int holdLocks = c == null ? 1 : c.intValue() + 1;
+ locksPerThread.put(forThread, new Integer(holdLocks));
+ return holdLocks;
+ }
+
+ /**
+ * Decrease depth of this thread's lock.
+ *
+ * Must be called within a synchronized block.
+ *
+ * If this returns 0, the caller is responsible for releasing the lock within that same block.
+ *
+ * @param file
+ * file for which lock depth is being decreased
+ * @param forThread
+ * thread for which lock depth is being decreased
+ * @return remaining depth of this lock
+ */
+ private int decrementLock(File file, Thread forThread) {
+ ConcurrentHashMap locksPerThread = (ConcurrentHashMap) currentLockHolders.get(file);
+ if (locksPerThread == null) {
+ throw new RuntimeException("Calling decrementLock on a thread which holds no locks");
+ }
+ Integer c = (Integer) locksPerThread.get(forThread);
+ int oldHeldLocks = c == null ? 0 : c.intValue();
+ if (oldHeldLocks <= 0) {
+ throw new RuntimeException("Calling decrementLock on a thread which holds no locks");
+ }
+ int newHeldLocks = oldHeldLocks - 1;
+ if (newHeldLocks > 0) {
+ locksPerThread.put(forThread, new Integer(newHeldLocks));
+ } else {
+ locksPerThread.remove(forThread);
+ }
+ return newHeldLocks;
+ }
+
+ /**
+ * Return a string naming the threads which currently hold this lock.
+ */
+ protected String getCurrentLockHolderNames(File file) {
+ StringBuilder sb = new StringBuilder();
+ ConcurrentHashMap m = (ConcurrentHashMap) currentLockHolders.get(file);
+ if (m == null) {
+ return "(NULL)";
+ }
+ Enumeration threads = m.keys();
+ while (threads.hasMoreElements()) {
+ Thread t = (Thread) threads.nextElement();
+ sb.append(t.toString());
+ if (threads.hasMoreElements()) {
+ sb.append(", ");
+ }
+ }
+ return sb.toString();
+ }
+
+ public static interface FileLocker {
+ boolean tryLock(File f);
+
+ void unlock(File f);
+ }
+
+ /**
+ * "locks" a file by creating it if it doesn't exist, relying on the
+ * {@link File#createNewFile()} atomicity.
+ */
+ public static class CreateFileLocker implements FileLocker {
+ private boolean debugLocking;
+
+ public CreateFileLocker(boolean debugLocking) {
+ this.debugLocking = debugLocking;
+ }
+
+ public boolean tryLock(File file) {
+ try {
+ if (file.getParentFile().exists() || file.getParentFile().mkdirs()) {
+ if (file.createNewFile()) {
+ DeleteOnExitHook.add(file);
+ return true;
+ } else {
+ if (debugLocking) {
+ debugLocking("file creation failed " + file);
+ }
+ }
+ }
+ } catch (IOException e) {
+ // ignored
+ Message.verbose("file creation failed due to an exception: " + e.getMessage()
+ + " (" + file + ")");
+ }
+ return false;
+ }
+
+ public void unlock(File file) {
+ file.delete();
+ DeleteOnExitHook.remove(file);
+ }
+ }
+
+ /**
+ * Locks a file using the {@link FileLock} mechanism.
+ */
+ public static class NIOFileLocker implements FileLocker {
+
+ private Map locks = new ConcurrentHashMap();
+
+ private boolean debugLocking;
+
+ public NIOFileLocker(boolean debugLocking) {
+ this.debugLocking = debugLocking;
+ }
+
+ private static class LockData {
+ private RandomAccessFile raf;
+
+ private FileLock l;
+
+ LockData(RandomAccessFile raf, FileLock l) {
+ this.raf = raf;
+ this.l = l;
+ }
+ }
+
+ public boolean tryLock(File file) {
+ try {
+ if (file.getParentFile().exists() || file.getParentFile().mkdirs()) {
+ // this must not be closed until unlock
+ RandomAccessFile raf = new RandomAccessFile(file, "rw");
+ FileLock l = raf.getChannel().tryLock();
+ if (l != null) {
+ synchronized (this) {
+ locks.put(file, new LockData(raf, l));
+ }
+ return true;
+ } else {
+ if (debugLocking) {
+ debugLocking("failed to acquire lock on " + file);
+ }
+ }
+ }
+ } catch (IOException e) {
+ // ignored
+ Message.verbose("file lock failed due to an exception: " + e.getMessage() + " ("
+ + file + ")");
+ }
+ return false;
+ }
+
+ public void unlock(File file) {
+ synchronized (this) {
+ LockData data = (LockData) locks.get(file);
+ if (data == null) {
+ throw new IllegalArgumentException("file not previously locked: " + file);
+ }
+
+ try {
+ locks.remove(file);
+ data.l.release();
+ data.raf.close();
+ } catch (IOException e) {
+ Message.error("problem while releasing lock on " + file + ": " + e.getMessage());
+ }
+ }
+ }
+
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/lock/LockStrategy.java b/src/java/org/apache/ivy/plugins/lock/LockStrategy.java
new file mode 100644
index 0000000..a28d6f8
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/lock/LockStrategy.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.lock;
+
+import java.io.File;
+
+import org.apache.ivy.core.module.descriptor.Artifact;
+
+/**
+ * A lock strategy determines when and how lock should be performed when downloading data to a
+ * cache.
+ * <p>
+ * Note that some implementations may actually choose to NOT perform locking, when no lock is
+ * necessary (cache not shared). Some other implementations may choose to lock the cache for the
+ * download of a whole module (not possible yet), or at the artifact level.
+ * <p>
+ * </p>
+ * The lock methods should return true when the lock is either actually acquired or not performed by
+ * the strategy. </p>
+ * <p>
+ * Locking used in the locking strategy must support reentrant lock. Reentrant locking should be
+ * performed for the whole strategy.
+ * </p>
+ */
+public interface LockStrategy {
+
+ /**
+ * Returns the name of the strategy
+ *
+ * @return the name of the strategy
+ */
+ String getName();
+
+ /**
+ * Performs a lock before downloading the given {@link Artifact} to the given file.
+ *
+ * @param artifact
+ * the artifact about to be downloaded
+ * @param artifactFileToDownload
+ * the file where the artifact will be downloaded
+ * @return true if the artifact is locked, false otherwise
+ * @throws InterruptedException
+ * if the thread is interrupted while waiting to acquire the lock
+ */
+ boolean lockArtifact(Artifact artifact, File artifactFileToDownload)
+ throws InterruptedException;
+
+ /**
+ * Release the lock acquired for an artifact download.
+ *
+ * @param artifact
+ * the artifact for which the lock was acquired
+ * @param artifactFileToDownload
+ * the file where the artifact is supposed to have been downloaded
+ */
+ void unlockArtifact(Artifact artifact, File artifactFileToDownload);
+
+}
diff --git a/src/java/org/apache/ivy/plugins/lock/NIOFileLockStrategy.java b/src/java/org/apache/ivy/plugins/lock/NIOFileLockStrategy.java
new file mode 100644
index 0000000..9f8c474
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/lock/NIOFileLockStrategy.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.lock;
+
+public class NIOFileLockStrategy extends ArtifactLockStrategy {
+
+ public NIOFileLockStrategy(boolean debugLocking) {
+ super(new NIOFileLocker(debugLocking), debugLocking);
+ setName("artifact-lock-nio");
+ }
+
+}
diff --git a/src/java/org/apache/ivy/plugins/lock/NoLockStrategy.java b/src/java/org/apache/ivy/plugins/lock/NoLockStrategy.java
new file mode 100644
index 0000000..6ca735c
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/lock/NoLockStrategy.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.lock;
+
+import java.io.File;
+
+import org.apache.ivy.core.module.descriptor.Artifact;
+
+public class NoLockStrategy extends AbstractLockStrategy {
+ public NoLockStrategy() {
+ setName("no-lock");
+ }
+
+ public final boolean lockArtifact(Artifact artifact, File artifactFileToDownload) {
+ return true;
+ }
+
+ public final void unlockArtifact(Artifact artifact, File artifactFileToDownload) {
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/matcher/AbstractPatternMatcher.java b/src/java/org/apache/ivy/plugins/matcher/AbstractPatternMatcher.java
new file mode 100644
index 0000000..cf90573
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/matcher/AbstractPatternMatcher.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.ivy.plugins.matcher;
+
+/**
+ * An abstract implementation of the pattern matcher providing base template methods
+ */
+public abstract class AbstractPatternMatcher implements PatternMatcher {
+ private final String name;
+
+ /**
+ * Create a new instance of a pattern matcher
+ *
+ * @param name
+ * the name of the pattern matcher. Never null.
+ */
+ public AbstractPatternMatcher(/* @NotNull */String name) {
+ this.name = name;
+ }
+
+ public/* @NotNull */Matcher getMatcher(/* @NotNull */String expression) {
+ if (expression == null) {
+ throw new NullPointerException();
+ }
+ if (ANY_EXPRESSION.equals(expression)) {
+ return AnyMatcher.INSTANCE;
+ }
+ return newMatcher(expression);
+ }
+
+ public/* @NotNull */String getName() {
+ return name;
+ }
+
+ /**
+ * Returns an instance of the implementation specific matcher.
+ *
+ * @param expression
+ * the string to be matched.
+ * @return the instance of the related matcher. Never null.
+ */
+ protected abstract/* @NotNull */Matcher newMatcher(/* @NotNull */String expression);
+
+ public String toString() {
+ return getName();
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/matcher/AnyMatcher.java b/src/java/org/apache/ivy/plugins/matcher/AnyMatcher.java
new file mode 100644
index 0000000..7f6d36f
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/matcher/AnyMatcher.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.matcher;
+
+/**
+ * A matcher that will match everything.
+ */
+public/* @Immutable */class AnyMatcher implements Matcher {
+ public static final Matcher INSTANCE = new AnyMatcher();
+
+ public AnyMatcher() {
+ }
+
+ public boolean matches(String input) {
+ if (input == null) {
+ throw new NullPointerException();
+ }
+ return true;
+ }
+
+ public boolean isExact() {
+ return false;
+ }
+
+}
diff --git a/src/java/org/apache/ivy/plugins/matcher/ExactOrRegexpPatternMatcher.java b/src/java/org/apache/ivy/plugins/matcher/ExactOrRegexpPatternMatcher.java
new file mode 100644
index 0000000..116a47a
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/matcher/ExactOrRegexpPatternMatcher.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.matcher;
+
+/**
+ * A pattern matcher that tries to match exactly the input with the expression, or match it as a
+ * pattern.
+ * <p/>
+ * The evaluation for matching is perform first by checking if expression and input are equals (via
+ * equals method) else it attempts to do it by trying to match the input using the expression as a
+ * regexp.
+ *
+ * @see ExactPatternMatcher
+ * @see RegexpPatternMatcher
+ */
+public/* @Immutable */final class ExactOrRegexpPatternMatcher extends AbstractPatternMatcher {
+
+ public static final ExactOrRegexpPatternMatcher INSTANCE = new ExactOrRegexpPatternMatcher();
+
+ public ExactOrRegexpPatternMatcher() {
+ super(EXACT_OR_REGEXP);
+ }
+
+ protected Matcher newMatcher(String expression) {
+ return new ExactOrRegexpMatcher(expression);
+ }
+
+ private static final class ExactOrRegexpMatcher implements Matcher {
+ private Matcher exact;
+
+ private Matcher regexp;
+
+ public ExactOrRegexpMatcher(String expression) {
+ exact = ExactPatternMatcher.INSTANCE.getMatcher(expression);
+ regexp = RegexpPatternMatcher.INSTANCE.getMatcher(expression);
+ }
+
+ public boolean matches(String input) {
+ if (input == null) {
+ throw new NullPointerException();
+ }
+ return exact.matches(input) || regexp.matches(input);
+ }
+
+ public boolean isExact() {
+ return regexp.isExact(); // && exact.isExact();
+ }
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/matcher/ExactPatternMatcher.java b/src/java/org/apache/ivy/plugins/matcher/ExactPatternMatcher.java
new file mode 100644
index 0000000..565ee24
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/matcher/ExactPatternMatcher.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.matcher;
+
+/**
+ * Implementation of an exact matcher.
+ * <p/>
+ * The matching will be performed against an expression being a string. It will only matches if both
+ * strings are equal (per equals()) rule or if both strings are null.
+ */
+public/* @Immutable */final class ExactPatternMatcher extends AbstractPatternMatcher {
+
+ public static final ExactPatternMatcher INSTANCE = new ExactPatternMatcher();
+
+ public ExactPatternMatcher() {
+ super(EXACT);
+ }
+
+ protected Matcher newMatcher(String expression) {
+ return new ExactMatcher(expression);
+ }
+
+ private static/* @Immutable */class ExactMatcher implements Matcher {
+ private String expression;
+
+ public ExactMatcher(String expression) {
+ this.expression = expression;
+ }
+
+ public boolean matches(String input) {
+ if (input == null) {
+ throw new NullPointerException();
+ }
+ return input.equals(expression);
+ }
+
+ public boolean isExact() {
+ return true;
+ }
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/matcher/GlobPatternMatcher.java b/src/java/org/apache/ivy/plugins/matcher/GlobPatternMatcher.java
new file mode 100644
index 0000000..544cb23
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/matcher/GlobPatternMatcher.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.matcher;
+
+import java.util.regex.PatternSyntaxException;
+
+import org.apache.oro.text.GlobCompiler;
+import org.apache.oro.text.regex.MalformedPatternException;
+import org.apache.oro.text.regex.Pattern;
+import org.apache.oro.text.regex.Perl5Matcher;
+
+/**
+ * A pattern matcher matching input using unix-like glob matcher expressions. Meta characters are:
+ * <ul>
+ * <li>* - Matches zero or more characters</li>
+ * <li>? - Matches exactly one character.</li>
+ * </ul>
+ * <p/>
+ * <b> Note that this matcher is available only with <a href="http://jakarta.apache.org/oro"Apache
+ * Jakarta Oro 2.0.8</a> in your classpath.</b>
+ *
+ * @see <a
+ * href="http://jakarta.apache.org/oro/api/org/apache/oro/text/GlobCompiler.html">GlobCompiler</a>
+ */
+public/* @Immutable */final class GlobPatternMatcher extends AbstractPatternMatcher {
+
+ public static final GlobPatternMatcher INSTANCE = new GlobPatternMatcher();
+
+ /*
+ * NOTE: GlobCompiler does ~100K compilation/s - If necessary look into using ThreadLocal for
+ * GlobCompiler/Perl5Matcher to cut on useless object creation - If expression are reused over
+ * and over a LRU cache could make sense
+ */
+
+ public GlobPatternMatcher() {
+ super(GLOB);
+ }
+
+ protected Matcher newMatcher(String expression) {
+ return new GlobMatcher(expression);
+ }
+
+ private static class GlobMatcher implements Matcher {
+ private Pattern pattern;
+
+ private String expression;
+
+ private Boolean exact;
+
+ public GlobMatcher(String expression) throws PatternSyntaxException {
+ this.expression = expression;
+ try {
+ pattern = new GlobCompiler().compile(expression);
+ } catch (MalformedPatternException e) {
+ throw new PatternSyntaxException(e.getMessage(), expression, 0);
+ }
+ }
+
+ public boolean matches(String input) {
+ if (input == null) {
+ throw new NullPointerException();
+ }
+ return new Perl5Matcher().matches(input, pattern);
+ }
+
+ public boolean isExact() {
+ if (exact == null) {
+ exact = calculateExact();
+ }
+ return exact.booleanValue();
+ }
+
+ private Boolean calculateExact() {
+ Boolean result = Boolean.TRUE;
+
+ char[] expressionChars = expression.toCharArray();
+ for (int i = 0; i < expressionChars.length; i++) {
+ char ch = expressionChars[i];
+ if (ch == '*' || ch == '?' || ch == '[' || ch == ']') {
+ result = Boolean.FALSE;
+ break;
+ }
+ }
+
+ return result;
+ }
+ }
+
+}
diff --git a/src/java/org/apache/ivy/plugins/matcher/MapMatcher.java b/src/java/org/apache/ivy/plugins/matcher/MapMatcher.java
new file mode 100644
index 0000000..fb1bc81
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/matcher/MapMatcher.java
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.matcher;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+
+public class MapMatcher {
+ private Map/* <String, Matcher> */matchers = new HashMap();
+
+ private PatternMatcher pm;
+
+ private Map attributes;
+
+ public MapMatcher(Map attributes, PatternMatcher pm) {
+ this.attributes = attributes;
+ this.pm = pm;
+ for (Iterator iter = attributes.entrySet().iterator(); iter.hasNext();) {
+ Entry entry = (Entry) iter.next();
+ String value = (String) entry.getValue();
+ if (value != null) {
+ matchers.put(entry.getKey(), pm.getMatcher(value));
+ }
+ }
+ }
+
+ public boolean matches(Map/* <String,String> */m) {
+ for (Iterator iter = matchers.entrySet().iterator(); iter.hasNext();) {
+ Entry entry = (Entry) iter.next();
+
+ Matcher matcher = (Matcher) entry.getValue();
+ String value = (String) m.get(entry.getKey());
+ if ((value == null) || !matcher.matches(value)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public String toString() {
+ return attributes + " (" + pm.getName() + ")";
+ }
+
+ public Map getAttributes() {
+ return Collections.unmodifiableMap(attributes);
+ }
+
+ public PatternMatcher getPatternMatcher() {
+ return pm;
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/matcher/Matcher.java b/src/java/org/apache/ivy/plugins/matcher/Matcher.java
new file mode 100644
index 0000000..0a2cf77
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/matcher/Matcher.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.matcher;
+
+/**
+ * An interface that defines a string matcher.
+ */
+public interface Matcher {
+
+ /**
+ * Check whether a given string is matched by this matcher.
+ *
+ * @param input
+ * the string to be matched. Cannot be null.
+ * @return true if the input string is matched, false otherwise.
+ */
+ public boolean matches(/* @NotNull */String input);
+
+ /**
+ * Return if the matcher will match *only* if the expression equals the input. <i> WARN: This is
+ * used only as a performance trick, to avoid scanning for things when you already know exactly
+ * what you want. In the install task where it used it avoid scanning the repository to list all
+ * modules to find that only one matches, and that it has the name requested. </i>
+ *
+ * @return true if the matcher only matches when the expression is equals to the input, false
+ * otherwise.
+ */
+ public boolean isExact();
+}
diff --git a/src/java/org/apache/ivy/plugins/matcher/MatcherHelper.java b/src/java/org/apache/ivy/plugins/matcher/MatcherHelper.java
new file mode 100644
index 0000000..69a007a
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/matcher/MatcherHelper.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.matcher;
+
+import org.apache.ivy.core.module.id.ArtifactId;
+import org.apache.ivy.core.module.id.ModuleId;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+
+/**
+ * Set of helper methods to match ModuleId, ModuleRevisionId, ArtifactId
+ */
+public final class MatcherHelper {
+ // TODO this class might be better off as MatcherUtils in util package
+
+ private MatcherHelper() {
+ }
+
+ public static boolean matches(PatternMatcher m, String expression, String input) {
+ return m.getMatcher(expression).matches(input);
+ }
+
+ public static boolean matches(PatternMatcher m, ModuleId exp, ModuleId mid) {
+ return matches(m, exp.getOrganisation(), mid.getOrganisation())
+ && matches(m, exp.getName(), mid.getName());
+ }
+
+ public static boolean matches(PatternMatcher m, ModuleRevisionId exp, ModuleRevisionId mrid) {
+ return matches(m, exp.getOrganisation(), mrid.getOrganisation())
+ && matches(m, exp.getName(), mrid.getName())
+ && matches(m, exp.getRevision(), mrid.getRevision());
+ }
+
+ public static boolean matches(PatternMatcher m, ArtifactId exp, ArtifactId aid) {
+ return matches(m, exp.getModuleId(), aid.getModuleId())
+ && matches(m, exp.getName(), aid.getName())
+ && matches(m, exp.getExt(), aid.getExt())
+ && matches(m, exp.getType(), aid.getType());
+ }
+
+ public static boolean isExact(PatternMatcher m, ModuleRevisionId exp) {
+ return isExact(m, exp.getOrganisation()) && isExact(m, exp.getName())
+ && isExact(m, exp.getRevision());
+ }
+
+ // unused
+ public static boolean isExact(PatternMatcher m, ModuleId exp) {
+ return isExact(m, exp.getOrganisation()) && isExact(m, exp.getName());
+ }
+
+ public static boolean isExact(PatternMatcher m, String exp) {
+ return m.getMatcher(exp).isExact();
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/matcher/NoMatcher.java b/src/java/org/apache/ivy/plugins/matcher/NoMatcher.java
new file mode 100644
index 0000000..87b3c9c
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/matcher/NoMatcher.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.matcher;
+
+/**
+ * A matcher that matches nothing.
+ */
+public final/* @Immutable */class NoMatcher implements Matcher {
+
+ public static final Matcher INSTANCE = new NoMatcher();
+
+ public NoMatcher() {
+ }
+
+ public boolean matches(String input) {
+ if (input == null) {
+ throw new NullPointerException();
+ }
+ return false;
+ }
+
+ public boolean isExact() {
+ return true;
+ }
+
+}
diff --git a/src/java/org/apache/ivy/plugins/matcher/PatternMatcher.java b/src/java/org/apache/ivy/plugins/matcher/PatternMatcher.java
new file mode 100644
index 0000000..27b35eb
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/matcher/PatternMatcher.java
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.matcher;
+
+/**
+ * Interface for a pattern matcher.
+ * <p/>
+ * The pattern matcher is the main abstraction regarding the matching of an expression.
+ * Implementation may vary depending on the expression syntax handling that is desired.
+ */
+public interface PatternMatcher {
+
+ /**
+ * 'exact' pattern matcher name
+ */
+ public static final String EXACT = "exact";
+
+ /**
+ * pattern matcher name 'regexp'
+ */
+ public static final String REGEXP = "regexp";
+
+ /**
+ * pattern matcher 'glob'
+ */
+ public static final String GLOB = "glob";
+
+ /**
+ * pattern matcher name 'exactOrRegexp'
+ */
+ public static final String EXACT_OR_REGEXP = "exactOrRegexp";
+
+ /**
+ * Any expression string: '*'
+ */
+ public static final String ANY_EXPRESSION = "*";
+
+ /**
+ * Return the matcher for the given expression.
+ *
+ * @param expression
+ * the expression to be matched. Cannot be null ?
+ * @return the matcher instance for the given expression. Never null.
+ */
+ public/* @NotNull */Matcher getMatcher(/* @NotNull */String expression);
+
+ /**
+ * return the name of this pattern matcher
+ *
+ * @return the name of this pattern matcher. Never null.
+ * @see #EXACT
+ * @see #REGEXP
+ * @see #GLOB
+ * @see #EXACT_OR_REGEXP
+ */
+ public/* @NotNull */String getName();
+}
diff --git a/src/java/org/apache/ivy/plugins/matcher/RegexpPatternMatcher.java b/src/java/org/apache/ivy/plugins/matcher/RegexpPatternMatcher.java
new file mode 100644
index 0000000..832ab84
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/matcher/RegexpPatternMatcher.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.matcher;
+
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+/**
+ * A pattern matcher matching input using regular expressions.
+ *
+ * @see Pattern
+ */
+public final/* @Immutable */class RegexpPatternMatcher extends AbstractPatternMatcher {
+ public static final RegexpPatternMatcher INSTANCE = new RegexpPatternMatcher();
+
+ /*
+ * NOTE: Regexp compiler does ~200K compilation/s - If necessary look into using ThreadLocal
+ * Pattern to cut on useless object creation - If expression are reused over and over a LRU
+ * cache could make sense
+ */
+
+ public RegexpPatternMatcher() {
+ super(REGEXP);
+ }
+
+ protected Matcher newMatcher(String expression) {
+ return new RegexpMatcher(expression);
+ }
+
+ private static/* @Immutable */class RegexpMatcher implements Matcher {
+ private Pattern pattern;
+
+ private String expression;
+
+ private Boolean exact;
+
+ public RegexpMatcher(String expression) throws PatternSyntaxException {
+ if (expression == null) {
+ throw new NullPointerException();
+ }
+ this.expression = expression;
+ pattern = Pattern.compile(expression);
+ }
+
+ public boolean matches(String input) {
+ if (input == null) {
+ throw new NullPointerException();
+ }
+ return pattern.matcher(input).matches();
+ }
+
+ public boolean isExact() {
+ if (exact == null) {
+ exact = calculateExact();
+ }
+ return exact.booleanValue();
+ }
+
+ private Boolean calculateExact() {
+ Boolean result = Boolean.TRUE;
+
+ char[] expressionChars = expression.toCharArray();
+ for (int i = 0; i < expressionChars.length; i++) {
+ char ch = expressionChars[i];
+ if (!Character.isLetterOrDigit(ch) && !Character.isWhitespace(ch) && ('-' != ch)
+ && ('_' != ch)) {
+ result = Boolean.FALSE;
+ break;
+ }
+ }
+
+ return result;
+ }
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/namespace/MRIDRule.java b/src/java/org/apache/ivy/plugins/namespace/MRIDRule.java
new file mode 100644
index 0000000..3e1714b
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/namespace/MRIDRule.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.namespace;
+
+public class MRIDRule {
+ private String org;
+
+ private String module;
+
+ private String branch;
+
+ private String rev;
+
+ public MRIDRule(String org, String mod, String rev) {
+ this.org = org;
+ this.module = mod;
+ this.rev = rev;
+ }
+
+ public MRIDRule() {
+ }
+
+ public String getModule() {
+ return module;
+ }
+
+ public void setModule(String module) {
+ this.module = module;
+ }
+
+ public String getOrg() {
+ return org;
+ }
+
+ public void setOrg(String org) {
+ this.org = org;
+ }
+
+ public String getRev() {
+ return rev;
+ }
+
+ public void setRev(String rev) {
+ this.rev = rev;
+ }
+
+ public String toString() {
+ return "[ " + org + " " + module + (branch != null ? " " + branch : "") + " " + rev + " ]";
+ }
+
+ public String getBranch() {
+ return branch;
+ }
+
+ public void setBranch(String branch) {
+ this.branch = branch;
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/namespace/MRIDTransformationRule.java b/src/java/org/apache/ivy/plugins/namespace/MRIDTransformationRule.java
new file mode 100644
index 0000000..a6880c8
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/namespace/MRIDTransformationRule.java
@@ -0,0 +1,147 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.namespace;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.util.Message;
+
+public class MRIDTransformationRule implements NamespaceTransformer {
+ private static class MridRuleMatcher {
+ private static final String[] TYPES = new String[] {"o", "m", "b", "r"};
+
+ private Matcher[] matchers = new Matcher[TYPES.length];
+
+ public boolean match(MRIDRule src, ModuleRevisionId mrid) {
+ // CheckStyle:MagicNumber| OFF
+ matchers[0] = Pattern.compile(getPattern(src.getOrg())).matcher(mrid.getOrganisation());
+ if (!matchers[0].matches()) {
+ return false;
+ }
+ matchers[1] = Pattern.compile(getPattern(src.getModule())).matcher(mrid.getName());
+ if (!matchers[1].matches()) {
+ return false;
+ }
+ if (mrid.getBranch() == null) {
+ matchers[2] = null;
+ } else {
+ matchers[2] = Pattern.compile(getPattern(src.getBranch()))
+ .matcher(mrid.getBranch());
+ if (!matchers[2].matches()) {
+ return false;
+ }
+ }
+ matchers[3] = Pattern.compile(getPattern(src.getRev())).matcher(mrid.getRevision());
+ if (!matchers[3].matches()) {
+ return false;
+ }
+
+ return true;
+ // CheckStyle:MagicNumber| ON
+ }
+
+ public ModuleRevisionId apply(MRIDRule dest, ModuleRevisionId mrid) {
+ String org = applyRules(dest.getOrg(), "o");
+ String mod = applyRules(dest.getModule(), "m");
+ String branch = applyRules(dest.getBranch(), "b");
+ String rev = applyRules(dest.getRev(), "r");
+
+ return ModuleRevisionId.newInstance(org, mod, branch, rev,
+ mrid.getQualifiedExtraAttributes());
+ }
+
+ private String applyRules(String str, String type) {
+ for (int i = 0; i < TYPES.length; i++) {
+ str = applyTypeRule(str, TYPES[i], type, matchers[i]);
+ }
+ return str;
+ }
+
+ private String applyTypeRule(String rule, String type, String ruleType, Matcher m) {
+ if (m == null) {
+ return rule;
+ }
+ String res = rule == null ? "$" + ruleType + "0" : rule;
+ for (int i = 0; i < TYPES.length; i++) {
+ if (TYPES[i].equals(type)) {
+ res = res.replaceAll("([^\\\\])\\$" + type, "$1\\$");
+ res = res.replaceAll("^\\$" + type, "\\$");
+ } else {
+ res = res.replaceAll("([^\\\\])\\$" + TYPES[i], "$1\\\\\\$" + TYPES[i]);
+ res = res.replaceAll("^\\$" + TYPES[i], "\\\\\\$" + TYPES[i]);
+ }
+ }
+
+ StringBuffer sb = new StringBuffer();
+ m.reset();
+ m.find();
+ m.appendReplacement(sb, res);
+
+ String str = sb.toString();
+ // null rule not replaced, let it be null
+ if (rule == null && ("$" + ruleType + "0").equals(str)) {
+ return null;
+ }
+
+ return str;
+ }
+
+ private String getPattern(String p) {
+ return p == null ? ".*" : p;
+ }
+ }
+
+ private List src = new ArrayList();
+
+ private MRIDRule dest;
+
+ public void addSrc(MRIDRule src) {
+ this.src.add(src);
+ }
+
+ public void addDest(MRIDRule dest) {
+ if (this.dest != null) {
+ throw new IllegalArgumentException("only one dest is allowed per mapping");
+ }
+ this.dest = dest;
+ }
+
+ public ModuleRevisionId transform(ModuleRevisionId mrid) {
+ MridRuleMatcher matcher = new MridRuleMatcher();
+ for (Iterator iter = src.iterator(); iter.hasNext();) {
+ MRIDRule rule = (MRIDRule) iter.next();
+ if (matcher.match(rule, mrid)) {
+ ModuleRevisionId destMrid = matcher.apply(dest, mrid);
+ Message.debug("found matching namespace rule: " + rule + ". Applied " + dest
+ + " on " + mrid + ". Transformed to " + destMrid);
+ return destMrid;
+ }
+ }
+ return mrid;
+ }
+
+ public boolean isIdentity() {
+ return false;
+ }
+
+}
diff --git a/src/java/org/apache/ivy/plugins/namespace/NameSpaceHelper.java b/src/java/org/apache/ivy/plugins/namespace/NameSpaceHelper.java
new file mode 100644
index 0000000..2199286
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/namespace/NameSpaceHelper.java
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.namespace;
+
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.DefaultArtifact;
+import org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ArtifactId;
+import org.apache.ivy.core.module.id.ModuleId;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.report.MetadataArtifactDownloadReport;
+import org.apache.ivy.core.resolve.ResolvedModuleRevision;
+
+public final class NameSpaceHelper {
+ private NameSpaceHelper() {
+ }
+
+ public static DependencyDescriptor toSystem(DependencyDescriptor dd, Namespace ns) {
+ return DefaultDependencyDescriptor.transformInstance(dd, ns);
+ }
+
+ public static DependencyDescriptor transform(DependencyDescriptor dd, NamespaceTransformer t,
+ boolean fromSystem) {
+ return DefaultDependencyDescriptor.transformInstance(dd, t, fromSystem);
+ }
+
+ public static ModuleDescriptor toSystem(ModuleDescriptor md, Namespace ns) {
+ return DefaultModuleDescriptor.transformInstance(md, ns);
+ }
+
+ public static ResolvedModuleRevision toSystem(ResolvedModuleRevision rmr, Namespace ns) {
+ if (ns.getToSystemTransformer().isIdentity()) {
+ return rmr;
+ }
+ ModuleDescriptor md = toSystem(rmr.getDescriptor(), ns);
+ if (md.equals(rmr.getDescriptor())) {
+ return rmr;
+ }
+ return new ResolvedModuleRevision(rmr.getResolver(), rmr.getArtifactResolver(), md,
+ transform(rmr.getReport(), ns.getToSystemTransformer()), rmr.isForce());
+ }
+
+ public static Artifact transform(Artifact artifact, NamespaceTransformer t) {
+ if (t.isIdentity()) {
+ return artifact;
+ }
+ ModuleRevisionId mrid = t.transform(artifact.getModuleRevisionId());
+ if (artifact.getModuleRevisionId().equals(mrid)) {
+ return artifact;
+ }
+ return new DefaultArtifact(mrid, artifact.getPublicationDate(), artifact.getName(),
+ artifact.getType(), artifact.getExt(), artifact.getUrl(),
+ artifact.getQualifiedExtraAttributes());
+ }
+
+ public static MetadataArtifactDownloadReport transform(MetadataArtifactDownloadReport report,
+ NamespaceTransformer t) {
+ if (t.isIdentity()) {
+ return report;
+ }
+ MetadataArtifactDownloadReport madr = new MetadataArtifactDownloadReport(transform(
+ report.getArtifact(), t));
+ madr.setSearched(report.isSearched());
+ madr.setDownloadStatus(report.getDownloadStatus());
+ madr.setDownloadDetails(report.getDownloadDetails());
+ madr.setArtifactOrigin(report.getArtifactOrigin());
+ madr.setDownloadTimeMillis(report.getDownloadTimeMillis());
+ madr.setOriginalLocalFile(report.getOriginalLocalFile());
+ madr.setLocalFile(report.getLocalFile());
+ madr.setSize(report.getSize());
+ return madr;
+ }
+
+ public static ArtifactId transform(ArtifactId artifactId, NamespaceTransformer t) {
+ if (t.isIdentity()) {
+ return artifactId;
+ }
+ ModuleId mid = transform(artifactId.getModuleId(), t);
+ if (mid.equals(artifactId.getModuleId())) {
+ return artifactId;
+ }
+ return new ArtifactId(mid, artifactId.getName(), artifactId.getType(), artifactId.getExt());
+ }
+
+ public static ModuleId transform(ModuleId mid, NamespaceTransformer t) {
+ if (t.isIdentity()) {
+ return mid;
+ }
+ return t.transform(new ModuleRevisionId(mid, "")).getModuleId();
+ }
+
+ public static String transformOrganisation(String org, NamespaceTransformer t) {
+ return transform(new ModuleId(org, ""), t).getOrganisation();
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/namespace/Namespace.java b/src/java/org/apache/ivy/plugins/namespace/Namespace.java
new file mode 100644
index 0000000..43c61a9
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/namespace/Namespace.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.namespace;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+
+public class Namespace {
+ public static final Namespace SYSTEM_NAMESPACE;
+ static {
+ SYSTEM_NAMESPACE = new Namespace();
+ }
+
+ private List/* <NamespaceRule> */rules = new ArrayList();
+
+ private String name;
+
+ private boolean chainRules = false;
+
+ private NamespaceTransformer fromSystemTransformer = new NamespaceTransformer() {
+ public ModuleRevisionId transform(ModuleRevisionId mrid) {
+ if (mrid == null) {
+ return null;
+ }
+ for (Iterator iter = rules.iterator(); iter.hasNext();) {
+ NamespaceRule rule = (NamespaceRule) iter.next();
+ ModuleRevisionId nmrid = rule.getFromSystem().transform(mrid);
+ if (chainRules) {
+ mrid = nmrid;
+ } else if (!nmrid.equals(mrid)) {
+ return nmrid;
+ }
+ }
+ return mrid;
+ }
+
+ public boolean isIdentity() {
+ return rules.isEmpty();
+ }
+ };
+
+ private NamespaceTransformer toSystemTransformer = new NamespaceTransformer() {
+ public ModuleRevisionId transform(ModuleRevisionId mrid) {
+ if (mrid == null) {
+ return null;
+ }
+ for (Iterator iter = rules.iterator(); iter.hasNext();) {
+ NamespaceRule rule = (NamespaceRule) iter.next();
+ ModuleRevisionId nmrid = rule.getToSystem().transform(mrid);
+ if (chainRules) {
+ mrid = nmrid;
+ } else if (!nmrid.equals(mrid)) {
+ return nmrid;
+ }
+ }
+ return mrid;
+ }
+
+ public boolean isIdentity() {
+ return rules.isEmpty();
+ }
+ };
+
+ public void addRule(NamespaceRule rule) {
+ rules.add(rule);
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public NamespaceTransformer getFromSystemTransformer() {
+ return fromSystemTransformer;
+ }
+
+ public NamespaceTransformer getToSystemTransformer() {
+ return toSystemTransformer;
+ }
+
+ public boolean isChainrules() {
+ return chainRules;
+ }
+
+ public void setChainrules(boolean chainRules) {
+ this.chainRules = chainRules;
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/namespace/NamespaceRule.java b/src/java/org/apache/ivy/plugins/namespace/NamespaceRule.java
new file mode 100644
index 0000000..d47b897
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/namespace/NamespaceRule.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.namespace;
+
+public class NamespaceRule {
+ private String name;
+
+ private String description;
+
+ private MRIDTransformationRule fromSystem;
+
+ private MRIDTransformationRule toSystem;
+
+ public MRIDTransformationRule getFromSystem() {
+ return fromSystem;
+ }
+
+ public void addFromsystem(MRIDTransformationRule fromSystem) {
+ if (this.fromSystem != null) {
+ throw new IllegalArgumentException("only one fromsystem is allowed per rule");
+ }
+ this.fromSystem = fromSystem;
+ }
+
+ public MRIDTransformationRule getToSystem() {
+ return toSystem;
+ }
+
+ public void addTosystem(MRIDTransformationRule toSystem) {
+ if (this.toSystem != null) {
+ throw new IllegalArgumentException("only one tosystem is allowed per rule");
+ }
+ this.toSystem = toSystem;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/namespace/NamespaceTransformer.java b/src/java/org/apache/ivy/plugins/namespace/NamespaceTransformer.java
new file mode 100644
index 0000000..75148b2
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/namespace/NamespaceTransformer.java
@@ -0,0 +1,26 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.namespace;
+
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+
+public interface NamespaceTransformer {
+ public ModuleRevisionId transform(ModuleRevisionId mrid);
+
+ public boolean isIdentity();
+}
diff --git a/src/java/org/apache/ivy/plugins/parser/AbstractModuleDescriptorParser.java b/src/java/org/apache/ivy/plugins/parser/AbstractModuleDescriptorParser.java
new file mode 100644
index 0000000..25ec67c
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/parser/AbstractModuleDescriptorParser.java
@@ -0,0 +1,366 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.parser;
+
+import java.io.IOException;
+import java.net.URL;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.Configuration;
+import org.apache.ivy.core.module.descriptor.DefaultArtifact;
+import org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.plugins.repository.Resource;
+import org.apache.ivy.plugins.repository.ResourceHelper;
+import org.apache.ivy.plugins.repository.url.URLResource;
+import org.apache.ivy.util.Message;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.helpers.DefaultHandler;
+
+public abstract class AbstractModuleDescriptorParser implements ModuleDescriptorParser {
+ public ModuleDescriptor parseDescriptor(ParserSettings ivySettings, URL descriptorURL,
+ boolean validate) throws ParseException, IOException {
+ return parseDescriptor(ivySettings, descriptorURL, new URLResource(descriptorURL), validate);
+ }
+
+ public String getType() {
+ return "ivy";
+ }
+
+ public Artifact getMetadataArtifact(ModuleRevisionId mrid, Resource res) {
+ return DefaultArtifact.newIvyArtifact(mrid, new Date(res.getLastModified()));
+ }
+
+ protected abstract static class AbstractParser extends DefaultHandler {
+ private static final String DEFAULT_CONF_MAPPING = "*->*";
+
+ private String defaultConf; // used only as defaultconf, not used for
+
+ // guesssing right side part of a mapping
+ private String defaultConfMapping; // same as default conf but is used
+
+ // for guesssing right side part of a mapping
+ private DefaultDependencyDescriptor defaultConfMappingDescriptor;
+
+ private Resource res;
+
+ private List errors = new ArrayList();
+
+ private DefaultModuleDescriptor md;
+
+ private ModuleDescriptorParser parser;
+
+ protected AbstractParser(ModuleDescriptorParser parser) {
+ this.parser = parser;
+ }
+
+ public ModuleDescriptorParser getModuleDescriptorParser() {
+ return parser;
+ }
+
+ protected void checkErrors() throws ParseException {
+ if (!errors.isEmpty()) {
+ throw new ParseException(errors.toString(), 0);
+ }
+ }
+
+ public void setResource(Resource res) {
+ this.res = res; // used for log and date only
+ md = new DefaultModuleDescriptor(parser, res);
+ md.setLastModified(ResourceHelper.getLastModifiedOrDefault(res));
+ }
+
+ protected Resource getResource() {
+ return res;
+ }
+
+ protected String getDefaultConfMapping() {
+ return defaultConfMapping;
+ }
+
+ protected void setDefaultConfMapping(String defaultConf) {
+ defaultConfMapping = defaultConf;
+ getMd().setDefaultConfMapping(defaultConf);
+ }
+
+ protected void parseDepsConfs(String confs, DefaultDependencyDescriptor dd) {
+ parseDepsConfs(confs, dd, defaultConfMapping != null);
+ }
+
+ protected void parseDepsConfs(String confs, DefaultDependencyDescriptor dd,
+ boolean useDefaultMappingToGuessRightOperande) {
+ parseDepsConfs(confs, dd, useDefaultMappingToGuessRightOperande, true);
+ }
+
+ protected void parseDepsConfs(String confs, DefaultDependencyDescriptor dd,
+ boolean useDefaultMappingToGuessRightOperande, boolean evaluateConditions) {
+ if (confs == null) {
+ return;
+ }
+
+ String[] conf = confs.split(";");
+ parseDepsConfs(conf, dd, useDefaultMappingToGuessRightOperande, evaluateConditions);
+ }
+
+ protected void parseDepsConfs(String[] conf, DefaultDependencyDescriptor dd,
+ boolean useDefaultMappingToGuessRightOperande) {
+ parseDepsConfs(conf, dd, useDefaultMappingToGuessRightOperande, true);
+ }
+
+ protected void parseDepsConfs(String[] conf, DefaultDependencyDescriptor dd,
+ boolean useDefaultMappingToGuessRightOperande, boolean evaluateConditions) {
+ replaceConfigurationWildcards(md);
+ for (int i = 0; i < conf.length; i++) {
+ String[] ops = conf[i].split("->");
+ if (ops.length == 1) {
+ String[] modConfs = ops[0].split(",");
+ if (!useDefaultMappingToGuessRightOperande) {
+ for (int j = 0; j < modConfs.length; j++) {
+ dd.addDependencyConfiguration(modConfs[j].trim(), modConfs[j].trim());
+ }
+ } else {
+ for (int j = 0; j < modConfs.length; j++) {
+ String[] depConfs = getDefaultConfMappingDescriptor()
+ .getDependencyConfigurations(modConfs[j]);
+ if (depConfs.length > 0) {
+ for (int k = 0; k < depConfs.length; k++) {
+ String mappedDependency = evaluateConditions ? evaluateCondition(
+ depConfs[k].trim(), dd) : depConfs[k].trim();
+ if (mappedDependency != null) {
+ dd.addDependencyConfiguration(modConfs[j].trim(),
+ mappedDependency);
+ }
+ }
+ } else {
+ // no default mapping found for this configuration, map
+ // configuration to itself
+ dd.addDependencyConfiguration(modConfs[j].trim(),
+ modConfs[j].trim());
+ }
+ }
+ }
+ } else if (ops.length == 2) {
+ String[] modConfs = ops[0].split(",");
+ String[] depConfs = ops[1].split(",");
+ for (int j = 0; j < modConfs.length; j++) {
+ for (int k = 0; k < depConfs.length; k++) {
+ String mappedDependency = evaluateConditions ? evaluateCondition(
+ depConfs[k].trim(), dd) : depConfs[k].trim();
+ if (mappedDependency != null) {
+ dd.addDependencyConfiguration(modConfs[j].trim(), mappedDependency);
+ }
+ }
+ }
+ } else {
+ addError("invalid conf " + conf[i] + " for " + dd);
+ }
+ }
+
+ if (md.isMappingOverride()) {
+ addExtendingConfigurations(conf, dd, useDefaultMappingToGuessRightOperande);
+ }
+ }
+
+ /**
+ * Evaluate the optional condition in the given configuration, like "[org=MYORG]confX". If
+ * the condition evaluates to true, the configuration is returned, if the condition
+ * evaluatate to false, null is returned. If there are no conditions, the configuration
+ * itself is returned.
+ *
+ * @param conf
+ * the configuration to evaluate
+ * @param dd
+ * the dependencydescriptor to which the configuration will be added
+ * @return the evaluated condition
+ */
+ private String evaluateCondition(String conf, DefaultDependencyDescriptor dd) {
+ if (conf.charAt(0) != '[') {
+ return conf;
+ }
+
+ int endConditionIndex = conf.indexOf(']');
+ if (endConditionIndex == -1) {
+ addError("invalid conf " + conf + " for " + dd);
+ return null;
+ }
+
+ String condition = conf.substring(1, endConditionIndex);
+
+ int notEqualIndex = condition.indexOf("!=");
+ if (notEqualIndex == -1) {
+ int equalIndex = condition.indexOf('=');
+ if (equalIndex == -1) {
+ addError("invalid conf " + conf + " for " + dd.getDependencyRevisionId());
+ return null;
+ }
+
+ String leftOp = condition.substring(0, equalIndex).trim();
+ String rightOp = condition.substring(equalIndex + 1).trim();
+
+ // allow organisation synonyms, like 'org' or 'organization'
+ if (leftOp.equals("org") || leftOp.equals("organization")) {
+ leftOp = "organisation";
+ }
+
+ String attrValue = dd.getAttribute(leftOp);
+ if (!rightOp.equals(attrValue)) {
+ return null;
+ }
+ } else {
+ String leftOp = condition.substring(0, notEqualIndex).trim();
+ String rightOp = condition.substring(notEqualIndex + 2).trim();
+
+ // allow organisation synonyms, like 'org' or 'organization'
+ if (leftOp.equals("org") || leftOp.equals("organization")) {
+ leftOp = "organisation";
+ }
+
+ String attrValue = dd.getAttribute(leftOp);
+ if (rightOp.equals(attrValue)) {
+ return null;
+ }
+ }
+
+ return conf.substring(endConditionIndex + 1);
+ }
+
+ private void addExtendingConfigurations(String[] confs, DefaultDependencyDescriptor dd,
+ boolean useDefaultMappingToGuessRightOperande) {
+ for (int i = 0; i < confs.length; i++) {
+ addExtendingConfigurations(confs[i], dd, useDefaultMappingToGuessRightOperande);
+ }
+ }
+
+ private void addExtendingConfigurations(String conf, DefaultDependencyDescriptor dd,
+ boolean useDefaultMappingToGuessRightOperande) {
+ Set configsToAdd = new HashSet();
+ Configuration[] configs = md.getConfigurations();
+ for (int i = 0; i < configs.length; i++) {
+ String[] ext = configs[i].getExtends();
+ for (int j = 0; j < ext.length; j++) {
+ if (conf.equals(ext[j])) {
+ String configName = configs[i].getName();
+ configsToAdd.add(configName);
+ addExtendingConfigurations(configName, dd,
+ useDefaultMappingToGuessRightOperande);
+ }
+ }
+ }
+
+ String[] confs = (String[]) configsToAdd.toArray(new String[configsToAdd.size()]);
+ parseDepsConfs(confs, dd, useDefaultMappingToGuessRightOperande);
+ }
+
+ protected DependencyDescriptor getDefaultConfMappingDescriptor() {
+ if (defaultConfMappingDescriptor == null) {
+ defaultConfMappingDescriptor = new DefaultDependencyDescriptor(
+ ModuleRevisionId.newInstance("", "", ""), false);
+ parseDepsConfs(defaultConfMapping, defaultConfMappingDescriptor, false, false);
+ }
+ return defaultConfMappingDescriptor;
+ }
+
+ protected void addError(String msg) {
+ if (res != null) {
+ errors.add(msg + " in " + res + "\n");
+ } else {
+ errors.add(msg + "\n");
+ }
+ }
+
+ public void warning(SAXParseException ex) {
+ Message.warn("xml parsing: " + getLocationString(ex) + ": " + ex.getMessage());
+ }
+
+ public void error(SAXParseException ex) {
+ addError("xml parsing: " + getLocationString(ex) + ": " + ex.getMessage());
+ }
+
+ public void fatalError(SAXParseException ex) throws SAXException {
+ addError("[Fatal Error] " + getLocationString(ex) + ": " + ex.getMessage());
+ }
+
+ /** Returns a string of the location. */
+ private String getLocationString(SAXParseException ex) {
+ StringBuffer str = new StringBuffer();
+
+ String systemId = ex.getSystemId();
+ if (systemId != null) {
+ int index = systemId.lastIndexOf('/');
+ if (index != -1) {
+ systemId = systemId.substring(index + 1);
+ }
+ str.append(systemId);
+ } else if (getResource() != null) {
+ str.append(getResource().toString());
+ }
+ str.append(':');
+ str.append(ex.getLineNumber());
+ str.append(':');
+ str.append(ex.getColumnNumber());
+
+ return str.toString();
+
+ } // getLocationString(SAXParseException):String
+
+ protected String getDefaultConf() {
+ return defaultConf != null ? defaultConf
+ : (defaultConfMapping != null ? defaultConfMapping : DEFAULT_CONF_MAPPING);
+ }
+
+ protected void setDefaultConf(String defaultConf) {
+ this.defaultConf = defaultConf;
+ getMd().setDefaultConf(defaultConf);
+ }
+
+ public ModuleDescriptor getModuleDescriptor() throws ParseException {
+ checkErrors();
+ return md;
+ }
+
+ protected Date getDefaultPubDate() {
+ return new Date(md.getLastModified());
+ }
+
+ private void replaceConfigurationWildcards(ModuleDescriptor md) {
+ Configuration[] configs = md.getConfigurations();
+ for (int i = 0; i < configs.length; i++) {
+ configs[i].replaceWildcards(md);
+ }
+ }
+
+ protected void setMd(DefaultModuleDescriptor md) {
+ this.md = md;
+ }
+
+ protected DefaultModuleDescriptor getMd() {
+ return md;
+ }
+ }
+
+}
diff --git a/src/java/org/apache/ivy/plugins/parser/ModuleDescriptorParser.java b/src/java/org/apache/ivy/plugins/parser/ModuleDescriptorParser.java
new file mode 100644
index 0000000..0971f33
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/parser/ModuleDescriptorParser.java
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.parser;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.text.ParseException;
+
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.plugins.repository.Resource;
+
+public interface ModuleDescriptorParser {
+ public ModuleDescriptor parseDescriptor(ParserSettings ivySettings, URL descriptorURL,
+ boolean validate) throws ParseException, IOException;
+
+ public ModuleDescriptor parseDescriptor(ParserSettings ivySettings, URL descriptorURL,
+ Resource res, boolean validate) throws ParseException, IOException;
+
+ /**
+ * Convert a module descriptor to an ivy file. This method MUST close the given input stream
+ * when job is finished
+ *
+ * @param is
+ * input stream with opened on original module descriptor resource
+ */
+ public void toIvyFile(InputStream is, Resource res, File destFile, ModuleDescriptor md)
+ throws ParseException, IOException;
+
+ public boolean accept(Resource res);
+
+ /**
+ * Return the 'type' of module artifacts this parser is parsing
+ *
+ * @return the 'type' of module artifacts this parser is parsing
+ */
+ public String getType();
+
+ /**
+ * Returns the module metadata artifact corresponding to the given module revision id that this
+ * parser parses
+ *
+ * @param res
+ * the resource for which the module artifact should be returned
+ * @param mrid
+ * the module revision id for which the module artifact should be returned
+ * @return the module artifact corresponding to the given mrid and resource
+ */
+ public Artifact getMetadataArtifact(ModuleRevisionId mrid, Resource res);
+}
diff --git a/src/java/org/apache/ivy/plugins/parser/ModuleDescriptorParserRegistry.java b/src/java/org/apache/ivy/plugins/parser/ModuleDescriptorParserRegistry.java
new file mode 100644
index 0000000..f2150d2
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/parser/ModuleDescriptorParserRegistry.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.parser;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.text.ParseException;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.osgi.core.OSGiManifestParser;
+import org.apache.ivy.plugins.parser.m2.PomModuleDescriptorParser;
+import org.apache.ivy.plugins.parser.xml.XmlModuleDescriptorParser;
+import org.apache.ivy.plugins.repository.Resource;
+import org.apache.ivy.util.Message;
+
+public final class ModuleDescriptorParserRegistry extends AbstractModuleDescriptorParser {
+ private static final ModuleDescriptorParserRegistry INSTANCE = new ModuleDescriptorParserRegistry();
+
+ public static ModuleDescriptorParserRegistry getInstance() {
+ return INSTANCE;
+ }
+
+ private List parsers = new LinkedList();
+
+ private ModuleDescriptorParserRegistry() {
+ parsers.add(PomModuleDescriptorParser.getInstance());
+ parsers.add(OSGiManifestParser.getInstance());
+ parsers.add(XmlModuleDescriptorParser.getInstance());
+ }
+
+ /**
+ * Adds a the given parser to this registry.
+ *
+ * @param parser
+ * the parser to add
+ */
+ public void addParser(ModuleDescriptorParser parser) {
+ /*
+ * The parser is added in the front of the list of parsers. This is necessary because the
+ * XmlModuleDescriptorParser accepts all resources!
+ */
+ parsers.add(0, parser);
+ }
+
+ public ModuleDescriptorParser[] getParsers() {
+ return (ModuleDescriptorParser[]) parsers
+ .toArray(new ModuleDescriptorParser[parsers.size()]);
+ }
+
+ public ModuleDescriptorParser getParser(Resource res) {
+ for (Iterator iter = parsers.iterator(); iter.hasNext();) {
+ ModuleDescriptorParser parser = (ModuleDescriptorParser) iter.next();
+ if (parser.accept(res)) {
+ return parser;
+ }
+ }
+ return null;
+ }
+
+ public ModuleDescriptor parseDescriptor(ParserSettings settings, URL descriptorURL,
+ Resource res, boolean validate) throws ParseException, IOException {
+ ModuleDescriptorParser parser = getParser(res);
+ if (parser == null) {
+ Message.warn("no module descriptor parser found for " + res);
+ return null;
+ }
+ return parser.parseDescriptor(settings, descriptorURL, res, validate);
+ }
+
+ public boolean accept(Resource res) {
+ return getParser(res) != null;
+ }
+
+ public void toIvyFile(InputStream is, Resource res, File destFile, ModuleDescriptor md)
+ throws ParseException, IOException {
+ ModuleDescriptorParser parser = getParser(res);
+ if (parser == null) {
+ Message.warn("no module descriptor parser found for " + res);
+ } else {
+ parser.toIvyFile(is, res, destFile, md);
+ }
+ }
+
+}
diff --git a/src/java/org/apache/ivy/plugins/parser/ParserSettings.java b/src/java/org/apache/ivy/plugins/parser/ParserSettings.java
new file mode 100644
index 0000000..e306421
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/parser/ParserSettings.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.parser;
+
+import java.io.File;
+import java.util.Map;
+
+import org.apache.ivy.core.RelativeUrlResolver;
+import org.apache.ivy.core.cache.ResolutionCacheManager;
+import org.apache.ivy.core.module.id.ModuleId;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.module.status.StatusManager;
+import org.apache.ivy.plugins.conflict.ConflictManager;
+import org.apache.ivy.plugins.matcher.PatternMatcher;
+import org.apache.ivy.plugins.namespace.Namespace;
+import org.apache.ivy.plugins.resolver.DependencyResolver;
+
+public interface ParserSettings {
+
+ String substitute(String value);
+
+ Map/* <String, String> */substitute(Map/* <String, String> */strings);
+
+ ResolutionCacheManager getResolutionCacheManager();
+
+ ConflictManager getConflictManager(String name);
+
+ PatternMatcher getMatcher(String matcherName);
+
+ Namespace getNamespace(String namespace);
+
+ StatusManager getStatusManager();
+
+ RelativeUrlResolver getRelativeUrlResolver();
+
+ DependencyResolver getResolver(ModuleRevisionId mRevId);
+
+ File resolveFile(String filename);
+
+ String getDefaultBranch(ModuleId moduleId);
+
+ /**
+ * Returns the namespace context in which the current descriptor is parsed.
+ */
+ Namespace getContextNamespace();
+
+}
diff --git a/src/java/org/apache/ivy/plugins/parser/m2/DefaultPomDependencyMgt.java b/src/java/org/apache/ivy/plugins/parser/m2/DefaultPomDependencyMgt.java
new file mode 100644
index 0000000..5e05f49
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/parser/m2/DefaultPomDependencyMgt.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.parser.m2;
+
+import java.util.List;
+
+public class DefaultPomDependencyMgt implements PomDependencyMgt {
+ private String groupId;
+
+ private String artifactId;
+
+ private String version;
+
+ private String scope;
+
+ private List /* <ModuleId> */excludedModules;
+
+ public DefaultPomDependencyMgt(String groupId, String artifactId, String version, String scope,
+ List /* <ModuleId> */excludedModules) {
+ this.groupId = groupId;
+ this.artifactId = artifactId;
+ this.version = version;
+ this.scope = scope;
+ this.excludedModules = excludedModules;
+ }
+
+ public String getScope() {
+ return scope;
+ }
+
+ public String getGroupId() {
+ return groupId;
+ }
+
+ public String getArtifactId() {
+ return artifactId;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public List /* <ModuleId> */getExcludedModules() {
+ return excludedModules;
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/parser/m2/PomDependencyMgt.java b/src/java/org/apache/ivy/plugins/parser/m2/PomDependencyMgt.java
new file mode 100644
index 0000000..6b6b0af
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/parser/m2/PomDependencyMgt.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.parser.m2;
+
+import java.util.List;
+
+public interface PomDependencyMgt {
+
+ public abstract String getGroupId();
+
+ public abstract String getArtifactId();
+
+ public abstract String getVersion();
+
+ public abstract String getScope();
+
+ public List /* <ModuleId> */getExcludedModules();
+}
\ No newline at end of file
diff --git a/src/java/org/apache/ivy/plugins/parser/m2/PomModuleDescriptorBuilder.java b/src/java/org/apache/ivy/plugins/parser/m2/PomModuleDescriptorBuilder.java
new file mode 100644
index 0000000..40a007a
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/parser/m2/PomModuleDescriptorBuilder.java
@@ -0,0 +1,736 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.parser.m2;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.ivy.Ivy;
+import org.apache.ivy.core.cache.ArtifactOrigin;
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.Configuration;
+import org.apache.ivy.core.module.descriptor.Configuration.Visibility;
+import org.apache.ivy.core.module.descriptor.DefaultArtifact;
+import org.apache.ivy.core.module.descriptor.DefaultDependencyArtifactDescriptor;
+import org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.DefaultExcludeRule;
+import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.ExtraInfoHolder;
+import org.apache.ivy.core.module.descriptor.License;
+import org.apache.ivy.core.module.descriptor.MDArtifact;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.descriptor.OverrideDependencyDescriptorMediator;
+import org.apache.ivy.core.module.id.ArtifactId;
+import org.apache.ivy.core.module.id.ModuleId;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.plugins.matcher.ExactPatternMatcher;
+import org.apache.ivy.plugins.matcher.PatternMatcher;
+import org.apache.ivy.plugins.parser.ModuleDescriptorParser;
+import org.apache.ivy.plugins.parser.ParserSettings;
+import org.apache.ivy.plugins.parser.m2.PomReader.PomDependencyData;
+import org.apache.ivy.plugins.repository.Resource;
+import org.apache.ivy.plugins.resolver.DependencyResolver;
+import org.apache.ivy.util.Message;
+
+/**
+ * Build a module descriptor. This class handle the complexity of the structure of an ivy
+ * ModuleDescriptor and isolate the PomModuleDescriptorParser from it.
+ */
+public class PomModuleDescriptorBuilder {
+
+ private static final int DEPENDENCY_MANAGEMENT_KEY_PARTS_COUNT = 4;
+
+ public static final Configuration[] MAVEN2_CONFIGURATIONS = new Configuration[] {
+ new Configuration("default", Visibility.PUBLIC,
+ "runtime dependencies and master artifact can be used with this conf",
+ new String[] {"runtime", "master"}, true, null),
+ new Configuration("master", Visibility.PUBLIC,
+ "contains only the artifact published by this module itself, "
+ + "with no transitive dependencies", new String[0], true, null),
+ new Configuration("compile", Visibility.PUBLIC,
+ "this is the default scope, used if none is specified. "
+ + "Compile dependencies are available in all classpaths.",
+ new String[0], true, null),
+ new Configuration(
+ "provided",
+ Visibility.PUBLIC,
+ "this is much like compile, but indicates you expect the JDK or a container "
+ + "to provide it. "
+ + "It is only available on the compilation classpath, and is not transitive.",
+ new String[0], true, null),
+ new Configuration("runtime", Visibility.PUBLIC,
+ "this scope indicates that the dependency is not required for compilation, "
+ + "but is for execution. It is in the runtime and test classpaths, "
+ + "but not the compile classpath.", new String[] {"compile"}, true,
+ null),
+ new Configuration(
+ "test",
+ Visibility.PRIVATE,
+ "this scope indicates that the dependency is not required for normal use of "
+ + "the application, and is only available for the test compilation and "
+ + "execution phases.", new String[] {"runtime"}, true, null),
+ new Configuration(
+ "system",
+ Visibility.PUBLIC,
+ "this scope is similar to provided except that you have to provide the JAR "
+ + "which contains it explicitly. The artifact is always available and is not "
+ + "looked up in a repository.", new String[0], true, null),
+ new Configuration("sources", Visibility.PUBLIC,
+ "this configuration contains the source artifact of this module, if any.",
+ new String[0], true, null),
+ new Configuration("javadoc", Visibility.PUBLIC,
+ "this configuration contains the javadoc artifact of this module, if any.",
+ new String[0], true, null),
+ new Configuration("optional", Visibility.PUBLIC, "contains all optional dependencies",
+ new String[0], true, null)};
+
+ static final Map MAVEN2_CONF_MAPPING = new HashMap();
+
+ private static final String DEPENDENCY_MANAGEMENT = "m:dependency.management";
+
+ private static final String PROPERTIES = "m:properties";
+
+ private static final String EXTRA_INFO_DELIMITER = "__";
+
+ private static final Collection/* <String> */JAR_PACKAGINGS = Arrays.asList(new String[] {
+ "ejb", "bundle", "maven-plugin", "eclipse-plugin", "jbi-component",
+ "jbi-shared-library", "orbit", "hk2-jar"});
+
+ static interface ConfMapper {
+ public void addMappingConfs(DefaultDependencyDescriptor dd, boolean isOptional);
+ }
+
+ static {
+ MAVEN2_CONF_MAPPING.put("compile", new ConfMapper() {
+ public void addMappingConfs(DefaultDependencyDescriptor dd, boolean isOptional) {
+ if (isOptional) {
+ dd.addDependencyConfiguration("optional", "compile(*)");
+ // dd.addDependencyConfiguration("optional", "provided(*)");
+ dd.addDependencyConfiguration("optional", "master(*)");
+
+ } else {
+ dd.addDependencyConfiguration("compile", "compile(*)");
+ // dd.addDependencyConfiguration("compile", "provided(*)");
+ dd.addDependencyConfiguration("compile", "master(*)");
+ dd.addDependencyConfiguration("runtime", "runtime(*)");
+ }
+ }
+ });
+ MAVEN2_CONF_MAPPING.put("provided", new ConfMapper() {
+ public void addMappingConfs(DefaultDependencyDescriptor dd, boolean isOptional) {
+ if (isOptional) {
+ dd.addDependencyConfiguration("optional", "compile(*)");
+ dd.addDependencyConfiguration("optional", "provided(*)");
+ dd.addDependencyConfiguration("optional", "runtime(*)");
+ dd.addDependencyConfiguration("optional", "master(*)");
+ } else {
+ dd.addDependencyConfiguration("provided", "compile(*)");
+ dd.addDependencyConfiguration("provided", "provided(*)");
+ dd.addDependencyConfiguration("provided", "runtime(*)");
+ dd.addDependencyConfiguration("provided", "master(*)");
+ }
+ }
+ });
+ MAVEN2_CONF_MAPPING.put("runtime", new ConfMapper() {
+ public void addMappingConfs(DefaultDependencyDescriptor dd, boolean isOptional) {
+ if (isOptional) {
+ dd.addDependencyConfiguration("optional", "compile(*)");
+ dd.addDependencyConfiguration("optional", "provided(*)");
+ dd.addDependencyConfiguration("optional", "master(*)");
+
+ } else {
+ dd.addDependencyConfiguration("runtime", "compile(*)");
+ dd.addDependencyConfiguration("runtime", "runtime(*)");
+ dd.addDependencyConfiguration("runtime", "master(*)");
+ }
+ }
+ });
+ MAVEN2_CONF_MAPPING.put("test", new ConfMapper() {
+ public void addMappingConfs(DefaultDependencyDescriptor dd, boolean isOptional) {
+ // optional doesn't make sense in the test scope
+ dd.addDependencyConfiguration("test", "runtime(*)");
+ dd.addDependencyConfiguration("test", "master(*)");
+ }
+ });
+ MAVEN2_CONF_MAPPING.put("system", new ConfMapper() {
+ public void addMappingConfs(DefaultDependencyDescriptor dd, boolean isOptional) {
+ // optional doesn't make sense in the system scope
+ dd.addDependencyConfiguration("system", "master(*)");
+ }
+ });
+ }
+
+ private final PomModuleDescriptor ivyModuleDescriptor;
+
+ private ModuleRevisionId mrid;
+
+ private DefaultArtifact mainArtifact;
+
+ private ParserSettings parserSettings;
+
+ private static final String WRONG_NUMBER_OF_PARTS_MSG = "what seemed to be a dependency "
+ + "management extra info exclusion had the wrong number of parts (should have 2) ";
+
+ public PomModuleDescriptorBuilder(ModuleDescriptorParser parser, Resource res,
+ ParserSettings ivySettings) {
+ ivyModuleDescriptor = new PomModuleDescriptor(parser, res);
+ ivyModuleDescriptor.setResolvedPublicationDate(new Date(res.getLastModified()));
+ for (int i = 0; i < MAVEN2_CONFIGURATIONS.length; i++) {
+ ivyModuleDescriptor.addConfiguration(MAVEN2_CONFIGURATIONS[i]);
+ }
+ ivyModuleDescriptor.setMappingOverride(true);
+ ivyModuleDescriptor.addExtraAttributeNamespace("m", Ivy.getIvyHomeURL() + "maven");
+ parserSettings = ivySettings;
+ }
+
+ public ModuleDescriptor getModuleDescriptor() {
+ return ivyModuleDescriptor;
+ }
+
+ public void setModuleRevId(String groupId, String artifactId, String version) {
+ mrid = ModuleRevisionId.newInstance(groupId, artifactId, version);
+ ivyModuleDescriptor.setModuleRevisionId(mrid);
+
+ if ((version == null) || version.endsWith("SNAPSHOT")) {
+ ivyModuleDescriptor.setStatus("integration");
+ } else {
+ ivyModuleDescriptor.setStatus("release");
+ }
+ }
+
+ public void setHomePage(String homePage) {
+ ivyModuleDescriptor.setHomePage(homePage);
+ }
+
+ public void setDescription(String description) {
+ ivyModuleDescriptor.setDescription(description);
+ }
+
+ public void setLicenses(License[] licenses) {
+ for (int i = 0; i < licenses.length; i++) {
+ ivyModuleDescriptor.addLicense(licenses[i]);
+ }
+ }
+
+ public void addMainArtifact(String artifactId, String packaging) {
+ String ext;
+
+ /*
+ * TODO: we should make packaging to ext mapping configurable, since it's not possible to
+ * cover all cases.
+ */
+ if ("pom".equals(packaging)) {
+ // no artifact defined! Add the default artifact if it exist.
+ DependencyResolver resolver = parserSettings.getResolver(mrid);
+
+ if (resolver != null) {
+ DefaultArtifact artifact = new DefaultArtifact(mrid, new Date(), artifactId, "jar",
+ "jar");
+ ArtifactOrigin artifactOrigin = resolver.locate(artifact);
+
+ if (!ArtifactOrigin.isUnknown(artifactOrigin)) {
+ mainArtifact = artifact;
+ ivyModuleDescriptor.addArtifact("master", mainArtifact);
+ }
+ }
+
+ return;
+ } else if (JAR_PACKAGINGS.contains(packaging)) {
+ ext = "jar";
+ } else if ("pear".equals(packaging)) {
+ ext = "phar";
+ } else {
+ ext = packaging;
+ }
+
+ mainArtifact = new DefaultArtifact(mrid, new Date(), artifactId, packaging, ext);
+ ivyModuleDescriptor.addArtifact("master", mainArtifact);
+ }
+
+ public void addDependency(Resource res, PomDependencyData dep) {
+ String scope = dep.getScope();
+ if ((scope != null) && (scope.length() > 0) && !MAVEN2_CONF_MAPPING.containsKey(scope)) {
+ // unknown scope, defaulting to 'compile'
+ scope = "compile";
+ }
+
+ String version = dep.getVersion();
+ version = (version == null || version.length() == 0) ? getDefaultVersion(dep) : version;
+ ModuleRevisionId moduleRevId = ModuleRevisionId.newInstance(dep.getGroupId(),
+ dep.getArtifactId(), version);
+
+ // Some POMs depend on theirselfves, don't add this dependency: Ivy doesn't allow this!
+ // Example: https://repo1.maven.org/maven2/net/jini/jsk-platform/2.1/jsk-platform-2.1.pom
+ ModuleRevisionId mRevId = ivyModuleDescriptor.getModuleRevisionId();
+ if ((mRevId != null) && mRevId.getModuleId().equals(moduleRevId.getModuleId())) {
+ return;
+ }
+
+ DefaultDependencyDescriptor dd = new PomDependencyDescriptor(dep, ivyModuleDescriptor,
+ moduleRevId);
+ scope = (scope == null || scope.length() == 0) ? getDefaultScope(dep) : scope;
+ ConfMapper mapping = (ConfMapper) MAVEN2_CONF_MAPPING.get(scope);
+ mapping.addMappingConfs(dd, dep.isOptional());
+ Map extraAtt = new HashMap();
+ if ((dep.getClassifier() != null)
+ || ((dep.getType() != null) && !"jar".equals(dep.getType()))) {
+ String type = "jar";
+ if (dep.getType() != null) {
+ type = dep.getType();
+ }
+ String ext = type;
+
+ // if type is 'test-jar', the extension is 'jar' and the classifier is 'tests'
+ // Cfr. http://maven.apache.org/guides/mini/guide-attached-tests.html
+ if ("test-jar".equals(type)) {
+ ext = "jar";
+ extraAtt.put("m:classifier", "tests");
+ } else if (JAR_PACKAGINGS.contains(type)) {
+ ext = "jar";
+ }
+
+ // we deal with classifiers by setting an extra attribute and forcing the
+ // dependency to assume such an artifact is published
+ if (dep.getClassifier() != null) {
+ extraAtt.put("m:classifier", dep.getClassifier());
+ }
+ DefaultDependencyArtifactDescriptor depArtifact = new DefaultDependencyArtifactDescriptor(
+ dd, dd.getDependencyId().getName(), type, ext, null, extraAtt);
+ // here we have to assume a type and ext for the artifact, so this is a limitation
+ // compared to how m2 behave with classifiers
+ String optionalizedScope = dep.isOptional() ? "optional" : scope;
+ dd.addDependencyArtifact(optionalizedScope, depArtifact);
+ }
+
+ // experimentation shows the following, excluded modules are
+ // inherited from parent POMs if either of the following is true:
+ // the <exclusions> element is missing or the <exclusions> element
+ // is present, but empty.
+ List /* <ModuleId> */excluded = dep.getExcludedModules();
+ if (excluded.isEmpty()) {
+ excluded = getDependencyMgtExclusions(ivyModuleDescriptor, dep.getGroupId(),
+ dep.getArtifactId());
+ }
+ for (Iterator itExcl = excluded.iterator(); itExcl.hasNext();) {
+ ModuleId excludedModule = (ModuleId) itExcl.next();
+ String[] confs = dd.getModuleConfigurations();
+ for (int k = 0; k < confs.length; k++) {
+ dd.addExcludeRule(confs[k], new DefaultExcludeRule(new ArtifactId(excludedModule,
+ PatternMatcher.ANY_EXPRESSION, PatternMatcher.ANY_EXPRESSION,
+ PatternMatcher.ANY_EXPRESSION), ExactPatternMatcher.INSTANCE, null));
+ }
+ }
+
+ ivyModuleDescriptor.addDependency(dd);
+ }
+
+ public void addDependency(DependencyDescriptor descriptor) {
+ // Some POMs depend on themselves through their parent pom, don't add this dependency
+ // since Ivy doesn't allow this!
+ // Example:
+ // https://repo1.maven.org/maven2/com/atomikos/atomikos-util/3.6.4/atomikos-util-3.6.4.pom
+ ModuleId dependencyId = descriptor.getDependencyId();
+ ModuleRevisionId mRevId = ivyModuleDescriptor.getModuleRevisionId();
+ if ((mRevId != null) && mRevId.getModuleId().equals(dependencyId)) {
+ return;
+ }
+
+ ivyModuleDescriptor.addDependency(descriptor);
+ }
+
+ public void addDependencyMgt(PomDependencyMgt dep) {
+ ivyModuleDescriptor.addDependencyManagement(dep);
+
+ String key = getDependencyMgtExtraInfoKeyForVersion(dep.getGroupId(), dep.getArtifactId());
+ overwriteExtraInfoIfExists(key, dep.getVersion());
+ if (dep.getScope() != null) {
+ String scopeKey = getDependencyMgtExtraInfoKeyForScope(dep.getGroupId(),
+ dep.getArtifactId());
+ overwriteExtraInfoIfExists(scopeKey, dep.getScope());
+ }
+ if (!dep.getExcludedModules().isEmpty()) {
+ final String exclusionPrefix = getDependencyMgtExtraInfoPrefixForExclusion(
+ dep.getGroupId(), dep.getArtifactId());
+ int index = 0;
+ for (final Iterator iter = dep.getExcludedModules().iterator(); iter.hasNext();) {
+ final ModuleId excludedModule = (ModuleId) iter.next();
+ overwriteExtraInfoIfExists(
+ exclusionPrefix + index,
+ excludedModule.getOrganisation() + EXTRA_INFO_DELIMITER
+ + excludedModule.getName());
+ index += 1;
+ }
+ }
+ // dependency management info is also used for version mediation of transitive dependencies
+ ivyModuleDescriptor.addDependencyDescriptorMediator(
+ ModuleId.newInstance(dep.getGroupId(), dep.getArtifactId()),
+ ExactPatternMatcher.INSTANCE,
+ new OverrideDependencyDescriptorMediator(null, dep.getVersion()));
+ }
+
+ public void addPlugin(PomDependencyMgt plugin) {
+ String pluginValue = plugin.getGroupId() + EXTRA_INFO_DELIMITER + plugin.getArtifactId()
+ + EXTRA_INFO_DELIMITER + plugin.getVersion();
+ ExtraInfoHolder extraInfoByTagName = ivyModuleDescriptor
+ .getExtraInfoByTagName("m:maven.plugins");
+ if (extraInfoByTagName == null) {
+ extraInfoByTagName = new ExtraInfoHolder();
+ extraInfoByTagName.setName("m:maven.plugins");
+ ivyModuleDescriptor.addExtraInfo(extraInfoByTagName);
+ }
+ String pluginExtraInfo = extraInfoByTagName.getContent();
+ if (pluginExtraInfo == null) {
+ pluginExtraInfo = pluginValue;
+ } else {
+ pluginExtraInfo = pluginExtraInfo + "|" + pluginValue;
+ }
+ extraInfoByTagName.setContent(pluginExtraInfo);
+ }
+
+ public static List /* <PomDependencyMgt> */getPlugins(ModuleDescriptor md) {
+ List result = new ArrayList();
+ String plugins = md.getExtraInfoContentByTagName("m:maven.plugins");
+ if (plugins == null) {
+ return new ArrayList();
+ }
+ String[] pluginsArray = plugins.split("\\|");
+ for (int i = 0; i < pluginsArray.length; i++) {
+ String[] parts = pluginsArray[i].split(EXTRA_INFO_DELIMITER);
+ result.add(new PomPluginElement(parts[0], parts[1], parts[2]));
+ }
+
+ return result;
+ }
+
+ private static class PomPluginElement implements PomDependencyMgt {
+ private String groupId;
+
+ private String artifactId;
+
+ private String version;
+
+ public PomPluginElement(String groupId, String artifactId, String version) {
+ this.groupId = groupId;
+ this.artifactId = artifactId;
+ this.version = version;
+ }
+
+ public String getGroupId() {
+ return groupId;
+ }
+
+ public String getArtifactId() {
+ return artifactId;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public String getScope() {
+ return null;
+ }
+
+ public List /* <ModuleId> */getExcludedModules() {
+ return Collections.EMPTY_LIST; // probably not used?
+ }
+ }
+
+ private String getDefaultVersion(PomDependencyData dep) {
+ ModuleId moduleId = ModuleId.newInstance(dep.getGroupId(), dep.getArtifactId());
+ if (ivyModuleDescriptor.getDependencyManagementMap().containsKey(moduleId)) {
+ return ((PomDependencyMgt) ivyModuleDescriptor.getDependencyManagementMap().get(
+ moduleId)).getVersion();
+ }
+ String key = getDependencyMgtExtraInfoKeyForVersion(dep.getGroupId(), dep.getArtifactId());
+ return ivyModuleDescriptor.getExtraInfoContentByTagName(key);
+ }
+
+ private String getDefaultScope(PomDependencyData dep) {
+ String result;
+ ModuleId moduleId = ModuleId.newInstance(dep.getGroupId(), dep.getArtifactId());
+ if (ivyModuleDescriptor.getDependencyManagementMap().containsKey(moduleId)) {
+ result = ((PomDependencyMgt) ivyModuleDescriptor.getDependencyManagementMap().get(
+ moduleId)).getScope();
+ } else {
+ String key = getDependencyMgtExtraInfoKeyForScope(dep.getGroupId(), dep.getArtifactId());
+ result = ivyModuleDescriptor.getExtraInfoContentByTagName(key);
+ }
+ if ((result == null) || !MAVEN2_CONF_MAPPING.containsKey(result)) {
+ result = "compile";
+ }
+ return result;
+ }
+
+ private static String getDependencyMgtExtraInfoKeyForVersion(String groupId, String artifaceId) {
+ return DEPENDENCY_MANAGEMENT + EXTRA_INFO_DELIMITER + groupId + EXTRA_INFO_DELIMITER
+ + artifaceId + EXTRA_INFO_DELIMITER + "version";
+ }
+
+ private static String getDependencyMgtExtraInfoKeyForScope(String groupId, String artifaceId) {
+ return DEPENDENCY_MANAGEMENT + EXTRA_INFO_DELIMITER + groupId + EXTRA_INFO_DELIMITER
+ + artifaceId + EXTRA_INFO_DELIMITER + "scope";
+ }
+
+ private static String getPropertyExtraInfoKey(String propertyName) {
+ return PROPERTIES + EXTRA_INFO_DELIMITER + propertyName;
+ }
+
+ private static String getDependencyMgtExtraInfoPrefixForExclusion(String groupId,
+ String artifaceId) {
+ return DEPENDENCY_MANAGEMENT + EXTRA_INFO_DELIMITER + groupId + EXTRA_INFO_DELIMITER
+ + artifaceId + EXTRA_INFO_DELIMITER + "exclusion_";
+ }
+
+ private static List /* <ModuleId> */getDependencyMgtExclusions(ModuleDescriptor descriptor,
+ String groupId, String artifactId) {
+ if (descriptor instanceof PomModuleDescriptor) {
+ PomDependencyMgt dependencyMgt = (PomDependencyMgt) ((PomModuleDescriptor) descriptor)
+ .getDependencyManagementMap().get(ModuleId.newInstance(groupId, artifactId));
+ if (dependencyMgt != null) {
+ return dependencyMgt.getExcludedModules();
+ }
+ }
+ String exclusionPrefix = getDependencyMgtExtraInfoPrefixForExclusion(groupId, artifactId);
+ List /* <ModuleId> */exclusionIds = new LinkedList /* <ModuleId> */();
+ for (ExtraInfoHolder extraInfoHolder : descriptor.getExtraInfos()) {
+ String key = extraInfoHolder.getName();
+ if (key.startsWith(exclusionPrefix)) {
+ String fullExclusion = extraInfoHolder.getContent();
+ String[] exclusionParts = fullExclusion.split(EXTRA_INFO_DELIMITER);
+ if (exclusionParts.length != 2) {
+ Message.error(WRONG_NUMBER_OF_PARTS_MSG + exclusionParts.length + " : "
+ + fullExclusion);
+ continue;
+ }
+ exclusionIds.add(ModuleId.newInstance(exclusionParts[0], exclusionParts[1]));
+ }
+ }
+ return exclusionIds;
+ }
+
+ public static Map/* <ModuleId, String version> */
+ getDependencyManagementMap(ModuleDescriptor md) {
+ Map ret = new LinkedHashMap();
+ if (md instanceof PomModuleDescriptor) {
+ for (final Iterator iterator = ((PomModuleDescriptor) md).getDependencyManagementMap()
+ .entrySet().iterator(); iterator.hasNext();) {
+ Map.Entry e = (Entry) iterator.next();
+ PomDependencyMgt dependencyMgt = (PomDependencyMgt) e.getValue();
+ ret.put(e.getKey(), dependencyMgt.getVersion());
+ }
+ } else {
+ for (ExtraInfoHolder extraInfoHolder : md.getExtraInfos()) {
+ String key = extraInfoHolder.getName();
+ if ((key).startsWith(DEPENDENCY_MANAGEMENT)) {
+ String[] parts = key.split(EXTRA_INFO_DELIMITER);
+ if (parts.length != DEPENDENCY_MANAGEMENT_KEY_PARTS_COUNT) {
+ Message.warn("what seem to be a dependency management extra info "
+ + "doesn't match expected pattern: " + key);
+ } else {
+ ret.put(ModuleId.newInstance(parts[1], parts[2]),
+ extraInfoHolder.getContent());
+ }
+ }
+ }
+ }
+ return ret;
+ }
+
+ public static List getDependencyManagements(ModuleDescriptor md) {
+ List result = new ArrayList();
+
+ if (md instanceof PomModuleDescriptor) {
+ result.addAll(((PomModuleDescriptor) md).getDependencyManagementMap().values());
+ } else {
+ for (ExtraInfoHolder extraInfoHolder : md.getExtraInfos()) {
+ String key = extraInfoHolder.getName();
+ if ((key).startsWith(DEPENDENCY_MANAGEMENT)) {
+ String[] parts = key.split(EXTRA_INFO_DELIMITER);
+ if (parts.length != DEPENDENCY_MANAGEMENT_KEY_PARTS_COUNT) {
+ Message.warn("what seem to be a dependency management extra info "
+ + "doesn't match expected pattern: " + key);
+ } else {
+ String versionKey = DEPENDENCY_MANAGEMENT + EXTRA_INFO_DELIMITER + parts[1]
+ + EXTRA_INFO_DELIMITER + parts[2] + EXTRA_INFO_DELIMITER
+ + "version";
+ String scopeKey = DEPENDENCY_MANAGEMENT + EXTRA_INFO_DELIMITER + parts[1]
+ + EXTRA_INFO_DELIMITER + parts[2] + EXTRA_INFO_DELIMITER + "scope";
+
+ String version = md.getExtraInfoContentByTagName(versionKey);
+ String scope = md.getExtraInfoContentByTagName(scopeKey);
+
+ List /* <ModuleId> */exclusions = getDependencyMgtExclusions(md, parts[1],
+ parts[2]);
+ result.add(new DefaultPomDependencyMgt(parts[1], parts[2], version, scope,
+ exclusions));
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ @Deprecated
+ public void addExtraInfos(Map extraAttributes) {
+ for (Iterator it = extraAttributes.entrySet().iterator(); it.hasNext();) {
+ Map.Entry entry = (Entry) it.next();
+ String key = (String) entry.getKey();
+ String value = (String) entry.getValue();
+ addExtraInfo(key, value);
+ }
+ }
+
+ private void addExtraInfo(String key, String value) {
+ if (ivyModuleDescriptor.getExtraInfoByTagName(key) == null) {
+ ivyModuleDescriptor.getExtraInfos().add(new ExtraInfoHolder(key, value));
+ }
+ }
+
+ private void overwriteExtraInfoIfExists(String key, String value) {
+ boolean found = false;
+ for (ExtraInfoHolder extraInfoHolder : ivyModuleDescriptor.getExtraInfos()) {
+ if (extraInfoHolder.getName().equals(key)) {
+ extraInfoHolder.setContent(value);
+ found = true;
+ }
+ }
+ if (!found) {
+ ivyModuleDescriptor.getExtraInfos().add(new ExtraInfoHolder(key, value));
+ }
+ }
+
+ public void addExtraInfos(List<ExtraInfoHolder> extraInfosHolder) {
+ for (ExtraInfoHolder extraInfoHolder : extraInfosHolder) {
+ addExtraInfo(extraInfoHolder.getName(), extraInfoHolder.getContent());
+ }
+ }
+
+ @Deprecated
+ public static Map extractPomProperties(Map extraInfo) {
+ Map r = new HashMap();
+ for (Iterator it = extraInfo.entrySet().iterator(); it.hasNext();) {
+ Map.Entry extraInfoEntry = (Map.Entry) it.next();
+ if (((String) extraInfoEntry.getKey()).startsWith(PROPERTIES)) {
+ String prop = ((String) extraInfoEntry.getKey()).substring(PROPERTIES.length()
+ + EXTRA_INFO_DELIMITER.length());
+ r.put(prop, extraInfoEntry.getValue());
+ }
+ }
+ return r;
+ }
+
+ public static Map extractPomProperties(List<ExtraInfoHolder> extraInfos) {
+ Map r = new HashMap();
+ for (ExtraInfoHolder extraInfoHolder : extraInfos) {
+ if ((extraInfoHolder.getName()).startsWith(PROPERTIES)) {
+ String prop = (extraInfoHolder.getName()).substring(PROPERTIES.length()
+ + EXTRA_INFO_DELIMITER.length());
+ r.put(prop, extraInfoHolder.getContent());
+ }
+ }
+ return r;
+ }
+
+ public void addProperty(String propertyName, String value) {
+ addExtraInfo(getPropertyExtraInfoKey(propertyName), value);
+ }
+
+ public Artifact getMainArtifact() {
+ return mainArtifact;
+ }
+
+ public Artifact getSourceArtifact() {
+ return new MDArtifact(ivyModuleDescriptor, mrid.getName(), "source", "jar", null,
+ Collections.singletonMap("m:classifier", "sources"));
+ }
+
+ public Artifact getSrcArtifact() {
+ return new MDArtifact(ivyModuleDescriptor, mrid.getName(), "source", "jar", null,
+ Collections.singletonMap("m:classifier", "src"));
+ }
+
+ public Artifact getJavadocArtifact() {
+ return new MDArtifact(ivyModuleDescriptor, mrid.getName(), "javadoc", "jar", null,
+ Collections.singletonMap("m:classifier", "javadoc"));
+ }
+
+ public void addSourceArtifact() {
+ ivyModuleDescriptor.addArtifact("sources", getSourceArtifact());
+ }
+
+ public void addSrcArtifact() {
+ ivyModuleDescriptor.addArtifact("sources", getSrcArtifact());
+ }
+
+ public void addJavadocArtifact() {
+ ivyModuleDescriptor.addArtifact("javadoc", getJavadocArtifact());
+ }
+
+ /**
+ * <code>DependencyDescriptor</code> that provides access to the original
+ * <code>PomDependencyData</code>.
+ */
+ public static class PomDependencyDescriptor extends DefaultDependencyDescriptor {
+ private final PomDependencyData pomDependencyData;
+
+ private PomDependencyDescriptor(PomDependencyData pomDependencyData,
+ ModuleDescriptor moduleDescriptor, ModuleRevisionId revisionId) {
+ super(moduleDescriptor, revisionId, true, false, true);
+ this.pomDependencyData = pomDependencyData;
+ }
+
+ /**
+ * Get PomDependencyData.
+ *
+ * @return PomDependencyData
+ */
+ public PomDependencyData getPomDependencyData() {
+ return pomDependencyData;
+ }
+ }
+
+ public static class PomModuleDescriptor extends DefaultModuleDescriptor {
+ private final Map/* <ModuleId, PomDependencyMgt> */dependencyManagementMap = new HashMap();
+
+ public PomModuleDescriptor(ModuleDescriptorParser parser, Resource res) {
+ super(parser, res);
+ }
+
+ public void addDependencyManagement(PomDependencyMgt dependencyMgt) {
+ dependencyManagementMap.put(
+ ModuleId.newInstance(dependencyMgt.getGroupId(), dependencyMgt.getArtifactId()),
+ dependencyMgt);
+ }
+
+ public Map getDependencyManagementMap() {
+ return dependencyManagementMap;
+ }
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/parser/m2/PomModuleDescriptorParser.java b/src/java/org/apache/ivy/plugins/parser/m2/PomModuleDescriptorParser.java
new file mode 100644
index 0000000..6017e3a
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/parser/m2/PomModuleDescriptorParser.java
@@ -0,0 +1,380 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.parser.m2;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.text.ParseException;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.ivy.core.IvyContext;
+import org.apache.ivy.core.cache.ArtifactOrigin;
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.Configuration;
+import org.apache.ivy.core.module.descriptor.Configuration.Visibility;
+import org.apache.ivy.core.module.descriptor.DefaultArtifact;
+import org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.resolve.ResolveData;
+import org.apache.ivy.core.resolve.ResolveEngine;
+import org.apache.ivy.core.resolve.ResolveOptions;
+import org.apache.ivy.core.resolve.ResolvedModuleRevision;
+import org.apache.ivy.plugins.namespace.NameSpaceHelper;
+import org.apache.ivy.plugins.parser.ModuleDescriptorParser;
+import org.apache.ivy.plugins.parser.ParserSettings;
+import org.apache.ivy.plugins.parser.m2.PomModuleDescriptorBuilder.PomDependencyDescriptor;
+import org.apache.ivy.plugins.parser.m2.PomReader.PomDependencyData;
+import org.apache.ivy.plugins.parser.m2.PomReader.PomDependencyMgtElement;
+import org.apache.ivy.plugins.parser.xml.XmlModuleDescriptorWriter;
+import org.apache.ivy.plugins.repository.Resource;
+import org.apache.ivy.plugins.repository.url.URLResource;
+import org.apache.ivy.plugins.resolver.DependencyResolver;
+import org.apache.ivy.util.Message;
+import org.xml.sax.SAXException;
+
+/**
+ * A parser for Maven 2 POM.
+ * <p>
+ * The configurations used in the generated module descriptor mimics the behavior defined by maven 2
+ * scopes, as documented here:<br/>
+ * http://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html The
+ * PomModuleDescriptorParser use a PomDomReader to read the pom, and the PomModuleDescriptorBuilder
+ * to write the ivy module descriptor using the info read by the PomDomReader.
+ */
+public final class PomModuleDescriptorParser implements ModuleDescriptorParser {
+
+ private static final PomModuleDescriptorParser INSTANCE = new PomModuleDescriptorParser();
+
+ public static PomModuleDescriptorParser getInstance() {
+ return INSTANCE;
+ }
+
+ private PomModuleDescriptorParser() {
+ }
+
+ public void toIvyFile(InputStream is, Resource res, File destFile, ModuleDescriptor md)
+ throws ParseException, IOException {
+ try {
+ XmlModuleDescriptorWriter.write(md, destFile);
+ } finally {
+ if (is != null) {
+ is.close();
+ }
+ }
+ }
+
+ public boolean accept(Resource res) {
+ return res.getName().endsWith(".pom") || res.getName().endsWith("pom.xml")
+ || res.getName().endsWith("project.xml");
+ }
+
+ public String toString() {
+ return "pom parser";
+ }
+
+ public Artifact getMetadataArtifact(ModuleRevisionId mrid, Resource res) {
+ return DefaultArtifact.newPomArtifact(mrid, new Date(res.getLastModified()));
+ }
+
+ public String getType() {
+ return "pom";
+ }
+
+ public ModuleDescriptor parseDescriptor(ParserSettings ivySettings, URL descriptorURL,
+ boolean validate) throws ParseException, IOException {
+ URLResource resource = new URLResource(descriptorURL);
+ return parseDescriptor(ivySettings, descriptorURL, resource, validate);
+ }
+
+ public ModuleDescriptor parseDescriptor(ParserSettings ivySettings, URL descriptorURL,
+ Resource res, boolean validate) throws ParseException, IOException {
+
+ PomModuleDescriptorBuilder mdBuilder = new PomModuleDescriptorBuilder(this, res,
+ ivySettings);
+
+ try {
+ PomReader domReader = new PomReader(descriptorURL, res);
+ domReader.setProperty("parent.version", domReader.getParentVersion());
+ domReader.setProperty("parent.groupId", domReader.getParentGroupId());
+ domReader.setProperty("project.parent.version", domReader.getParentVersion());
+ domReader.setProperty("project.parent.groupId", domReader.getParentGroupId());
+
+ Map pomProperties = domReader.getPomProperties();
+ for (Iterator iter = pomProperties.entrySet().iterator(); iter.hasNext();) {
+ Map.Entry prop = (Map.Entry) iter.next();
+ domReader.setProperty((String) prop.getKey(), (String) prop.getValue());
+ mdBuilder.addProperty((String) prop.getKey(), (String) prop.getValue());
+ }
+
+ ModuleDescriptor parentDescr = null;
+ if (domReader.hasParent()) {
+ // Is there any other parent properties?
+
+ ModuleRevisionId parentModRevID = ModuleRevisionId.newInstance(
+ domReader.getParentGroupId(), domReader.getParentArtifactId(),
+ domReader.getParentVersion());
+ ResolvedModuleRevision parentModule = parseOtherPom(ivySettings, parentModRevID);
+ if (parentModule != null) {
+ parentDescr = parentModule.getDescriptor();
+ } else {
+ throw new IOException("Impossible to load parent for " + res.getName() + "."
+ + " Parent=" + parentModRevID);
+ }
+ if (parentDescr != null) {
+ Map parentPomProps = PomModuleDescriptorBuilder
+ .extractPomProperties(parentDescr.getExtraInfos());
+ for (Iterator iter = parentPomProps.entrySet().iterator(); iter.hasNext();) {
+ Map.Entry prop = (Map.Entry) iter.next();
+ domReader.setProperty((String) prop.getKey(), (String) prop.getValue());
+ }
+ }
+ }
+
+ String groupId = domReader.getGroupId();
+ String artifactId = domReader.getArtifactId();
+ String version = domReader.getVersion();
+ mdBuilder.setModuleRevId(groupId, artifactId, version);
+
+ mdBuilder.setHomePage(domReader.getHomePage());
+ mdBuilder.setDescription(domReader.getDescription());
+ mdBuilder.setLicenses(domReader.getLicenses());
+
+ ModuleRevisionId relocation = domReader.getRelocation();
+
+ if (relocation != null) {
+ if (groupId != null && artifactId != null
+ && artifactId.equals(relocation.getName())
+ && groupId.equals(relocation.getOrganisation())) {
+ Message.error("Relocation to an other version number not supported in ivy : "
+ + mdBuilder.getModuleDescriptor().getModuleRevisionId()
+ + " relocated to " + relocation
+ + ". Please update your dependency to directly use the right version.");
+ Message.warn("Resolution will only pick dependencies of the relocated element."
+ + " Artefact and other metadata will be ignored.");
+ ResolvedModuleRevision relocatedModule = parseOtherPom(ivySettings, relocation);
+ if (relocatedModule == null) {
+ throw new ParseException("impossible to load module " + relocation
+ + " to which "
+ + mdBuilder.getModuleDescriptor().getModuleRevisionId()
+ + " has been relocated", 0);
+ }
+ DependencyDescriptor[] dds = relocatedModule.getDescriptor().getDependencies();
+ for (int i = 0; i < dds.length; i++) {
+ mdBuilder.addDependency(dds[i]);
+ }
+ } else {
+ Message.info(mdBuilder.getModuleDescriptor().getModuleRevisionId()
+ + " is relocated to " + relocation
+ + ". Please update your dependencies.");
+ Message.verbose("Relocated module will be considered as a dependency");
+ DefaultDependencyDescriptor dd = new DefaultDependencyDescriptor(
+ mdBuilder.getModuleDescriptor(), relocation, true, false, true);
+ /* Map all public dependencies */
+ Configuration[] m2Confs = PomModuleDescriptorBuilder.MAVEN2_CONFIGURATIONS;
+ for (int i = 0; i < m2Confs.length; i++) {
+ if (Visibility.PUBLIC.equals(m2Confs[i].getVisibility())) {
+ dd.addDependencyConfiguration(m2Confs[i].getName(),
+ m2Confs[i].getName());
+ }
+ }
+ mdBuilder.addDependency(dd);
+ }
+ } else {
+ domReader.setProperty("project.groupId", groupId);
+ domReader.setProperty("pom.groupId", groupId);
+ domReader.setProperty("groupId", groupId);
+ domReader.setProperty("project.artifactId", artifactId);
+ domReader.setProperty("pom.artifactId", artifactId);
+ domReader.setProperty("artifactId", artifactId);
+ domReader.setProperty("project.version", version);
+ domReader.setProperty("pom.version", version);
+ domReader.setProperty("version", version);
+
+ if (parentDescr != null) {
+ mdBuilder.addExtraInfos(parentDescr.getExtraInfos());
+
+ // add dependency management info from parent
+ List depMgt = PomModuleDescriptorBuilder.getDependencyManagements(parentDescr);
+ for (Iterator it = depMgt.iterator(); it.hasNext();) {
+ PomDependencyMgt dep = (PomDependencyMgt) it.next();
+ if (dep instanceof PomDependencyMgtElement) {
+ dep = domReader.new PomDependencyMgtElement(
+ (PomDependencyMgtElement) dep);
+ }
+ mdBuilder.addDependencyMgt(dep);
+ }
+
+ // add plugins from parent
+ List /* <PomDependencyMgt> */plugins = PomModuleDescriptorBuilder
+ .getPlugins(parentDescr);
+ for (Iterator it = plugins.iterator(); it.hasNext();) {
+ mdBuilder.addPlugin((PomDependencyMgt) it.next());
+ }
+ }
+
+ for (Iterator it = domReader.getDependencyMgt().iterator(); it.hasNext();) {
+ PomDependencyMgt dep = (PomDependencyMgt) it.next();
+ if ("import".equals(dep.getScope())) {
+ ModuleRevisionId importModRevID = ModuleRevisionId.newInstance(
+ dep.getGroupId(), dep.getArtifactId(), dep.getVersion());
+ ResolvedModuleRevision importModule = parseOtherPom(ivySettings,
+ importModRevID);
+ if (importModule != null) {
+ ModuleDescriptor importDescr = importModule.getDescriptor();
+
+ // add dependency management info from imported module
+ List depMgt = PomModuleDescriptorBuilder
+ .getDependencyManagements(importDescr);
+ for (Iterator it2 = depMgt.iterator(); it2.hasNext();) {
+ PomDependencyMgt importedDepMgt = (PomDependencyMgt) it2.next();
+ mdBuilder.addDependencyMgt(new DefaultPomDependencyMgt(
+ importedDepMgt.getGroupId(),
+ importedDepMgt.getArtifactId(),
+ importedDepMgt.getVersion(), importedDepMgt.getScope(),
+ importedDepMgt.getExcludedModules()));
+ }
+ } else {
+ throw new IOException("Impossible to import module for "
+ + res.getName() + "." + " Import=" + importModRevID);
+ }
+
+ } else {
+ mdBuilder.addDependencyMgt(dep);
+ }
+ }
+
+ for (Iterator it = domReader.getDependencies().iterator(); it.hasNext();) {
+ PomReader.PomDependencyData dep = (PomReader.PomDependencyData) it.next();
+ mdBuilder.addDependency(res, dep);
+ }
+
+ if (parentDescr != null) {
+ for (int i = 0; i < parentDescr.getDependencies().length; i++) {
+ DependencyDescriptor descriptor = parentDescr.getDependencies()[i];
+ if (descriptor instanceof PomDependencyDescriptor) {
+ PomDependencyData parentDep = ((PomDependencyDescriptor) descriptor)
+ .getPomDependencyData();
+ PomDependencyData dep = domReader.new PomDependencyData(parentDep);
+ mdBuilder.addDependency(res, dep);
+ } else {
+ mdBuilder.addDependency(descriptor);
+ }
+ }
+ }
+
+ for (Iterator it = domReader.getPlugins().iterator(); it.hasNext();) {
+ PomReader.PomPluginElement plugin = (PomReader.PomPluginElement) it.next();
+ mdBuilder.addPlugin(plugin);
+ }
+
+ mdBuilder.addMainArtifact(artifactId, domReader.getPackaging());
+
+ addSourcesAndJavadocArtifactsIfPresent(mdBuilder, ivySettings);
+ }
+ } catch (SAXException e) {
+ throw newParserException(e);
+ }
+
+ return mdBuilder.getModuleDescriptor();
+ }
+
+ private void addSourcesAndJavadocArtifactsIfPresent(PomModuleDescriptorBuilder mdBuilder,
+ ParserSettings ivySettings) {
+ if (mdBuilder.getMainArtifact() == null) {
+ // no main artifact in pom, we don't need to search for meta artifacts
+ return;
+ }
+ ModuleDescriptor md = mdBuilder.getModuleDescriptor();
+ ModuleRevisionId mrid = md.getModuleRevisionId();
+ DependencyResolver resolver = ivySettings.getResolver(mrid);
+
+ if (resolver == null) {
+ Message.debug("no resolver found for " + mrid
+ + ": no source or javadoc artifact lookup");
+ } else {
+ ArtifactOrigin mainArtifact = resolver.locate(mdBuilder.getMainArtifact());
+
+ if (!ArtifactOrigin.isUnknown(mainArtifact)) {
+ String mainArtifactLocation = mainArtifact.getLocation();
+
+ ArtifactOrigin sourceArtifact = resolver.locate(mdBuilder.getSourceArtifact());
+ if (!ArtifactOrigin.isUnknown(sourceArtifact)
+ && !sourceArtifact.getLocation().equals(mainArtifactLocation)) {
+ Message.debug("source artifact found for " + mrid);
+ mdBuilder.addSourceArtifact();
+ } else {
+ // it seems that sometimes the 'src' classifier is used instead of 'sources'
+ // Cfr. IVY-1138
+ ArtifactOrigin srcArtifact = resolver.locate(mdBuilder.getSrcArtifact());
+ if (!ArtifactOrigin.isUnknown(srcArtifact)
+ && !srcArtifact.getLocation().equals(mainArtifactLocation)) {
+ Message.debug("source artifact found for " + mrid);
+ mdBuilder.addSrcArtifact();
+ } else {
+ Message.debug("no source artifact found for " + mrid);
+ }
+ }
+ ArtifactOrigin javadocArtifact = resolver.locate(mdBuilder.getJavadocArtifact());
+ if (!ArtifactOrigin.isUnknown(javadocArtifact)
+ && !javadocArtifact.getLocation().equals(mainArtifactLocation)) {
+ Message.debug("javadoc artifact found for " + mrid);
+ mdBuilder.addJavadocArtifact();
+ } else {
+ Message.debug("no javadoc artifact found for " + mrid);
+ }
+ }
+ }
+ }
+
+ private ResolvedModuleRevision parseOtherPom(ParserSettings ivySettings,
+ ModuleRevisionId parentModRevID) throws ParseException {
+ DependencyDescriptor dd = new DefaultDependencyDescriptor(parentModRevID, true);
+ ResolveData data = IvyContext.getContext().getResolveData();
+ if (data == null) {
+ ResolveEngine engine = IvyContext.getContext().getIvy().getResolveEngine();
+ ResolveOptions options = new ResolveOptions();
+ options.setDownload(false);
+ data = new ResolveData(engine, options);
+ }
+
+ DependencyResolver resolver = ivySettings.getResolver(parentModRevID);
+ if (resolver == null) {
+ // TODO: Throw exception here?
+ return null;
+ } else {
+ dd = NameSpaceHelper.toSystem(dd, ivySettings.getContextNamespace());
+ ResolvedModuleRevision otherModule = resolver.getDependency(dd, data);
+ return otherModule;
+ }
+ }
+
+ private ParseException newParserException(Exception e) {
+ Message.error(e.getMessage());
+ ParseException pe = new ParseException(e.getMessage(), 0);
+ pe.initCause(e);
+ return pe;
+ }
+
+}
diff --git a/src/java/org/apache/ivy/plugins/parser/m2/PomModuleDescriptorWriter.java b/src/java/org/apache/ivy/plugins/parser/m2/PomModuleDescriptorWriter.java
new file mode 100644
index 0000000..5cd254a
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/parser/m2/PomModuleDescriptorWriter.java
@@ -0,0 +1,392 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.parser.m2;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.LineNumberReader;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.ivy.Ivy;
+import org.apache.ivy.core.IvyContext;
+import org.apache.ivy.core.IvyPatternHelper;
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.DependencyArtifactDescriptor;
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.ExcludeRule;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.settings.IvySettings;
+import org.apache.ivy.core.settings.IvyVariableContainer;
+import org.apache.ivy.plugins.parser.m2.PomWriterOptions.ConfigurationScopeMapping;
+import org.apache.ivy.plugins.parser.m2.PomWriterOptions.ExtraDependency;
+import org.apache.ivy.util.ConfigurationUtils;
+
+public final class PomModuleDescriptorWriter {
+
+ private static final String SKIP_LINE = "SKIP_LINE";
+
+ private static final ConfigurationScopeMapping DEFAULT_MAPPING = new ConfigurationScopeMapping(
+ new HashMap() {
+ {
+ put("compile", "compile");
+ put("runtime", "runtime");
+ put("provided", "provided");
+ put("test", "test");
+ put("system", "system");
+ }
+ });
+
+ private PomModuleDescriptorWriter() {
+ }
+
+ public static void write(ModuleDescriptor md, File output, PomWriterOptions options)
+ throws IOException {
+ LineNumberReader in;
+ if (options.getTemplate() == null) {
+ in = new LineNumberReader(new InputStreamReader(
+ PomModuleDescriptorWriter.class.getResourceAsStream("pom.template")));
+ } else {
+ in = new LineNumberReader(new InputStreamReader(new FileInputStream(
+ options.getTemplate())));
+ }
+
+ if (output.getParentFile() != null) {
+ output.getParentFile().mkdirs();
+ }
+ PrintWriter out = new PrintWriter(new OutputStreamWriter(new FileOutputStream(output),
+ "UTF-8"));
+ try {
+ IvySettings settings = IvyContext.getContext().getSettings();
+ IvyVariableContainer variables = new IvyVariableContainerWrapper(
+ settings.getVariableContainer());
+
+ variables.setVariable("ivy.pom.license", SKIP_LINE, true);
+ variables.setVariable("ivy.pom.header", SKIP_LINE, true);
+ variables.setVariable("ivy.pom.groupId", SKIP_LINE, true);
+ variables.setVariable("ivy.pom.artifactId", SKIP_LINE, true);
+ variables.setVariable("ivy.pom.version", SKIP_LINE, true);
+ variables.setVariable("ivy.pom.packaging", SKIP_LINE, true);
+ variables.setVariable("ivy.pom.name", SKIP_LINE, true);
+ variables.setVariable("ivy.pom.description", SKIP_LINE, true);
+ variables.setVariable("ivy.pom.url", SKIP_LINE, true);
+
+ if (options.getLicenseHeader() != null) {
+ variables.setVariable("ivy.pom.license", options.getLicenseHeader(), true);
+ }
+ if (options.isPrintIvyInfo()) {
+ String header = "<!--\n" + " Apache Maven 2 POM generated by Apache Ivy\n"
+ + " " + Ivy.getIvyHomeURL() + "\n" + " Apache Ivy version: "
+ + Ivy.getIvyVersion() + " " + Ivy.getIvyDate() + "\n" + "-->";
+ variables.setVariable("ivy.pom.header", header, true);
+ }
+
+ setModuleVariables(md, variables, options);
+
+ boolean dependenciesPrinted = false;
+
+ int lastIndent = 0;
+ int indent = 0;
+ String line = in.readLine();
+ while (line != null) {
+ line = IvyPatternHelper.substituteVariables(line, variables);
+ if (line.indexOf(SKIP_LINE) != -1) {
+ // skip this line
+ line = in.readLine();
+ continue;
+ }
+
+ if (line.trim().length() == 0) {
+ // empty line
+ out.println(line);
+ line = in.readLine();
+ continue;
+ }
+
+ lastIndent = indent;
+ indent = line.indexOf('<');
+
+ if (!dependenciesPrinted && line.indexOf("</dependencies>") != -1) {
+ printDependencies(md, out, options, indent, false);
+ dependenciesPrinted = true;
+ }
+
+ if (!dependenciesPrinted && line.indexOf("</project>") != -1) {
+ printDependencies(md, out, options, lastIndent, true);
+ dependenciesPrinted = true;
+ }
+
+ out.println(line);
+ line = in.readLine();
+ }
+ } finally {
+ in.close();
+ out.close();
+ }
+ }
+
+ private static void setModuleVariables(ModuleDescriptor md, IvyVariableContainer variables,
+ PomWriterOptions options) {
+ ModuleRevisionId mrid = md.getModuleRevisionId();
+ variables.setVariable("ivy.pom.groupId", mrid.getOrganisation(), true);
+
+ String artifactId = options.getArtifactName();
+ if (artifactId == null) {
+ artifactId = mrid.getName();
+ }
+
+ String packaging = options.getArtifactPackaging();
+ if (packaging == null) {
+ // find artifact to determine the packaging
+ Artifact artifact = findArtifact(md, artifactId);
+ if (artifact == null) {
+ // no suitable artifact found, default to 'pom'
+ packaging = "pom";
+ } else {
+ packaging = artifact.getType();
+ }
+ }
+
+ variables.setVariable("ivy.pom.artifactId", artifactId, true);
+ variables.setVariable("ivy.pom.packaging", packaging, true);
+ if (mrid.getRevision() != null) {
+ variables.setVariable("ivy.pom.version", mrid.getRevision(), true);
+ }
+ if (options.getDescription() != null) {
+ variables.setVariable("ivy.pom.description", options.getDescription(), true);
+ }
+ if (md.getHomePage() != null) {
+ variables.setVariable("ivy.pom.url", md.getHomePage(), true);
+ }
+ }
+
+ /**
+ * Returns the first artifact with the correct name and without a classifier.
+ */
+ private static Artifact findArtifact(ModuleDescriptor md, String artifactName) {
+ Artifact[] artifacts = md.getAllArtifacts();
+ for (int i = 0; i < artifacts.length; i++) {
+ if (artifacts[i].getName().equals(artifactName)
+ && artifacts[i].getAttribute("classifier") == null) {
+ return artifacts[i];
+ }
+ }
+
+ return null;
+ }
+
+ private static void indent(PrintWriter out, int indent) {
+ for (int i = 0; i < indent; i++) {
+ out.print(' ');
+ }
+ }
+
+ private static void printDependencies(ModuleDescriptor md, PrintWriter out,
+ PomWriterOptions options, int indent, boolean printDependencies) {
+ List extraDeps = options.getExtraDependencies();
+ DependencyDescriptor[] dds = getDependencies(md, options);
+
+ if (!extraDeps.isEmpty() || (dds.length > 0)) {
+ if (printDependencies) {
+ indent(out, indent);
+ out.println("<dependencies>");
+ }
+
+ // print the extra dependencies first
+ for (Iterator it = extraDeps.iterator(); it.hasNext();) {
+ PomWriterOptions.ExtraDependency dep = (ExtraDependency) it.next();
+ String groupId = dep.getGroup();
+ if (groupId == null) {
+ groupId = md.getModuleRevisionId().getOrganisation();
+ }
+ String version = dep.getVersion();
+ if (version == null) {
+ version = md.getModuleRevisionId().getRevision();
+ }
+ printDependency(out, indent, groupId, dep.getArtifact(), version, dep.getType(),
+ dep.getClassifier(), dep.getScope(), dep.isOptional(), true, null);
+ }
+
+ // now print the dependencies listed in the ModuleDescriptor
+ ConfigurationScopeMapping mapping = options.getMapping();
+ if (mapping == null) {
+ mapping = DEFAULT_MAPPING;
+ }
+
+ for (int i = 0; i < dds.length; i++) {
+ ModuleRevisionId mrid = dds[i].getDependencyRevisionId();
+ ExcludeRule[] excludes = null;
+ if (dds[i].canExclude()) {
+ excludes = dds[i].getAllExcludeRules();
+ }
+ DependencyArtifactDescriptor[] dads = dds[i].getAllDependencyArtifacts();
+ if (dads.length > 0) {
+ for (int j = 0; j < dads.length; j++) {
+ String type = dads[j].getType();
+ String classifier = dads[j].getExtraAttribute("classifier");
+ String scope = mapping.getScope(dds[i].getModuleConfigurations());
+ boolean optional = mapping.isOptional(dds[i].getModuleConfigurations());
+ printDependency(out, indent, mrid.getOrganisation(), mrid.getName(),
+ mrid.getRevision(), type, classifier, scope, optional,
+ dds[i].isTransitive(), excludes);
+ }
+ } else {
+ String scope = mapping.getScope(dds[i].getModuleConfigurations());
+ boolean optional = mapping.isOptional(dds[i].getModuleConfigurations());
+ printDependency(out, indent, mrid.getOrganisation(), mrid.getName(),
+ mrid.getRevision(), null, null, scope, optional, dds[i].isTransitive(),
+ excludes);
+ }
+ }
+
+ if (printDependencies) {
+ indent(out, indent);
+ out.println("</dependencies>");
+ }
+ }
+ }
+
+ private static void printDependency(PrintWriter out, int indent, String groupId,
+ String artifactId, String version, String type, String classifier, String scope,
+ boolean isOptional, boolean isTransitive, ExcludeRule[] excludes) {
+ indent(out, indent * 2);
+ out.println("<dependency>");
+ indent(out, indent * 3);
+ out.println("<groupId>" + groupId + "</groupId>");
+ indent(out, indent * 3);
+ out.println("<artifactId>" + artifactId + "</artifactId>");
+ indent(out, indent * 3);
+ out.println("<version>" + version + "</version>");
+ if (type != null && !"jar".equals(type)) {
+ indent(out, indent * 3);
+ out.println("<type>" + type + "</type>");
+ }
+ if (classifier != null) {
+ indent(out, indent * 3);
+ out.println("<classifier>" + classifier + "</classifier>");
+ }
+ if (scope != null) {
+ indent(out, indent * 3);
+ out.println("<scope>" + scope + "</scope>");
+ }
+ if (isOptional) {
+ indent(out, indent * 3);
+ out.println("<optional>true</optional>");
+ }
+ if (!isTransitive) {
+ indent(out, indent * 3);
+ out.println("<exclusions>");
+ indent(out, indent * 4);
+ out.println("<exclusion>");
+ indent(out, indent * 5);
+ out.println("<groupId>*</groupId>");
+ indent(out, indent * 5);
+ out.println("<artifactId>*</artifactId>");
+ indent(out, indent * 4);
+ out.println("</exclusion>");
+ indent(out, indent * 3);
+ out.println("</exclusions>");
+ } else if (excludes != null) {
+ printExclusions(excludes, out, indent);
+ }
+ indent(out, indent * 2);
+ out.println("</dependency>");
+ }
+
+ private static void printExclusions(ExcludeRule[] exclusions, PrintWriter out, int indent) {
+ indent(out, indent * 3);
+ out.println("<exclusions>");
+
+ for (int i = 0; i < exclusions.length; i++) {
+ indent(out, indent * 4);
+ out.println("<exclusion>");
+ ExcludeRule rule = exclusions[i];
+ indent(out, indent * 5);
+ out.println("<groupId>" + rule.getId().getModuleId().getOrganisation() + "</groupId>");
+ indent(out, indent * 5);
+ out.println("<artifactId>" + rule.getId().getModuleId().getName() + "</artifactId>");
+ indent(out, indent * 4);
+ out.println("</exclusion>");
+ }
+
+ indent(out, indent * 3);
+ out.println("</exclusions>");
+ }
+
+ private static DependencyDescriptor[] getDependencies(ModuleDescriptor md,
+ PomWriterOptions options) {
+ String[] confs = ConfigurationUtils.replaceWildcards(options.getConfs(), md);
+
+ List result = new ArrayList();
+ DependencyDescriptor[] dds = md.getDependencies();
+ for (int i = 0; i < dds.length; i++) {
+ String[] depConfs = dds[i].getDependencyConfigurations(confs);
+ if ((depConfs != null) && (depConfs.length > 0)) {
+ result.add(dds[i]);
+ }
+ }
+
+ return (DependencyDescriptor[]) result.toArray(new DependencyDescriptor[result.size()]);
+ }
+
+ /**
+ * Wraps an {@link IvyVariableContainer} delegating most method calls to the wrapped instance,
+ * except for a set of variables which are only stored locally in the wrapper, and not
+ * propagated to the wrapped instance.
+ */
+ private static final class IvyVariableContainerWrapper implements IvyVariableContainer {
+
+ private final IvyVariableContainer variables;
+
+ private Map localVariables = new HashMap();
+
+ private IvyVariableContainerWrapper(IvyVariableContainer variables) {
+ this.variables = variables;
+ }
+
+ public void setVariable(String varName, String value, boolean overwrite) {
+ localVariables.put(varName, value);
+ }
+
+ public void setEnvironmentPrefix(String prefix) {
+ variables.setEnvironmentPrefix(prefix);
+ }
+
+ public String getVariable(String name) {
+ String result = variables.getVariable(name);
+ if (result == null) {
+ result = (String) localVariables.get(name);
+ }
+ return result;
+ }
+
+ public Object clone() {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+}
diff --git a/src/java/org/apache/ivy/plugins/parser/m2/PomReader.java b/src/java/org/apache/ivy/plugins/parser/m2/PomReader.java
new file mode 100644
index 0000000..081db57
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/parser/m2/PomReader.java
@@ -0,0 +1,629 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.parser.m2;
+
+import java.io.BufferedInputStream;
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.LineNumberReader;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.ivy.core.IvyPatternHelper;
+import org.apache.ivy.core.module.descriptor.License;
+import org.apache.ivy.core.module.id.ModuleId;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.plugins.repository.Resource;
+import org.apache.ivy.util.XMLHelper;
+import org.apache.ivy.util.url.URLHandlerRegistry;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
+/**
+ * Provides the method to read some data out of the DOM tree of a pom file.
+ */
+public class PomReader {
+
+ private static final String PACKAGING = "packaging";
+
+ private static final String DEPENDENCY = "dependency";
+
+ private static final String DEPENDENCIES = "dependencies";
+
+ private static final String DEPENDENCY_MGT = "dependencyManagement";
+
+ private static final String PROJECT = "project";
+
+ private static final String MODEL = "model";
+
+ private static final String GROUP_ID = "groupId";
+
+ private static final String ARTIFACT_ID = "artifactId";
+
+ private static final String VERSION = "version";
+
+ private static final String DESCRIPTION = "description";
+
+ private static final String HOMEPAGE = "url";
+
+ private static final String LICENSES = "licenses";
+
+ private static final String LICENSE = "license";
+
+ private static final String LICENSE_NAME = "name";
+
+ private static final String LICENSE_URL = "url";
+
+ private static final String PARENT = "parent";
+
+ private static final String SCOPE = "scope";
+
+ private static final String CLASSIFIER = "classifier";
+
+ private static final String OPTIONAL = "optional";
+
+ private static final String EXCLUSIONS = "exclusions";
+
+ private static final String EXCLUSION = "exclusion";
+
+ private static final String DISTRIBUTION_MGT = "distributionManagement";
+
+ private static final String RELOCATION = "relocation";
+
+ private static final String PROPERTIES = "properties";
+
+ private static final String PLUGINS = "plugins";
+
+ private static final String PLUGIN = "plugin";
+
+ private static final String TYPE = "type";
+
+ private HashMap properties = new HashMap();
+
+ private final Element projectElement;
+
+ private final Element parentElement;
+
+ public PomReader(URL descriptorURL, Resource res) throws IOException, SAXException {
+ InputStream stream = new AddDTDFilterInputStream(URLHandlerRegistry.getDefault()
+ .openStream(descriptorURL));
+ InputSource source = new InputSource(stream);
+ source.setSystemId(XMLHelper.toSystemId(descriptorURL));
+ try {
+ Document pomDomDoc = XMLHelper.parseToDom(source, new EntityResolver() {
+ public InputSource resolveEntity(String publicId, String systemId)
+ throws SAXException, IOException {
+ if ((systemId != null) && systemId.endsWith("m2-entities.ent")) {
+ return new InputSource(PomReader.class
+ .getResourceAsStream("m2-entities.ent"));
+ }
+ return null;
+ }
+ });
+ projectElement = pomDomDoc.getDocumentElement();
+ if (!PROJECT.equals(projectElement.getNodeName())
+ && !MODEL.equals(projectElement.getNodeName())) {
+ throw new SAXParseException("project must be the root tag", res.getName(),
+ res.getName(), 0, 0);
+ }
+ parentElement = getFirstChildElement(projectElement, PARENT);
+ } finally {
+ try {
+ stream.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ }
+
+ public boolean hasParent() {
+ return parentElement != null;
+ }
+
+ /**
+ * Add a property if not yet set and value is not null. This garantee that property keep the
+ * first value that is put on it and that the properties are never null.
+ */
+ public void setProperty(String prop, String val) {
+ if (!properties.containsKey(prop) && val != null) {
+ properties.put(prop, val);
+ }
+ }
+
+ public String getGroupId() {
+ String groupId = getFirstChildText(projectElement, GROUP_ID);
+ if (groupId == null) {
+ groupId = getFirstChildText(parentElement, GROUP_ID);
+ }
+ return replaceProps(groupId);
+
+ }
+
+ public String getParentGroupId() {
+ String groupId = getFirstChildText(parentElement, GROUP_ID);
+ if (groupId == null) {
+ groupId = getFirstChildText(projectElement, GROUP_ID);
+ }
+ return replaceProps(groupId);
+ }
+
+ public String getArtifactId() {
+ String val = getFirstChildText(projectElement, ARTIFACT_ID);
+ if (val == null) {
+ val = getFirstChildText(parentElement, ARTIFACT_ID);
+ }
+ return replaceProps(val);
+ }
+
+ public String getParentArtifactId() {
+ String val = getFirstChildText(parentElement, ARTIFACT_ID);
+ if (val == null) {
+ val = getFirstChildText(projectElement, ARTIFACT_ID);
+ }
+ return replaceProps(val);
+ }
+
+ public String getVersion() {
+ String val = getFirstChildText(projectElement, VERSION);
+ if (val == null) {
+ val = getFirstChildText(parentElement, VERSION);
+ }
+ return replaceProps(val);
+ }
+
+ public String getParentVersion() {
+ String val = getFirstChildText(parentElement, VERSION);
+ if (val == null) {
+ val = getFirstChildText(projectElement, VERSION);
+ }
+ return replaceProps(val);
+ }
+
+ public String getPackaging() {
+ String val = getFirstChildText(projectElement, PACKAGING);
+ if (val == null) {
+ val = "jar";
+ }
+ return val;
+ }
+
+ public String getHomePage() {
+ String val = getFirstChildText(projectElement, HOMEPAGE);
+ if (val == null) {
+ val = "";
+ }
+ return val;
+ }
+
+ public String getDescription() {
+ String val = getFirstChildText(projectElement, DESCRIPTION);
+ if (val == null) {
+ val = "";
+ }
+ return val.trim();
+ }
+
+ public License[] getLicenses() {
+ Element licenses = getFirstChildElement(projectElement, LICENSES);
+ if (licenses == null) {
+ return new License[0];
+ }
+ licenses.normalize();
+ List/* <License> */lics = new ArrayList();
+ for (Iterator it = getAllChilds(licenses).iterator(); it.hasNext();) {
+ Element license = (Element) it.next();
+ if (LICENSE.equals(license.getNodeName())) {
+ String name = getFirstChildText(license, LICENSE_NAME);
+ String url = getFirstChildText(license, LICENSE_URL);
+
+ if ((name == null) && (url == null)) {
+ // move to next license
+ continue;
+ }
+
+ if (name == null) {
+ // The license name is required in Ivy but not in a POM!
+ name = "Unknown License";
+ }
+
+ lics.add(new License(name, url));
+ }
+ }
+ return (License[]) lics.toArray(new License[lics.size()]);
+ }
+
+ public ModuleRevisionId getRelocation() {
+ Element distrMgt = getFirstChildElement(projectElement, DISTRIBUTION_MGT);
+ Element relocation = getFirstChildElement(distrMgt, RELOCATION);
+ if (relocation == null) {
+ return null;
+ } else {
+ String relocGroupId = getFirstChildText(relocation, GROUP_ID);
+ String relocArtId = getFirstChildText(relocation, ARTIFACT_ID);
+ String relocVersion = getFirstChildText(relocation, VERSION);
+ relocGroupId = relocGroupId == null ? getGroupId() : relocGroupId;
+ relocArtId = relocArtId == null ? getArtifactId() : relocArtId;
+ relocVersion = relocVersion == null ? getVersion() : relocVersion;
+ return ModuleRevisionId.newInstance(relocGroupId, relocArtId, relocVersion);
+ }
+ }
+
+ public List /* <PomDependencyData> */getDependencies() {
+ Element dependenciesElement = getFirstChildElement(projectElement, DEPENDENCIES);
+ LinkedList dependencies = new LinkedList();
+ if (dependenciesElement != null) {
+ NodeList childs = dependenciesElement.getChildNodes();
+ for (int i = 0; i < childs.getLength(); i++) {
+ Node node = childs.item(i);
+ if (node instanceof Element && DEPENDENCY.equals(node.getNodeName())) {
+ dependencies.add(new PomDependencyData((Element) node));
+ }
+ }
+ }
+ return dependencies;
+ }
+
+ public List /* <PomDependencyMgt> */getDependencyMgt() {
+ Element dependenciesElement = getFirstChildElement(projectElement, DEPENDENCY_MGT);
+ dependenciesElement = getFirstChildElement(dependenciesElement, DEPENDENCIES);
+ LinkedList dependencies = new LinkedList();
+ if (dependenciesElement != null) {
+ NodeList childs = dependenciesElement.getChildNodes();
+ for (int i = 0; i < childs.getLength(); i++) {
+ Node node = childs.item(i);
+ if (node instanceof Element && DEPENDENCY.equals(node.getNodeName())) {
+ dependencies.add(new PomDependencyMgtElement((Element) node));
+ }
+ }
+ }
+ return dependencies;
+ }
+
+ public class PomDependencyMgtElement implements PomDependencyMgt {
+ private final Element depElement;
+
+ public PomDependencyMgtElement(PomDependencyMgtElement copyFrom) {
+ this(copyFrom.depElement);
+ }
+
+ PomDependencyMgtElement(Element depElement) {
+ this.depElement = depElement;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.ivy.plugins.parser.m2.PomDependencyMgt#getGroupId()
+ */
+ public String getGroupId() {
+ String val = getFirstChildText(depElement, GROUP_ID);
+ return replaceProps(val);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.ivy.plugins.parser.m2.PomDependencyMgt#getArtifaceId()
+ */
+ public String getArtifactId() {
+ String val = getFirstChildText(depElement, ARTIFACT_ID);
+ return replaceProps(val);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.ivy.plugins.parser.m2.PomDependencyMgt#getVersion()
+ */
+ public String getVersion() {
+ String val = getFirstChildText(depElement, VERSION);
+ return replaceProps(val);
+ }
+
+ public String getScope() {
+ String val = getFirstChildText(depElement, SCOPE);
+ return replaceProps(val);
+ }
+
+ public List /* <ModuleId> */getExcludedModules() {
+ Element exclusionsElement = getFirstChildElement(depElement, EXCLUSIONS);
+ LinkedList exclusions = new LinkedList();
+ if (exclusionsElement != null) {
+ NodeList childs = exclusionsElement.getChildNodes();
+ for (int i = 0; i < childs.getLength(); i++) {
+ Node node = childs.item(i);
+ if (node instanceof Element && EXCLUSION.equals(node.getNodeName())) {
+ String groupId = getFirstChildText((Element) node, GROUP_ID);
+ String artifactId = getFirstChildText((Element) node, ARTIFACT_ID);
+ if ((groupId != null) && (artifactId != null)) {
+ exclusions.add(ModuleId.newInstance(groupId, artifactId));
+ }
+ }
+ }
+ }
+ return exclusions;
+ }
+ }
+
+ public List /* <PomPluginElement> */getPlugins() {
+ LinkedList plugins = new LinkedList();
+
+ Element buildElement = getFirstChildElement(projectElement, "build");
+ if (buildElement == null) {
+ return plugins;
+ }
+
+ Element pluginsElement = getFirstChildElement(buildElement, PLUGINS);
+ if (pluginsElement != null) {
+ NodeList childs = pluginsElement.getChildNodes();
+ for (int i = 0; i < childs.getLength(); i++) {
+ Node node = childs.item(i);
+ if (node instanceof Element && PLUGIN.equals(node.getNodeName())) {
+ plugins.add(new PomPluginElement((Element) node));
+ }
+ }
+ }
+ return plugins;
+ }
+
+ public class PomPluginElement implements PomDependencyMgt {
+ private Element pluginElement;
+
+ PomPluginElement(Element pluginElement) {
+ this.pluginElement = pluginElement;
+ }
+
+ public String getGroupId() {
+ String val = getFirstChildText(pluginElement, GROUP_ID);
+ return replaceProps(val);
+ }
+
+ public String getArtifactId() {
+ String val = getFirstChildText(pluginElement, ARTIFACT_ID);
+ return replaceProps(val);
+ }
+
+ public String getVersion() {
+ String val = getFirstChildText(pluginElement, VERSION);
+ return replaceProps(val);
+ }
+
+ public String getScope() {
+ return null; // not used
+ }
+
+ public List /* <ModuleId> */getExcludedModules() {
+ return Collections.EMPTY_LIST; // probably not used?
+ }
+ }
+
+ public class PomDependencyData extends PomDependencyMgtElement {
+ private final Element depElement;
+
+ public PomDependencyData(PomDependencyData copyFrom) {
+ this(copyFrom.depElement);
+ }
+
+ PomDependencyData(Element depElement) {
+ super(depElement);
+ this.depElement = depElement;
+ }
+
+ public String getScope() {
+ String val = getFirstChildText(depElement, SCOPE);
+ return replaceProps(val);
+ }
+
+ public String getClassifier() {
+ String val = getFirstChildText(depElement, CLASSIFIER);
+ return replaceProps(val);
+ }
+
+ public String getType() {
+ String val = getFirstChildText(depElement, TYPE);
+ return replaceProps(val);
+ }
+
+ public boolean isOptional() {
+ Element e = getFirstChildElement(depElement, OPTIONAL);
+ return (e != null) && "true".equalsIgnoreCase(getTextContent(e));
+ }
+
+ }
+
+ /**
+ * @return the content of the properties tag into the pom.
+ */
+ public Map/* <String,String> */getPomProperties() {
+ Map pomProperties = new HashMap();
+ Element propsEl = getFirstChildElement(projectElement, PROPERTIES);
+ if (propsEl != null) {
+ propsEl.normalize();
+ }
+ for (Iterator it = getAllChilds(propsEl).iterator(); it.hasNext();) {
+ Element prop = (Element) it.next();
+ pomProperties.put(prop.getNodeName(), getTextContent(prop));
+ }
+ return pomProperties;
+ }
+
+ private String replaceProps(String val) {
+ if (val == null) {
+ return null;
+ } else {
+ return IvyPatternHelper.substituteVariables(val, properties).trim();
+ }
+ }
+
+ private static String getTextContent(Element element) {
+ StringBuffer result = new StringBuffer();
+
+ NodeList childNodes = element.getChildNodes();
+ for (int i = 0; i < childNodes.getLength(); i++) {
+ Node child = childNodes.item(i);
+
+ switch (child.getNodeType()) {
+ case Node.CDATA_SECTION_NODE:
+ case Node.TEXT_NODE:
+ result.append(child.getNodeValue());
+ break;
+ default:
+ break;
+ }
+ }
+
+ return result.toString();
+ }
+
+ private static String getFirstChildText(Element parentElem, String name) {
+ Element node = getFirstChildElement(parentElem, name);
+ if (node != null) {
+ return getTextContent(node);
+ } else {
+ return null;
+ }
+ }
+
+ private static Element getFirstChildElement(Element parentElem, String name) {
+ if (parentElem == null) {
+ return null;
+ }
+ NodeList childs = parentElem.getChildNodes();
+ for (int i = 0; i < childs.getLength(); i++) {
+ Node node = childs.item(i);
+ if (node instanceof Element && name.equals(node.getNodeName())) {
+ return (Element) node;
+ }
+ }
+ return null;
+ }
+
+ private static List/* <Element> */getAllChilds(Element parent) {
+ List r = new LinkedList();
+ if (parent != null) {
+ NodeList childs = parent.getChildNodes();
+ for (int i = 0; i < childs.getLength(); i++) {
+ Node node = childs.item(i);
+ if (node instanceof Element) {
+ r.add(node);
+ }
+ }
+ }
+ return r;
+ }
+
+ private static final class AddDTDFilterInputStream extends FilterInputStream {
+ private static final int MARK = 10000;
+
+ private static final String DOCTYPE = "<!DOCTYPE project SYSTEM \"m2-entities.ent\">\n";
+
+ private int count;
+
+ private byte[] prefix = DOCTYPE.getBytes();
+
+ private AddDTDFilterInputStream(InputStream in) throws IOException {
+ super(new BufferedInputStream(in));
+
+ this.in.mark(MARK);
+
+ // TODO: we should really find a better solution for this...
+ // maybe we could use a FilterReader instead of a FilterInputStream?
+ int byte1 = this.in.read();
+ int byte2 = this.in.read();
+ int byte3 = this.in.read();
+
+ if (byte1 == 239 && byte2 == 187 && byte3 == 191) {
+ // skip the UTF-8 BOM
+ this.in.mark(MARK);
+ } else {
+ this.in.reset();
+ }
+
+ int bytesToSkip = 0;
+ LineNumberReader reader = new LineNumberReader(new InputStreamReader(this.in, "UTF-8"),
+ 100);
+ String firstLine = reader.readLine();
+ if (firstLine != null) {
+ String trimmed = firstLine.trim();
+ if (trimmed.startsWith("<?xml ")) {
+ int endIndex = trimmed.indexOf("?>");
+ String xmlDecl = trimmed.substring(0, endIndex + 2);
+ prefix = (xmlDecl + "\n" + DOCTYPE).getBytes();
+ bytesToSkip = xmlDecl.getBytes().length;
+ }
+ } else {
+ prefix = new byte[0];
+ }
+
+ this.in.reset();
+ for (int i = 0; i < bytesToSkip; i++) {
+ this.in.read();
+ }
+ }
+
+ public int read() throws IOException {
+ if (count < prefix.length) {
+ return prefix[count++];
+ }
+
+ int result = super.read();
+ return result;
+ }
+
+ public int read(byte[] b, int off, int len) throws IOException {
+ if (b == null) {
+ throw new NullPointerException();
+ } else if ((off < 0) || (off > b.length) || (len < 0) || ((off + len) > b.length)
+ || ((off + len) < 0)) {
+ throw new IndexOutOfBoundsException();
+ } else if (len == 0) {
+ return 0;
+ }
+
+ int nbrBytesCopied = 0;
+
+ if (count < prefix.length) {
+ int nbrBytesFromPrefix = Math.min(prefix.length - count, len);
+ System.arraycopy(prefix, count, b, off, nbrBytesFromPrefix);
+ nbrBytesCopied = nbrBytesFromPrefix;
+ }
+
+ if (nbrBytesCopied < len) {
+ nbrBytesCopied += in.read(b, off + nbrBytesCopied, len - nbrBytesCopied);
+ }
+
+ count += nbrBytesCopied;
+ return nbrBytesCopied;
+ }
+ }
+
+}
diff --git a/src/java/org/apache/ivy/plugins/parser/m2/PomWriterOptions.java b/src/java/org/apache/ivy/plugins/parser/m2/PomWriterOptions.java
new file mode 100644
index 0000000..544d932
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/parser/m2/PomWriterOptions.java
@@ -0,0 +1,214 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.parser.m2;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class PomWriterOptions {
+
+ private String[] confs;
+
+ private String licenseHeader;
+
+ private ConfigurationScopeMapping mapping;
+
+ private boolean printIvyInfo = true;
+
+ private String artifactName;
+
+ private String artifactPackaging;
+
+ private List/* <ExtraDependency> */extraDependencies = new ArrayList();
+
+ private String description;
+
+ private File template;
+
+ public File getTemplate() {
+ return template;
+ }
+
+ public PomWriterOptions setTemplate(File template) {
+ this.template = template;
+ return this;
+ }
+
+ public String[] getConfs() {
+ return confs;
+ }
+
+ public PomWriterOptions setConfs(String[] confs) {
+ this.confs = confs;
+ return this;
+ }
+
+ public String getLicenseHeader() {
+ return licenseHeader;
+ }
+
+ public PomWriterOptions setLicenseHeader(String licenseHeader) {
+ this.licenseHeader = licenseHeader;
+ if (this.licenseHeader != null) {
+ this.licenseHeader = this.licenseHeader.trim();
+ }
+ return this;
+ }
+
+ public ConfigurationScopeMapping getMapping() {
+ return mapping;
+ }
+
+ public PomWriterOptions setMapping(ConfigurationScopeMapping mapping) {
+ this.mapping = mapping;
+ return this;
+ }
+
+ public boolean isPrintIvyInfo() {
+ return printIvyInfo;
+ }
+
+ public PomWriterOptions setPrintIvyInfo(boolean printIvyInfo) {
+ this.printIvyInfo = printIvyInfo;
+ return this;
+ }
+
+ public List/* <ExtraDependency> */getExtraDependencies() {
+ return extraDependencies;
+ }
+
+ public PomWriterOptions setExtraDependencies(List/* <ExtraDependency> */extraDependencies) {
+ this.extraDependencies = extraDependencies;
+ return this;
+ }
+
+ public String getArtifactName() {
+ return artifactName;
+ }
+
+ public PomWriterOptions setArtifactName(String artifactName) {
+ this.artifactName = artifactName;
+ return this;
+ }
+
+ public String getArtifactPackaging() {
+ return artifactPackaging;
+ }
+
+ public PomWriterOptions setArtifactPackaging(String artifactPackaging) {
+ this.artifactPackaging = artifactPackaging;
+ return this;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public PomWriterOptions setDescription(String description) {
+ this.description = description;
+ return this;
+ }
+
+ public static class ConfigurationScopeMapping {
+ private Map/* <String,String> */scopes;
+
+ public ConfigurationScopeMapping(Map/* <String,String> */scopesMapping) {
+ this.scopes = new HashMap(scopesMapping);
+ }
+
+ /**
+ * Returns the scope mapped to the given configuration array.
+ *
+ * @param confs
+ * the configurations for which the scope should be returned
+ * @return the scope to which the conf is mapped
+ */
+ public String getScope(String[] confs) {
+ for (int i = 0; i < confs.length; i++) {
+ if (scopes.containsKey(confs[i])) {
+ return (String) scopes.get(confs[i]);
+ }
+ }
+
+ return null;
+ }
+
+ public boolean isOptional(String[] confs) {
+ return getScope(confs) == null;
+ }
+ }
+
+ public static class ExtraDependency {
+ private String group;
+
+ private String artifact;
+
+ private String version;
+
+ private String scope;
+
+ private String type;
+
+ private String classifier;
+
+ private boolean optional;
+
+ public ExtraDependency(String group, String artifact, String version, String scope,
+ String type, String classifier, boolean optional) {
+ this.group = group;
+ this.artifact = artifact;
+ this.version = version;
+ this.scope = scope;
+ this.type = type;
+ this.classifier = classifier;
+ this.optional = optional;
+ }
+
+ public String getGroup() {
+ return group;
+ }
+
+ public String getArtifact() {
+ return artifact;
+ }
+
+ public String getVersion() {
+ return version;
+ }
+
+ public String getScope() {
+ return scope;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public String getClassifier() {
+ return classifier;
+ }
+
+ public boolean isOptional() {
+ return optional;
+ }
+ }
+
+}
diff --git a/src/java/org/apache/ivy/plugins/parser/m2/m2-entities.ent b/src/java/org/apache/ivy/plugins/parser/m2/m2-entities.ent
new file mode 100644
index 0000000..ae1ba09
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/parser/m2/m2-entities.ent
@@ -0,0 +1,114 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<!ENTITY nbsp " ">
+<!ENTITY iexcl "¡">
+<!ENTITY cent "¢">
+<!ENTITY pound "£">
+<!ENTITY curren "¤">
+<!ENTITY yen "¥">
+<!ENTITY brvbar "¦">
+<!ENTITY sect "§">
+<!ENTITY uml "¨">
+<!ENTITY copy "©">
+<!ENTITY ordf "ª">
+<!ENTITY laquo "«">
+<!ENTITY not "¬">
+<!ENTITY shy "">
+<!ENTITY reg "®">
+<!ENTITY macr "¯">
+<!ENTITY deg "°">
+<!ENTITY plusmn "±">
+<!ENTITY sup2 "²">
+<!ENTITY sup3 "³">
+<!ENTITY acute "´">
+<!ENTITY micro "µ">
+<!ENTITY para "¶">
+<!ENTITY middot "·">
+<!ENTITY cedil "¸">
+<!ENTITY sup1 "¹">
+<!ENTITY ordm "º">
+<!ENTITY raquo "»">
+<!ENTITY frac14 "¼">
+<!ENTITY frac12 "½">
+<!ENTITY frac34 "¾">
+<!ENTITY iquest "¿">
+<!ENTITY Agrave "À">
+<!ENTITY Aacute "Á">
+<!ENTITY Acirc "Â">
+<!ENTITY Atilde "Ã">
+<!ENTITY Auml "Ä">
+<!ENTITY Aring "Å">
+<!ENTITY AElig "Æ">
+<!ENTITY Ccedil "Ç">
+<!ENTITY Egrave "È">
+<!ENTITY Eacute "É">
+<!ENTITY Ecirc "Ê">
+<!ENTITY Euml "Ë">
+<!ENTITY Igrave "Ì">
+<!ENTITY Iacute "Í">
+<!ENTITY Icirc "Î">
+<!ENTITY Iuml "Ï">
+<!ENTITY ETH "Ð">
+<!ENTITY Ntilde "Ñ">
+<!ENTITY Ograve "Ò">
+<!ENTITY Oacute "Ó">
+<!ENTITY Ocirc "Ô">
+<!ENTITY Otilde "Õ">
+<!ENTITY Ouml "Ö">
+<!ENTITY times "×">
+<!ENTITY Oslash "Ø">
+<!ENTITY Ugrave "Ù">
+<!ENTITY Uacute "Ú">
+<!ENTITY Ucirc "Û">
+<!ENTITY Uuml "Ü">
+<!ENTITY Yacute "Ý">
+<!ENTITY THORN "Þ">
+<!ENTITY szlig "ß">
+<!ENTITY agrave "à">
+<!ENTITY aacute "á">
+<!ENTITY acirc "â">
+<!ENTITY atilde "ã">
+<!ENTITY auml "ä">
+<!ENTITY aring "å">
+<!ENTITY aelig "æ">
+<!ENTITY ccedil "ç">
+<!ENTITY egrave "è">
+<!ENTITY eacute "é">
+<!ENTITY ecirc "ê">
+<!ENTITY euml "ë">
+<!ENTITY igrave "ì">
+<!ENTITY iacute "í">
+<!ENTITY icirc "î">
+<!ENTITY iuml "ï">
+<!ENTITY eth "ð">
+<!ENTITY ntilde "ñ">
+<!ENTITY ograve "ò">
+<!ENTITY oacute "ó">
+<!ENTITY ocirc "ô">
+<!ENTITY otilde "õ">
+<!ENTITY ouml "ö">
+<!ENTITY divide "÷">
+<!ENTITY oslash "ø">
+<!ENTITY ugrave "ù">
+<!ENTITY uacute "ú">
+<!ENTITY ucirc "û">
+<!ENTITY uuml "ü">
+<!ENTITY yacute "ý">
+<!ENTITY thorn "þ">
+<!ENTITY yuml "ÿ">
diff --git a/src/java/org/apache/ivy/plugins/parser/m2/pom.template b/src/java/org/apache/ivy/plugins/parser/m2/pom.template
new file mode 100644
index 0000000..d0d4bd9
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/parser/m2/pom.template
@@ -0,0 +1,33 @@
+SKIP_LINE ***************************************************************
+SKIP_LINE * Licensed to the Apache Software Foundation (ASF) under one
+SKIP_LINE * or more contributor license agreements. See the NOTICE file
+SKIP_LINE * distributed with this work for additional information
+SKIP_LINE * regarding copyright ownership. The ASF licenses this file
+SKIP_LINE * to you under the Apache License, Version 2.0 (the
+SKIP_LINE * "License"); you may not use this file except in compliance
+SKIP_LINE * with the License. You may obtain a copy of the License at
+SKIP_LINE *
+SKIP_LINE * http://www.apache.org/licenses/LICENSE-2.0
+SKIP_LINE *
+SKIP_LINE * Unless required by applicable law or agreed to in writing,
+SKIP_LINE * software distributed under the License is distributed on an
+SKIP_LINE * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+SKIP_LINE * KIND, either express or implied. See the License for the
+SKIP_LINE * specific language governing permissions and limitations
+SKIP_LINE * under the License.
+SKIP_LINE ***************************************************************
+<?xml version="1.0" encoding="UTF-8"?>
+${ivy.pom.license}
+${ivy.pom.header}
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>${ivy.pom.groupId}</groupId>
+ <artifactId>${ivy.pom.artifactId}</artifactId>
+ <packaging>${ivy.pom.packaging}</packaging>
+ <version>${ivy.pom.version}</version>
+ <name>${ivy.pom.name}</name>
+ <description>${ivy.pom.description}</description>
+ <url>${ivy.pom.url}</url>
+</project>
diff --git a/src/java/org/apache/ivy/plugins/parser/xml/UpdateOptions.java b/src/java/org/apache/ivy/plugins/parser/xml/UpdateOptions.java
new file mode 100644
index 0000000..994b21e
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/parser/xml/UpdateOptions.java
@@ -0,0 +1,222 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.parser.xml;
+
+import java.util.Collections;
+import java.util.Date;
+import java.util.Map;
+
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.plugins.namespace.Namespace;
+import org.apache.ivy.plugins.parser.ParserSettings;
+
+public class UpdateOptions {
+ /**
+ * Settings to use for update, may be <code>null</code>.
+ */
+ private ParserSettings settings = null;
+
+ /**
+ * Namespace in which the module to update is, may be <code>null</code>.
+ */
+ private Namespace namespace = null;
+
+ /**
+ * Map from ModuleId of dependencies to new revision (as String)
+ */
+ private Map resolvedRevisions = Collections.EMPTY_MAP;
+
+ /**
+ * Map from ModuleId of dependencies to new branch (as String)
+ */
+ private Map resolvedBranches = Collections.EMPTY_MAP;
+
+ /**
+ * the new status, <code>null</code> to keep the old one
+ */
+ private String status = null;
+
+ /**
+ * the new revision, <code>null</code> to keep the old one
+ */
+ private String revision = null;
+
+ /**
+ * the new publication date, <code>null</code> to keep the old one
+ */
+ private Date pubdate = null;
+
+ /**
+ * Should included information be replaced
+ */
+ private boolean replaceInclude = true;
+
+ /**
+ * Should parent descriptor be merged inline
+ */
+ private boolean merge = true;
+
+ private ModuleDescriptor mergedDescriptor = null;
+
+ /**
+ * Configurations to exclude during update, or <code>null</code> to keep all confs.
+ */
+ private String[] confsToExclude = null;
+
+ /**
+ * True to set branch information on dependencies to default branch when omitted, false to keep
+ * it as is.
+ */
+ private boolean updateBranch = true;
+
+ private String branch;
+
+ /**
+ * True to indicate that the revConstraint attribute should be generated if applicable, false to
+ * never generate the revConstraint attribute.
+ */
+ private boolean generateRevConstraint = true;
+
+ public ParserSettings getSettings() {
+ return settings;
+ }
+
+ public UpdateOptions setSettings(ParserSettings settings) {
+ this.settings = settings;
+ return this;
+ }
+
+ public Namespace getNamespace() {
+ return namespace;
+ }
+
+ public UpdateOptions setNamespace(Namespace ns) {
+ this.namespace = ns;
+ return this;
+ }
+
+ public Map getResolvedRevisions() {
+ return resolvedRevisions;
+ }
+
+ public UpdateOptions setResolvedRevisions(Map resolvedRevisions) {
+ this.resolvedRevisions = resolvedRevisions;
+ return this;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public UpdateOptions setStatus(String status) {
+ this.status = status;
+ return this;
+ }
+
+ public String getRevision() {
+ return revision;
+ }
+
+ public UpdateOptions setRevision(String revision) {
+ this.revision = revision;
+ return this;
+ }
+
+ public Date getPubdate() {
+ return pubdate;
+ }
+
+ public UpdateOptions setPubdate(Date pubdate) {
+ this.pubdate = pubdate;
+ return this;
+ }
+
+ public boolean isReplaceInclude() {
+ return replaceInclude;
+ }
+
+ public UpdateOptions setReplaceInclude(boolean replaceInclude) {
+ this.replaceInclude = replaceInclude;
+ return this;
+ }
+
+ public boolean isMerge() {
+ // only return true if merge is set to true and if there is something to merge!
+ return merge && (mergedDescriptor != null)
+ && (mergedDescriptor.getInheritedDescriptors().length > 0);
+ }
+
+ public UpdateOptions setMerge(boolean merge) {
+ this.merge = merge;
+ return this;
+ }
+
+ public ModuleDescriptor getMergedDescriptor() {
+ return mergedDescriptor;
+ }
+
+ public UpdateOptions setMergedDescriptor(ModuleDescriptor mergedDescriptor) {
+ this.mergedDescriptor = mergedDescriptor;
+ return this;
+ }
+
+ public String[] getConfsToExclude() {
+ return confsToExclude;
+ }
+
+ public UpdateOptions setConfsToExclude(String[] confsToExclude) {
+ this.confsToExclude = confsToExclude;
+ return this;
+ }
+
+ public boolean isUpdateBranch() {
+ return updateBranch;
+ }
+
+ public UpdateOptions setUpdateBranch(boolean updateBranch) {
+ this.updateBranch = updateBranch;
+ return this;
+ }
+
+ public String getBranch() {
+ return branch;
+ }
+
+ public UpdateOptions setBranch(String pubBranch) {
+ this.branch = pubBranch;
+ return this;
+ }
+
+ public boolean isGenerateRevConstraint() {
+ return generateRevConstraint;
+ }
+
+ public UpdateOptions setGenerateRevConstraint(boolean generateRevConstraint) {
+ this.generateRevConstraint = generateRevConstraint;
+ return this;
+ }
+
+ public Map getResolvedBranches() {
+ return resolvedBranches;
+ }
+
+ public UpdateOptions setResolvedBranches(Map resolvedBranches) {
+ this.resolvedBranches = resolvedBranches;
+ return this;
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/parser/xml/XmlModuleDescriptorParser.java b/src/java/org/apache/ivy/plugins/parser/xml/XmlModuleDescriptorParser.java
new file mode 100644
index 0000000..de28d74
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/parser/xml/XmlModuleDescriptorParser.java
@@ -0,0 +1,1387 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.parser.xml;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.text.ParseException;
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Stack;
+
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.apache.ivy.Ivy;
+import org.apache.ivy.core.IvyContext;
+import org.apache.ivy.core.module.descriptor.Configuration;
+import org.apache.ivy.core.module.descriptor.ConfigurationAware;
+import org.apache.ivy.core.module.descriptor.DefaultArtifact;
+import org.apache.ivy.core.module.descriptor.DefaultDependencyArtifactDescriptor;
+import org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.DefaultExcludeRule;
+import org.apache.ivy.core.module.descriptor.DefaultExtendsDescriptor;
+import org.apache.ivy.core.module.descriptor.DefaultIncludeRule;
+import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
+import org.apache.ivy.core.module.descriptor.DependencyArtifactDescriptor;
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.ExcludeRule;
+import org.apache.ivy.core.module.descriptor.ExtraInfoHolder;
+import org.apache.ivy.core.module.descriptor.IncludeRule;
+import org.apache.ivy.core.module.descriptor.License;
+import org.apache.ivy.core.module.descriptor.MDArtifact;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.descriptor.OverrideDependencyDescriptorMediator;
+import org.apache.ivy.core.module.id.ArtifactId;
+import org.apache.ivy.core.module.id.ModuleId;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.resolve.ResolveData;
+import org.apache.ivy.core.resolve.ResolveEngine;
+import org.apache.ivy.core.resolve.ResolveOptions;
+import org.apache.ivy.core.resolve.ResolvedModuleRevision;
+import org.apache.ivy.plugins.conflict.ConflictManager;
+import org.apache.ivy.plugins.conflict.FixedConflictManager;
+import org.apache.ivy.plugins.matcher.PatternMatcher;
+import org.apache.ivy.plugins.namespace.NameSpaceHelper;
+import org.apache.ivy.plugins.namespace.Namespace;
+import org.apache.ivy.plugins.parser.AbstractModuleDescriptorParser;
+import org.apache.ivy.plugins.parser.ModuleDescriptorParser;
+import org.apache.ivy.plugins.parser.ModuleDescriptorParserRegistry;
+import org.apache.ivy.plugins.parser.ParserSettings;
+import org.apache.ivy.plugins.repository.Resource;
+import org.apache.ivy.plugins.repository.file.FileResource;
+import org.apache.ivy.plugins.repository.url.URLResource;
+import org.apache.ivy.plugins.resolver.DependencyResolver;
+import org.apache.ivy.util.DateUtil;
+import org.apache.ivy.util.FileUtil;
+import org.apache.ivy.util.Message;
+import org.apache.ivy.util.XMLHelper;
+import org.apache.ivy.util.extendable.ExtendableItemHelper;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+
+/**
+ * Parses an xml ivy file and output a ModuleDescriptor. For dependency and performance reasons, it
+ * uses only the SAX API, which makes the parsing code harder to understand.
+ */
+public class XmlModuleDescriptorParser extends AbstractModuleDescriptorParser {
+ static final String[] DEPENDENCY_REGULAR_ATTRIBUTES = new String[] {"org", "name", "branch",
+ "branchConstraint", "rev", "revConstraint", "force", "transitive", "changing", "conf"};
+
+ private static final XmlModuleDescriptorParser INSTANCE = new XmlModuleDescriptorParser();
+
+ public static XmlModuleDescriptorParser getInstance() {
+ return INSTANCE;
+ }
+
+ protected XmlModuleDescriptorParser() {
+ }
+
+ /**
+ * @param settings
+ * @param xmlURL
+ * the url pointing to the file to parse
+ * @param res
+ * the real resource to parse, used for log only
+ * @param validate
+ * @return
+ * @throws ParseException
+ * @throws IOException
+ */
+ public ModuleDescriptor parseDescriptor(ParserSettings ivySettings, URL xmlURL, Resource res,
+ boolean validate) throws ParseException, IOException {
+ Parser parser = newParser(ivySettings);
+ parser.setValidate(validate);
+ parser.setResource(res);
+ parser.setInput(xmlURL);
+ parser.parse();
+ return parser.getModuleDescriptor();
+ }
+
+ /** Used for test purpose */
+ ModuleDescriptor parseDescriptor(ParserSettings ivySettings, InputStream descriptor,
+ Resource res, boolean validate) throws ParseException, IOException {
+ Parser parser = newParser(ivySettings);
+ parser.setValidate(validate);
+ parser.setResource(res);
+ parser.setInput(descriptor);
+ parser.parse();
+ return parser.getModuleDescriptor();
+ }
+
+ /**
+ * Instantiates a Parser instance responsible for actual parsing of Ivy files.
+ * <p>
+ * Override this method if you want to use a custom Parser.
+ * </p>
+ *
+ * @param ivySettings
+ * the settings to use during parsing
+ * @return the Parser instance used for parsing Ivy files
+ */
+ protected Parser newParser(ParserSettings ivySettings) {
+ return new Parser(this, ivySettings);
+ }
+
+ public boolean accept(Resource res) {
+ return true; // this the default parser, it thus accepts all resources
+ }
+
+ public void toIvyFile(InputStream is, Resource res, File destFile, ModuleDescriptor md)
+ throws IOException, ParseException {
+ try {
+ Namespace ns = null;
+ if (md instanceof DefaultModuleDescriptor) {
+ DefaultModuleDescriptor dmd = (DefaultModuleDescriptor) md;
+ ns = dmd.getNamespace();
+ }
+ XmlModuleDescriptorUpdater.update(
+ is,
+ res,
+ destFile,
+ new UpdateOptions().setSettings(IvyContext.getContext().getSettings())
+ .setStatus(md.getStatus())
+ .setRevision(md.getResolvedModuleRevisionId().getRevision())
+ .setPubdate(md.getResolvedPublicationDate()).setUpdateBranch(false)
+ .setNamespace(ns));
+ } catch (SAXException e) {
+ ParseException ex = new ParseException("exception occurred while parsing " + res, 0);
+ ex.initCause(e);
+ throw ex;
+ } finally {
+ if (is != null) {
+ is.close();
+ }
+ }
+ }
+
+ public static class Parser extends AbstractParser {
+ public static final class State {
+ public static final int NONE = 0;
+
+ public static final int INFO = 1;
+
+ public static final int CONF = 2;
+
+ public static final int PUB = 3;
+
+ public static final int DEP = 4;
+
+ public static final int DEP_ARTIFACT = 5;
+
+ public static final int ARTIFACT_INCLUDE = 6;
+
+ public static final int ARTIFACT_EXCLUDE = 7;
+
+ public static final int CONFLICT = 8;
+
+ public static final int EXCLUDE = 9;
+
+ public static final int DEPS = 10;
+
+ public static final int DESCRIPTION = 11;
+
+ public static final int EXTRA_INFO = 12;
+
+ private State() {
+ }
+ }
+
+ protected static final List ALLOWED_VERSIONS = Arrays.asList(new String[] {"1.0", "1.1",
+ "1.2", "1.3", "1.4", "2.0", "2.1", "2.2", "2.3", "2.4"});
+
+ /* how and what do we have to parse */
+ private ParserSettings settings;
+
+ private boolean validate = true;
+
+ private URL descriptorURL;
+
+ private InputStream descriptorInput;
+
+ /* Parsing state */
+ private int state = State.NONE;
+
+ private PatternMatcher defaultMatcher;
+
+ private DefaultDependencyDescriptor dd;
+
+ private ConfigurationAware confAware;
+
+ private MDArtifact artifact;
+
+ private String conf;
+
+ private boolean artifactsDeclared = false;
+
+ private StringBuffer buffer;
+
+ private String descriptorVersion;
+
+ private String[] publicationsDefaultConf;
+
+ private Stack<ExtraInfoHolder> extraInfoStack = new Stack<ExtraInfoHolder>();
+
+ public Parser(ModuleDescriptorParser parser, ParserSettings ivySettings) {
+ super(parser);
+ settings = ivySettings;
+ }
+
+ public void setInput(InputStream descriptorInput) {
+ this.descriptorInput = descriptorInput;
+ }
+
+ public void setInput(URL descriptorURL) {
+ this.descriptorURL = descriptorURL;
+ }
+
+ public void setValidate(boolean validate) {
+ this.validate = validate;
+ }
+
+ public void parse() throws ParseException, IOException {
+ try {
+ URL schemaURL = validate ? getSchemaURL() : null;
+ if (descriptorURL != null) {
+ XMLHelper.parse(descriptorURL, schemaURL, this);
+ } else {
+ XMLHelper.parse(descriptorInput, schemaURL, this, null);
+ }
+ checkConfigurations();
+ replaceConfigurationWildcards();
+ getMd().setModuleArtifact(
+ DefaultArtifact.newIvyArtifact(getMd().getResolvedModuleRevisionId(), getMd()
+ .getPublicationDate()));
+ if (!artifactsDeclared) {
+ String[] confs = getMd().getConfigurationsNames();
+ for (int i = 0; i < confs.length; i++) {
+ getMd().addArtifact(
+ confs[i],
+ new MDArtifact(getMd(), getMd().getModuleRevisionId().getName(), "jar",
+ "jar"));
+ }
+ }
+ getMd().check();
+ } catch (ParserConfigurationException ex) {
+ IllegalStateException ise = new IllegalStateException(ex.getMessage() + " in "
+ + descriptorURL);
+ ise.initCause(ex);
+ throw ise;
+ } catch (Exception ex) {
+ checkErrors();
+ ParseException pe = new ParseException(ex.getMessage() + " in " + descriptorURL, 0);
+ pe.initCause(ex);
+ throw pe;
+ }
+ }
+
+ public void startElement(String uri, String localName, String qName, Attributes attributes)
+ throws SAXException {
+ try {
+ if (state == State.DESCRIPTION) {
+ // make sure we don't interpret any tag while in description tag
+ getBuffer().append("<").append(qName);
+ for (int i = 0; i < attributes.getLength(); i++) {
+ getBuffer().append(" ");
+ getBuffer().append(attributes.getQName(i));
+ getBuffer().append("=\"");
+ getBuffer().append(attributes.getValue(i));
+ getBuffer().append("\"");
+ }
+ getBuffer().append(">");
+ return;
+ } else if ("ivy-module".equals(qName)) {
+ ivyModuleStarted(attributes);
+ } else if ("info".equals(qName)) {
+ infoStarted(attributes);
+ } else if (state == State.INFO && "extends".equals(qName)) {
+ extendsStarted(attributes);
+ } else if (state == State.INFO && "license".equals(qName)) {
+ getMd().addLicense(
+ new License(settings.substitute(attributes.getValue("name")), settings
+ .substitute(attributes.getValue("url"))));
+ } else if (state == State.INFO && "description".equals(qName)) {
+ getMd().setHomePage(settings.substitute(attributes.getValue("homepage")));
+ state = State.DESCRIPTION;
+ buffer = new StringBuffer();
+ } else if (state == State.INFO && "ivyauthor".equals(qName)) {
+ // nothing to do, we don't store this
+ } else if (state == State.INFO && "repository".equals(qName)) {
+ // nothing to do, we don't store this
+ } else if (state == State.EXTRA_INFO || state == State.INFO
+ && isOtherNamespace(qName)) {
+ buffer = new StringBuffer();
+ state = State.EXTRA_INFO;
+ ExtraInfoHolder extraInfo = new ExtraInfoHolder();
+ extraInfo.setName(qName);
+ for (int i = 0; i < attributes.getLength(); i++) {
+ extraInfo.getAttributes().put(attributes.getQName(i),
+ attributes.getValue(i));
+ }
+ extraInfoStack.push(extraInfo);
+ } else if ("configurations".equals(qName)) {
+ configurationStarted(attributes);
+ } else if ("publications".equals(qName)) {
+ publicationsStarted(attributes);
+ } else if ("dependencies".equals(qName)) {
+ dependenciesStarted(attributes);
+ } else if ("conflicts".equals(qName)) {
+ if (!descriptorVersion.startsWith("1.")) {
+ Message.deprecated("using conflicts section is deprecated: "
+ + "please use hints section instead. Ivy file URL: "
+ + descriptorURL);
+ }
+ state = State.CONFLICT;
+ checkConfigurations();
+ } else if ("artifact".equals(qName)) {
+ artifactStarted(qName, attributes);
+ } else if ("include".equals(qName) && state == State.DEP) {
+ addIncludeRule(qName, attributes);
+ } else if ("exclude".equals(qName) && state == State.DEP) {
+ addExcludeRule(qName, attributes);
+ } else if ("exclude".equals(qName) && state == State.DEPS) {
+ state = State.EXCLUDE;
+ parseRule(qName, attributes);
+ getMd().addExcludeRule((ExcludeRule) confAware);
+ } else if ("dependency".equals(qName)) {
+ dependencyStarted(attributes);
+ } else if ("conf".equals(qName)) {
+ confStarted(attributes);
+ } else if ("mapped".equals(qName)) {
+ dd.addDependencyConfiguration(conf,
+ settings.substitute(attributes.getValue("name")));
+ } else if (("conflict".equals(qName) && state == State.DEPS)
+ || "manager".equals(qName) && state == State.CONFLICT) {
+ managerStarted(attributes, state == State.CONFLICT ? "name" : "manager");
+ } else if ("override".equals(qName) && state == State.DEPS) {
+ mediationOverrideStarted(attributes);
+ } else if ("include".equals(qName) && state == State.CONF) {
+ includeConfStarted(attributes);
+ } else if (validate && state != State.EXTRA_INFO && state != State.DESCRIPTION) {
+ addError("unknown tag " + qName);
+ }
+ } catch (Exception ex) {
+ if (ex instanceof SAXException) {
+ throw (SAXException) ex;
+ }
+ SAXException sax = new SAXException("Problem occurred while parsing ivy file: "
+ + ex.getMessage(), ex);
+ sax.initCause(ex);
+ throw sax;
+ }
+ }
+
+ /**
+ * Default parent location to check (for dev ONLY)
+ *
+ * @return a relative path to a parent module descriptor
+ */
+ protected String getDefaultParentLocation() {
+ return "../ivy.xml";
+ }
+
+ /**
+ * Handle extends elements. It checks :
+ * <ul>
+ * <li>filesystem based on location attribute, if no one is specified it will check the
+ * default parent location</li>
+ * <li>cache to find a resolved parent descriptor</li>
+ * <li>ask repositories to retrieve the parent module descriptor</li>
+ * </ul>
+ *
+ * @param attributes
+ * @throws ParseException
+ */
+ protected void extendsStarted(Attributes attributes) throws ParseException {
+ String parentOrganisation = settings.substitute(attributes.getValue("organisation"));
+ String parentModule = settings.substitute(attributes.getValue("module"));
+ String parentRevision = attributes.getValue("revision") != null ? settings
+ .substitute(attributes.getValue("revision")) : Ivy.getWorkingRevision();
+ String location = attributes.getValue("location") != null ? settings
+ .substitute(attributes.getValue("location")) : getDefaultParentLocation();
+ ModuleDescriptor parent = null;
+
+ String extendType = attributes.getValue("extendType") != null ? settings
+ .substitute(attributes.getValue("extendType").toLowerCase(Locale.US)) : "all";
+
+ List/* <String> */extendTypes = Arrays.asList(extendType.split(","));
+ ModuleId parentMid = new ModuleId(parentOrganisation, parentModule);
+ ModuleRevisionId parentMrid = new ModuleRevisionId(parentMid, parentRevision);
+
+ // check on filesystem based on location attribute (for dev ONLY)
+ boolean local = false;
+ try {
+ parent = parseParentModuleOnFilesystem(location);
+ if (parent != null) {
+ ModuleId foundMid = parent.getResolvedModuleRevisionId().getModuleId();
+ if (!foundMid.equals(parentMid)) {
+ // the filesystem contains a parent module with different organisation
+ // or module name; ignore that parent module
+ Message.info("Found a parent module with unexpected ModuleRevisionId at source location "
+ + location
+ + "! Expected: "
+ + parentMid
+ + ". Found: "
+ + foundMid
+ + ". This parent module will be ignored.");
+ parent = null;
+ }
+ }
+
+ local = parent != null;
+ } catch (IOException e) {
+ Message.warn("Unable to parse included ivy file " + location, e);
+ }
+
+ // if not found, tries to resolve using repositories
+ if (parent == null) {
+ try {
+ parent = parseOtherIvyFile(parentMrid);
+ } catch (ParseException e) {
+ Message.warn("Unable to parse included ivy file for " + parentMrid.toString(),
+ e);
+ }
+ }
+
+ // if still not found throw an exception
+ if (parent == null) {
+ throw new ParseException("Unable to parse included ivy file for "
+ + parentMrid.toString(), 0);
+ }
+
+ DefaultExtendsDescriptor ed = new DefaultExtendsDescriptor(parent, location,
+ (String[]) extendTypes.toArray(new String[extendTypes.size()]), local);
+ getMd().addInheritedDescriptor(ed);
+
+ mergeWithOtherModuleDescriptor(extendTypes, parent);
+ }
+
+ /**
+ * Merge current module with a given module descriptor and specify what should be inherited
+ * through extendTypes argument
+ *
+ * @param extendTypes
+ * specify what should be inherited
+ * @param parent
+ * a given parent module descriptor
+ */
+ protected void mergeWithOtherModuleDescriptor(List/* <String> */extendTypes,
+ ModuleDescriptor parent) throws ParseException {
+
+ if (extendTypes.contains("all")) {
+ mergeAll(parent);
+ } else {
+ if (extendTypes.contains("info")) {
+ mergeInfo(parent);
+ }
+
+ if (extendTypes.contains("configurations")) {
+ mergeConfigurations(parent);
+ }
+
+ if (extendTypes.contains("dependencies")) {
+ mergeDependencies(parent.getDependencies());
+ }
+
+ if (extendTypes.contains("description")) {
+ mergeDescription(parent.getDescription());
+ }
+ if (extendTypes.contains("licenses")) {
+ mergeLicenses(parent.getLicenses());
+ }
+ if (extendTypes.contains("excludes")) {
+ mergeExcludes(parent.getAllExcludeRules());
+ }
+ }
+
+ }
+
+ /**
+ * Merge everything from a given parent
+ *
+ * @param parent
+ * a given parent module desciptor
+ */
+ protected void mergeAll(ModuleDescriptor parent) {
+ mergeInfo(parent);
+ mergeConfigurations(parent);
+ mergeDependencies(parent.getDependencies());
+ mergeDescription(parent.getDescription());
+ mergeLicenses(parent.getLicenses());
+ mergeExcludes(parent.getAllExcludeRules());
+ }
+
+ /**
+ * Explain how to inherit metadatas related to info element
+ *
+ * @param parent
+ * a given parent module decriptor
+ */
+ protected void mergeInfo(ModuleDescriptor parent) {
+ ModuleRevisionId parentMrid = parent.getModuleRevisionId();
+
+ DefaultModuleDescriptor descriptor = getMd();
+ ModuleRevisionId currentMrid = descriptor.getModuleRevisionId();
+
+ ModuleRevisionId mergedMrid = ModuleRevisionId.newInstance(
+ mergeValue(parentMrid.getOrganisation(), currentMrid.getOrganisation()),
+ currentMrid.getName(),
+ mergeValue(parentMrid.getBranch(), currentMrid.getBranch()),
+ mergeRevisionValue(parentMrid.getRevision(), currentMrid.getRevision()),
+ mergeValues(parentMrid.getQualifiedExtraAttributes(),
+ currentMrid.getQualifiedExtraAttributes()));
+
+ descriptor.setModuleRevisionId(mergedMrid);
+ descriptor.setResolvedModuleRevisionId(mergedMrid);
+
+ descriptor.setStatus(mergeValue(parent.getStatus(), descriptor.getStatus()));
+ if (descriptor.getNamespace() == null && parent instanceof DefaultModuleDescriptor) {
+ Namespace parentNamespace = ((DefaultModuleDescriptor) parent).getNamespace();
+ descriptor.setNamespace(parentNamespace);
+ }
+
+ descriptor.getExtraInfos().addAll(parent.getExtraInfos());
+ }
+
+ private static String mergeRevisionValue(String inherited, String override) {
+ if (override == null || override.equals(Ivy.getWorkingRevision())) {
+ return inherited;
+ } else {
+ return override;
+ }
+ }
+
+ private static String mergeValue(String inherited, String override) {
+ return override == null ? inherited : override;
+ }
+
+ private static Map mergeValues(Map inherited, Map overrides) {
+ LinkedHashMap dup = new LinkedHashMap(inherited.size() + overrides.size());
+ dup.putAll(inherited);
+ dup.putAll(overrides);
+ return dup;
+ }
+
+ /**
+ * Describes how to merge configurations elements
+ *
+ * @param sourceMrid
+ * the source module revision id
+ * @param configurations
+ * array of configurations to be inherited
+ */
+ protected void mergeConfigurations(ModuleDescriptor parent) {
+ ModuleRevisionId sourceMrid = parent.getModuleRevisionId();
+ Configuration[] configurations = parent.getConfigurations();
+ for (int i = 0; i < configurations.length; i++) {
+ Configuration configuration = configurations[i];
+ Message.debug("Merging configuration with: " + configuration.getName());
+ // copy configuration from parent descriptor
+ getMd().addConfiguration(new Configuration(configuration, sourceMrid));
+ }
+
+ if (parent instanceof DefaultModuleDescriptor) {
+ setDefaultConfMapping(((DefaultModuleDescriptor) parent).getDefaultConfMapping());
+ setDefaultConf(((DefaultModuleDescriptor) parent).getDefaultConf());
+ getMd().setMappingOverride(((DefaultModuleDescriptor) parent).isMappingOverride());
+ }
+ }
+
+ /**
+ * Describes how dependencies should be inherited
+ *
+ * @param dependencies
+ * array of dependencies to inherit
+ */
+ protected void mergeDependencies(DependencyDescriptor[] dependencies) {
+ DefaultModuleDescriptor md = getMd();
+ for (int i = 0; i < dependencies.length; i++) {
+ DependencyDescriptor dependencyDescriptor = dependencies[i];
+ Message.debug("Merging dependency with: "
+ + dependencyDescriptor.getDependencyRevisionId().toString());
+ md.addDependency(dependencyDescriptor);
+ }
+ }
+
+ /**
+ * Describes how to merge description
+ *
+ * @param description
+ * description going to be inherited
+ */
+ protected void mergeDescription(String description) {
+ String current = getMd().getDescription();
+ if (current == null || current.trim().length() == 0) {
+ getMd().setDescription(description);
+ }
+ }
+
+ /**
+ * Describes how to merge licenses
+ *
+ * @param licenses
+ * licenses going to be inherited
+ */
+ public void mergeLicenses(License[] licenses) {
+ for (int i = 0; i < licenses.length; i++) {
+ getMd().addLicense(licenses[i]);
+ }
+ }
+
+ /**
+ * Describes how to merge exclude rules
+ *
+ * @param excludeRules
+ * exclude rules going to be inherited
+ */
+ public void mergeExcludes(ExcludeRule[] excludeRules) {
+ for (int i = 0; i < excludeRules.length; i++) {
+ getMd().addExcludeRule(excludeRules[i]);
+ }
+ }
+
+ /**
+ * Returns the parent module using the location attribute (for dev purpose).
+ *
+ * @param location
+ * a given location
+ * @throws IOException
+ * @throws ParseException
+ */
+ private ModuleDescriptor parseParentModuleOnFilesystem(String location) throws IOException,
+ ParseException {
+ if (!"file".equals(descriptorURL.getProtocol())) {
+ return null;
+ }
+
+ File file = new File(location);
+ if (!file.isAbsolute()) {
+ URL url = settings.getRelativeUrlResolver().getURL(descriptorURL, location);
+ try {
+ file = new File(new URI(url.toExternalForm()));
+ } catch (URISyntaxException e) {
+ file = new File(url.getPath());
+ }
+ }
+
+ file = FileUtil.normalize(file.getAbsolutePath());
+ if (!file.exists()) {
+ Message.verbose("Parent module doesn't exist on the filesystem: "
+ + file.getAbsolutePath());
+ return null;
+ }
+
+ FileResource res = new FileResource(null, file);
+ ModuleDescriptorParser parser = ModuleDescriptorParserRegistry.getInstance().getParser(
+ res);
+ return parser.parseDescriptor(getSettings(), file.toURL(), res, isValidate());
+ }
+
+ /**
+ * Describe how to parse a {@link ModuleDescriptor} by asking repositories
+ *
+ * @param parentMrid
+ * a given {@link ModuleRevisionId} to find
+ * @return a {@link ModuleDescriptor} if found. Return null if no {@link ModuleDescriptor}
+ * was found
+ * @throws ParseException
+ */
+ protected ModuleDescriptor parseOtherIvyFile(ModuleRevisionId parentMrid)
+ throws ParseException {
+ Message.debug("Trying to parse included ivy file by asking repository for module :"
+ + parentMrid.toString());
+ DependencyDescriptor dd = new DefaultDependencyDescriptor(parentMrid, true);
+ ResolveData data = IvyContext.getContext().getResolveData();
+ if (data == null) {
+ ResolveEngine engine = IvyContext.getContext().getIvy().getResolveEngine();
+ ResolveOptions options = new ResolveOptions();
+ options.setDownload(false);
+ data = new ResolveData(engine, options);
+ }
+ DependencyResolver resolver = getSettings().getResolver(parentMrid);
+ dd = NameSpaceHelper.toSystem(dd, getSettings().getContextNamespace());
+ ResolvedModuleRevision otherModule = resolver.getDependency(dd, data);
+ if (otherModule == null) {
+ throw new ParseException("Unable to find " + parentMrid.toString(), 0);
+ }
+ return otherModule.getDescriptor();
+
+ }
+
+ protected void publicationsStarted(Attributes attributes) {
+ state = State.PUB;
+ artifactsDeclared = true;
+ checkConfigurations();
+ String defaultConf = settings.substitute(attributes.getValue("defaultconf"));
+ if (defaultConf != null) {
+ setPublicationsDefaultConf(defaultConf);
+ }
+ }
+
+ protected void setPublicationsDefaultConf(String defaultConf) {
+ this.publicationsDefaultConf = defaultConf == null ? null : defaultConf.split(",");
+ }
+
+ protected boolean isOtherNamespace(String qName) {
+ return qName.indexOf(':') != -1;
+ }
+
+ protected void managerStarted(Attributes attributes, String managerAtt) {
+ String org = settings.substitute(attributes.getValue("org"));
+ org = org == null ? PatternMatcher.ANY_EXPRESSION : org;
+ String mod = settings.substitute(attributes.getValue("module"));
+ mod = mod == null ? PatternMatcher.ANY_EXPRESSION : mod;
+ ConflictManager cm;
+ String name = settings.substitute(attributes.getValue(managerAtt));
+ String rev = settings.substitute(attributes.getValue("rev"));
+ if (rev != null) {
+ String[] revs = rev.split(",");
+ for (int i = 0; i < revs.length; i++) {
+ revs[i] = revs[i].trim();
+ }
+ cm = new FixedConflictManager(revs);
+ } else if (name != null) {
+ cm = settings.getConflictManager(name);
+ if (cm == null) {
+ addError("unknown conflict manager: " + name);
+ return;
+ }
+ } else {
+ addError("bad conflict manager: no manager nor rev");
+ return;
+ }
+ String matcherName = settings.substitute(attributes.getValue("matcher"));
+ PatternMatcher matcher = matcherName == null ? defaultMatcher : settings
+ .getMatcher(matcherName);
+ if (matcher == null) {
+ addError("unknown matcher: " + matcherName);
+ return;
+ }
+ getMd().addConflictManager(new ModuleId(org, mod), matcher, cm);
+ }
+
+ protected void mediationOverrideStarted(Attributes attributes) {
+ String org = settings.substitute(attributes.getValue("org"));
+ org = org == null ? PatternMatcher.ANY_EXPRESSION : org;
+ String mod = settings.substitute(attributes.getValue("module"));
+ mod = mod == null ? PatternMatcher.ANY_EXPRESSION : mod;
+ String rev = settings.substitute(attributes.getValue("rev"));
+ String branch = settings.substitute(attributes.getValue("branch"));
+ String matcherName = settings.substitute(attributes.getValue("matcher"));
+ PatternMatcher matcher = matcherName == null ? defaultMatcher : settings
+ .getMatcher(matcherName);
+ if (matcher == null) {
+ addError("unknown matcher: " + matcherName);
+ return;
+ }
+ getMd().addDependencyDescriptorMediator(new ModuleId(org, mod), matcher,
+ new OverrideDependencyDescriptorMediator(branch, rev));
+ }
+
+ protected void includeConfStarted(Attributes attributes) throws SAXException, IOException,
+ ParserConfigurationException, ParseException {
+ URL url = settings.getRelativeUrlResolver().getURL(descriptorURL,
+ settings.substitute(attributes.getValue("file")),
+ settings.substitute(attributes.getValue("url")));
+
+ if (url == null) {
+ throw new SAXException("include tag must have a file or an url attribute");
+ }
+
+ // create a new temporary parser to read the configurations from
+ // the specified file.
+ Parser parser = new Parser(getModuleDescriptorParser(), settings);
+ parser.setInput(url);
+ parser.setMd(new DefaultModuleDescriptor(getModuleDescriptorParser(), new URLResource(
+ url)));
+ XMLHelper.parse(url, null, parser);
+
+ // add the configurations from this temporary parser to this module descriptor
+ Configuration[] configs = parser.getModuleDescriptor().getConfigurations();
+ for (int i = 0; i < configs.length; i++) {
+ getMd().addConfiguration(configs[i]);
+ }
+ if (parser.getDefaultConfMapping() != null) {
+ Message.debug("setting default conf mapping from imported configurations file: "
+ + parser.getDefaultConfMapping());
+ setDefaultConfMapping(parser.getDefaultConfMapping());
+ }
+ if (parser.getDefaultConf() != null) {
+ Message.debug("setting default conf from imported configurations file: "
+ + parser.getDefaultConf());
+ setDefaultConf(parser.getDefaultConf());
+ }
+ if (parser.getMd().isMappingOverride()) {
+ Message.debug("enabling mapping-override from imported configurations" + " file");
+ getMd().setMappingOverride(true);
+ }
+ }
+
+ protected void confStarted(Attributes attributes) {
+ String conf = settings.substitute(attributes.getValue("name"));
+ switch (state) {
+ case State.CONF:
+ String visibility = settings.substitute(attributes.getValue("visibility"));
+ String ext = settings.substitute(attributes.getValue("extends"));
+ String transitiveValue = attributes.getValue("transitive");
+ boolean transitive = (transitiveValue == null) ? true : Boolean.valueOf(
+ attributes.getValue("transitive")).booleanValue();
+ String deprecated = attributes.getValue("deprecated");
+ Configuration configuration = new Configuration(conf,
+ Configuration.Visibility.getVisibility(visibility == null ? "public"
+ : visibility), settings.substitute(attributes
+ .getValue("description")), ext == null ? null : ext.split(","),
+ transitive, deprecated);
+ ExtendableItemHelper.fillExtraAttributes(settings, configuration, attributes,
+ new String[] {"name", "visibility", "extends", "transitive", "description",
+ "deprecated"});
+ getMd().addConfiguration(configuration);
+ break;
+ case State.PUB:
+ if ("*".equals(conf)) {
+ String[] confs = getMd().getConfigurationsNames();
+ for (int i = 0; i < confs.length; i++) {
+ artifact.addConfiguration(confs[i]);
+ getMd().addArtifact(confs[i], artifact);
+ }
+ } else {
+ artifact.addConfiguration(conf);
+ getMd().addArtifact(conf, artifact);
+ }
+ break;
+ case State.DEP:
+ this.conf = conf;
+ String mappeds = settings.substitute(attributes.getValue("mapped"));
+ if (mappeds != null) {
+ String[] mapped = mappeds.split(",");
+ for (int i = 0; i < mapped.length; i++) {
+ dd.addDependencyConfiguration(conf, mapped[i].trim());
+ }
+ }
+ break;
+ case State.DEP_ARTIFACT:
+ case State.ARTIFACT_INCLUDE:
+ case State.ARTIFACT_EXCLUDE:
+ addConfiguration(conf);
+ break;
+ default:
+ if (validate) {
+ addError("conf tag found in invalid tag: " + state);
+ }
+ break;
+ }
+ }
+
+ protected void dependencyStarted(Attributes attributes) {
+ state = State.DEP;
+ String org = settings.substitute(attributes.getValue("org"));
+ if (org == null) {
+ org = getMd().getModuleRevisionId().getOrganisation();
+ }
+ boolean force = Boolean.valueOf(settings.substitute(attributes.getValue("force")))
+ .booleanValue();
+ boolean changing = Boolean
+ .valueOf(settings.substitute(attributes.getValue("changing"))).booleanValue();
+
+ String transitiveValue = settings.substitute(attributes.getValue("transitive"));
+ boolean transitive = (transitiveValue == null) ? true : Boolean.valueOf(
+ attributes.getValue("transitive")).booleanValue();
+
+ String name = settings.substitute(attributes.getValue("name"));
+ String branch = settings.substitute(attributes.getValue("branch"));
+ String branchConstraint = settings.substitute(attributes.getValue("branchConstraint"));
+
+ // if (branchConstraint == null) {
+ // // there was no branch constraint before, so we should
+ // // set the branchConstraint to the current default branch
+ // branchConstraint = settings.getDefaultBranch(ModuleId.newInstance(org, name));
+ // }
+
+ String rev = settings.substitute(attributes.getValue("rev"));
+ String revConstraint = settings.substitute(attributes.getValue("revConstraint"));
+
+ Map extraAttributes = ExtendableItemHelper.getExtraAttributes(settings, attributes,
+ DEPENDENCY_REGULAR_ATTRIBUTES);
+
+ ModuleRevisionId revId = ModuleRevisionId.newInstance(org, name, branch, rev,
+ extraAttributes);
+ ModuleRevisionId dynamicId = null;
+ if ((revConstraint == null) && (branchConstraint == null)) {
+ // no dynamic constraints defined, so dynamicId equals revId
+ dynamicId = ModuleRevisionId.newInstance(org, name, branch, rev, extraAttributes,
+ false);
+ } else {
+ if (branchConstraint == null) {
+ // this situation occurs when there was no branch defined
+ // in the original dependency descriptor. So the dynamicId
+ // shouldn't contain a branch neither
+ dynamicId = ModuleRevisionId.newInstance(org, name, null, revConstraint,
+ extraAttributes, false);
+ } else {
+ dynamicId = ModuleRevisionId.newInstance(org, name, branchConstraint,
+ revConstraint, extraAttributes);
+ }
+ }
+
+ dd = new DefaultDependencyDescriptor(getMd(), revId, dynamicId, force, changing,
+ transitive);
+ getMd().addDependency(dd);
+ String confs = settings.substitute(attributes.getValue("conf"));
+ if (confs != null && confs.length() > 0) {
+ parseDepsConfs(confs, dd);
+ }
+ }
+
+ protected void artifactStarted(String qName, Attributes attributes)
+ throws MalformedURLException {
+ if (state == State.PUB) {
+ // this is a published artifact
+ String artName = settings.substitute(attributes.getValue("name"));
+ artName = artName == null ? getMd().getModuleRevisionId().getName() : artName;
+ String type = settings.substitute(attributes.getValue("type"));
+ type = type == null ? "jar" : type;
+ String ext = settings.substitute(attributes.getValue("ext"));
+ ext = ext != null ? ext : type;
+ String url = settings.substitute(attributes.getValue("url"));
+ artifact = new MDArtifact(getMd(), artName, type, ext, url == null ? null
+ : new URL(url), ExtendableItemHelper.getExtraAttributes(settings,
+ attributes, new String[] {"ext", "type", "name", "conf"}));
+ String confs = settings.substitute(attributes.getValue("conf"));
+ // only add confs if they are specified. if they aren't, endElement will
+ // handle this
+ // only if there are no conf defined in sub elements
+ if (confs != null && confs.length() > 0) {
+ String[] conf;
+ if ("*".equals(confs)) {
+ conf = getMd().getConfigurationsNames();
+ } else {
+ conf = confs.split(",");
+ }
+ for (int i = 0; i < conf.length; i++) {
+ artifact.addConfiguration(conf[i].trim());
+ getMd().addArtifact(conf[i].trim(), artifact);
+ }
+ }
+ } else if (state == State.DEP) {
+ // this is an artifact asked for a particular dependency
+ addDependencyArtifacts(qName, attributes);
+ } else if (validate) {
+ addError("artifact tag found in invalid tag: " + state);
+ }
+ }
+
+ protected void dependenciesStarted(Attributes attributes) {
+ state = State.DEPS;
+ String defaultConf = settings.substitute(attributes.getValue("defaultconf"));
+ if (defaultConf != null) {
+ setDefaultConf(defaultConf);
+ }
+ defaultConf = settings.substitute(attributes.getValue("defaultconfmapping"));
+ if (defaultConf != null) {
+ setDefaultConfMapping(defaultConf);
+ }
+ String confMappingOverride = settings.substitute(attributes
+ .getValue("confmappingoverride"));
+ if (confMappingOverride != null) {
+ getMd().setMappingOverride(Boolean.valueOf(confMappingOverride).booleanValue());
+ }
+ checkConfigurations();
+ }
+
+ protected void configurationStarted(Attributes attributes) {
+ state = State.CONF;
+ setDefaultConfMapping(settings.substitute(attributes.getValue("defaultconfmapping")));
+ setDefaultConf(settings.substitute(attributes.getValue("defaultconf")));
+ getMd().setMappingOverride(
+ Boolean.valueOf(settings.substitute(attributes.getValue("confmappingoverride")))
+ .booleanValue());
+ }
+
+ protected void infoStarted(Attributes attributes) {
+ state = State.INFO;
+ String org = settings.substitute(attributes.getValue("organisation"));
+ String module = settings.substitute(attributes.getValue("module"));
+ String revision = settings.substitute(attributes.getValue("revision"));
+ String branch = settings.substitute(attributes.getValue("branch"));
+ getMd().setModuleRevisionId(
+ ModuleRevisionId.newInstance(
+ org,
+ module,
+ branch,
+ revision,
+ ExtendableItemHelper.getExtraAttributes(settings, attributes, new String[] {
+ "organisation", "module", "revision", "status", "publication",
+ "branch", "namespace", "default", "resolver"})));
+
+ String namespace = settings.substitute(attributes.getValue("namespace"));
+ if (namespace != null) {
+ Namespace ns = settings.getNamespace(namespace);
+ if (ns == null) {
+ Message.warn("namespace not found for " + getMd().getModuleRevisionId() + ": "
+ + namespace);
+ } else {
+ getMd().setNamespace(ns);
+ }
+ }
+
+ String status = settings.substitute(attributes.getValue("status"));
+ getMd().setStatus(
+ status == null ? settings.getStatusManager().getDefaultStatus() : status);
+ getMd().setDefault(
+ Boolean.valueOf(settings.substitute(attributes.getValue("default"))).booleanValue());
+ String pubDate = settings.substitute(attributes.getValue("publication"));
+ if (pubDate != null && pubDate.length() > 0) {
+ try {
+ getMd().setPublicationDate(DateUtil.parse(pubDate));
+ } catch (ParseException e) {
+ addError("invalid publication date format: " + pubDate);
+ getMd().setPublicationDate(getDefaultPubDate());
+ }
+ } else {
+ getMd().setPublicationDate(getDefaultPubDate());
+ }
+ }
+
+ protected void ivyModuleStarted(Attributes attributes) throws SAXException {
+ descriptorVersion = attributes.getValue("version");
+ int versionIndex = ALLOWED_VERSIONS.indexOf(descriptorVersion);
+ if (versionIndex == -1) {
+ addError("invalid version " + descriptorVersion);
+ throw new SAXException("invalid version " + descriptorVersion);
+ }
+ if (versionIndex >= ALLOWED_VERSIONS.indexOf("1.3")) {
+ Message.debug("post 1.3 ivy file: using " + PatternMatcher.EXACT
+ + " as default matcher");
+ defaultMatcher = settings.getMatcher(PatternMatcher.EXACT);
+ } else {
+ Message.debug("pre 1.3 ivy file: using " + PatternMatcher.EXACT_OR_REGEXP
+ + " as default matcher");
+ defaultMatcher = settings.getMatcher(PatternMatcher.EXACT_OR_REGEXP);
+ }
+
+ for (int i = 0; i < attributes.getLength(); i++) {
+ if (attributes.getQName(i).startsWith("xmlns:")) {
+ getMd().addExtraAttributeNamespace(
+ attributes.getQName(i).substring("xmlns:".length()), attributes.getValue(i));
+ }
+ }
+ }
+
+ protected void addDependencyArtifacts(String tag, Attributes attributes)
+ throws MalformedURLException {
+ state = State.DEP_ARTIFACT;
+ parseRule(tag, attributes);
+ }
+
+ protected void addIncludeRule(String tag, Attributes attributes)
+ throws MalformedURLException {
+ state = State.ARTIFACT_INCLUDE;
+ parseRule(tag, attributes);
+ }
+
+ protected void addExcludeRule(String tag, Attributes attributes)
+ throws MalformedURLException {
+ state = State.ARTIFACT_EXCLUDE;
+ parseRule(tag, attributes);
+ }
+
+ protected void parseRule(String tag, Attributes attributes) throws MalformedURLException {
+ String name = settings.substitute(attributes.getValue("name"));
+ if (name == null) {
+ name = settings.substitute(attributes.getValue("artifact"));
+ if (name == null) {
+ name = "artifact".equals(tag) ? dd.getDependencyId().getName()
+ : PatternMatcher.ANY_EXPRESSION;
+ }
+ }
+ String type = settings.substitute(attributes.getValue("type"));
+ if (type == null) {
+ type = "artifact".equals(tag) ? "jar" : PatternMatcher.ANY_EXPRESSION;
+ }
+ String ext = settings.substitute(attributes.getValue("ext"));
+ ext = ext != null ? ext : type;
+ if (state == State.DEP_ARTIFACT) {
+ String url = settings.substitute(attributes.getValue("url"));
+ Map extraAtt = ExtendableItemHelper.getExtraAttributes(settings, attributes,
+ new String[] {"name", "type", "ext", "url", "conf"});
+ confAware = new DefaultDependencyArtifactDescriptor(dd, name, type, ext,
+ url == null ? null : new URL(url), extraAtt);
+ } else if (state == State.ARTIFACT_INCLUDE) {
+ PatternMatcher matcher = getPatternMatcher(attributes.getValue("matcher"));
+ String org = settings.substitute(attributes.getValue("org"));
+ org = org == null ? PatternMatcher.ANY_EXPRESSION : org;
+ String module = settings.substitute(attributes.getValue("module"));
+ module = module == null ? PatternMatcher.ANY_EXPRESSION : module;
+ ArtifactId aid = new ArtifactId(new ModuleId(org, module), name, type, ext);
+ Map extraAtt = ExtendableItemHelper.getExtraAttributes(settings, attributes,
+ new String[] {"org", "module", "name", "type", "ext", "matcher", "conf"});
+ confAware = new DefaultIncludeRule(aid, matcher, extraAtt);
+ } else { // _state == ARTIFACT_EXCLUDE || EXCLUDE
+ PatternMatcher matcher = getPatternMatcher(attributes.getValue("matcher"));
+ String org = settings.substitute(attributes.getValue("org"));
+ org = org == null ? PatternMatcher.ANY_EXPRESSION : org;
+ String module = settings.substitute(attributes.getValue("module"));
+ module = module == null ? PatternMatcher.ANY_EXPRESSION : module;
+ ArtifactId aid = new ArtifactId(new ModuleId(org, module), name, type, ext);
+ Map extraAtt = ExtendableItemHelper.getExtraAttributes(settings, attributes,
+ new String[] {"org", "module", "name", "type", "ext", "matcher", "conf"});
+ confAware = new DefaultExcludeRule(aid, matcher, extraAtt);
+ }
+ String confs = settings.substitute(attributes.getValue("conf"));
+ // only add confs if they are specified. if they aren't, endElement will handle this
+ // only if there are no conf defined in sub elements
+ if (confs != null && confs.length() > 0) {
+ String[] conf;
+ if ("*".equals(confs)) {
+ conf = getMd().getConfigurationsNames();
+ } else {
+ conf = confs.split(",");
+ }
+ for (int i = 0; i < conf.length; i++) {
+ addConfiguration(conf[i].trim());
+ }
+ }
+ }
+
+ protected void addConfiguration(String c) {
+ confAware.addConfiguration(c);
+ if (state == State.EXCLUDE) {
+ // we are adding a configuration to a module wide exclude rule
+ // we have nothing special to do here, the rule has already been added to the module
+ // descriptor
+ } else {
+ // we are currently adding a configuration to either an include, exclude or artifact
+ // element
+ // of a dependency. This means that we have to add this element to the corresponding
+ // conf
+ // of the current dependency descriptor
+ if (confAware instanceof DependencyArtifactDescriptor) {
+ dd.addDependencyArtifact(c, (DependencyArtifactDescriptor) confAware);
+ } else if (confAware instanceof IncludeRule) {
+ dd.addIncludeRule(c, (IncludeRule) confAware);
+ } else if (confAware instanceof ExcludeRule) {
+ dd.addExcludeRule(c, (ExcludeRule) confAware);
+ }
+ }
+ }
+
+ protected PatternMatcher getPatternMatcher(String m) {
+ String matcherName = settings.substitute(m);
+ PatternMatcher matcher = matcherName == null ? defaultMatcher : settings
+ .getMatcher(matcherName);
+ if (matcher == null) {
+ throw new IllegalArgumentException("unknown matcher " + matcherName);
+ }
+ return matcher;
+ }
+
+ public void characters(char[] ch, int start, int length) throws SAXException {
+ if (buffer != null) {
+ buffer.append(ch, start, length);
+ }
+ }
+
+ public void endElement(String uri, String localName, String qName) throws SAXException {
+ if (state == State.PUB && "artifact".equals(qName)
+ && artifact.getConfigurations().length == 0) {
+ String[] confs = publicationsDefaultConf == null ? getMd().getConfigurationsNames()
+ : publicationsDefaultConf;
+ for (int i = 0; i < confs.length; i++) {
+ artifact.addConfiguration(confs[i].trim());
+ getMd().addArtifact(confs[i].trim(), artifact);
+ }
+ } else if ("configurations".equals(qName)) {
+ checkConfigurations();
+ } else if ((state == State.DEP_ARTIFACT && "artifact".equals(qName))
+ || (state == State.ARTIFACT_INCLUDE && "include".equals(qName))
+ || (state == State.ARTIFACT_EXCLUDE && "exclude".equals(qName))) {
+ state = State.DEP;
+ if (confAware.getConfigurations().length == 0) {
+ String[] confs = getMd().getConfigurationsNames();
+ for (int i = 0; i < confs.length; i++) {
+ addConfiguration(confs[i]);
+ }
+ }
+ confAware = null;
+ } else if ("exclude".equals(qName) && state == State.EXCLUDE) {
+ if (confAware.getConfigurations().length == 0) {
+ String[] confs = getMd().getConfigurationsNames();
+ for (int i = 0; i < confs.length; i++) {
+ addConfiguration(confs[i]);
+ }
+ }
+ confAware = null;
+ state = State.DEPS;
+ } else if ("dependency".equals(qName) && state == State.DEP) {
+ if (dd.getModuleConfigurations().length == 0) {
+ parseDepsConfs(getDefaultConf(), dd);
+ }
+ state = State.DEPS;
+ } else if ("dependencies".equals(qName) && state == State.DEPS) {
+ state = State.NONE;
+ } else if (state == State.INFO && "info".equals(qName)) {
+ state = State.NONE;
+ } else if (state == State.DESCRIPTION && "description".equals(qName)) {
+ getMd().setDescription(buffer == null ? "" : buffer.toString().trim());
+ buffer = null;
+ state = State.INFO;
+ } else if (state == State.EXTRA_INFO) {
+ String content = buffer == null ? "" : buffer.toString();
+ buffer = null;
+ ExtraInfoHolder extraInfo = extraInfoStack.pop();
+ extraInfo.setContent(content);
+ if (extraInfoStack.isEmpty()) {
+ getMd().addExtraInfo(extraInfo);
+ state = State.INFO;
+ } else {
+ ExtraInfoHolder parentHolder = extraInfoStack.peek();
+ parentHolder.getNestedExtraInfoHolder().add(extraInfo);
+ }
+ } else if (state == State.DESCRIPTION) {
+ if (buffer.toString().endsWith("<" + qName + ">")) {
+ buffer.deleteCharAt(buffer.length() - 1);
+ buffer.append("/>");
+ } else {
+ buffer.append("</" + qName + ">");
+ }
+ }
+ }
+
+ protected void checkConfigurations() {
+ if (getMd().getConfigurations().length == 0) {
+ getMd().addConfiguration(new Configuration("default"));
+ }
+ }
+
+ protected void replaceConfigurationWildcards() {
+ Configuration[] configs = getMd().getConfigurations();
+ for (int i = 0; i < configs.length; i++) {
+ configs[i].replaceWildcards(getMd());
+ }
+ }
+
+ /* getters and setters available for extension only */
+ protected ParserSettings getSettings() {
+ return settings;
+ }
+
+ protected URL getDescriptorURL() {
+ return descriptorURL;
+ }
+
+ protected InputStream getDescriptorInput() {
+ return descriptorInput;
+ }
+
+ protected int getState() {
+ return state;
+ }
+
+ protected void setState(int state) {
+ this.state = state;
+ }
+
+ protected PatternMatcher getDefaultMatcher() {
+ return defaultMatcher;
+ }
+
+ protected DefaultDependencyDescriptor getDd() {
+ return dd;
+ }
+
+ protected void setDd(DefaultDependencyDescriptor dd) {
+ this.dd = dd;
+ }
+
+ protected ConfigurationAware getConfAware() {
+ return confAware;
+ }
+
+ protected void setConfAware(ConfigurationAware confAware) {
+ this.confAware = confAware;
+ }
+
+ protected MDArtifact getArtifact() {
+ return artifact;
+ }
+
+ protected void setArtifact(MDArtifact artifact) {
+ this.artifact = artifact;
+ }
+
+ protected String getConf() {
+ return conf;
+ }
+
+ protected void setConf(String conf) {
+ this.conf = conf;
+ }
+
+ protected boolean isArtifactsDeclared() {
+ return artifactsDeclared;
+ }
+
+ protected void setArtifactsDeclared(boolean artifactsDeclared) {
+ this.artifactsDeclared = artifactsDeclared;
+ }
+
+ protected StringBuffer getBuffer() {
+ return buffer;
+ }
+
+ protected void setBuffer(StringBuffer buffer) {
+ this.buffer = buffer;
+ }
+
+ protected String getDescriptorVersion() {
+ return descriptorVersion;
+ }
+
+ protected void setDescriptorVersion(String descriptorVersion) {
+ this.descriptorVersion = descriptorVersion;
+ }
+
+ protected String[] getPublicationsDefaultConf() {
+ return publicationsDefaultConf;
+ }
+
+ protected void setPublicationsDefaultConf(String[] publicationsDefaultConf) {
+ this.publicationsDefaultConf = publicationsDefaultConf;
+ }
+
+ protected boolean isValidate() {
+ return validate;
+ }
+
+ protected URL getSchemaURL() {
+ return getClass().getResource("ivy.xsd");
+ }
+ }
+
+ public String toString() {
+ return "ivy parser";
+ }
+
+}
diff --git a/src/java/org/apache/ivy/plugins/parser/xml/XmlModuleDescriptorUpdater.java b/src/java/org/apache/ivy/plugins/parser/xml/XmlModuleDescriptorUpdater.java
new file mode 100644
index 0000000..9880f2d
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/parser/xml/XmlModuleDescriptorUpdater.java
@@ -0,0 +1,1340 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.parser.xml;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Stack;
+import java.util.StringTokenizer;
+
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.apache.ivy.core.module.descriptor.Configuration;
+import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.ExtendsDescriptor;
+import org.apache.ivy.core.module.descriptor.InheritableItem;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleId;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.plugins.namespace.NameSpaceHelper;
+import org.apache.ivy.plugins.namespace.Namespace;
+import org.apache.ivy.plugins.parser.ParserSettings;
+import org.apache.ivy.plugins.repository.Resource;
+import org.apache.ivy.plugins.repository.file.FileResource;
+import org.apache.ivy.plugins.repository.url.URLResource;
+import org.apache.ivy.util.Checks;
+import org.apache.ivy.util.DateUtil;
+import org.apache.ivy.util.Message;
+import org.apache.ivy.util.XMLHelper;
+import org.apache.ivy.util.extendable.ExtendableItemHelper;
+import org.xml.sax.Attributes;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.ext.LexicalHandler;
+import org.xml.sax.helpers.DefaultHandler;
+
+/**
+ * Used to update ivy files. Uses ivy file as source and not ModuleDescriptor to preserve as much as
+ * possible the original syntax
+ */
+public final class XmlModuleDescriptorUpdater {
+ // CheckStyle:StaticVariableName| OFF
+ // LINE_SEPARATOR is actually a constant, but we have to modify it for the tests
+ public static String LINE_SEPARATOR = System.getProperty("line.separator");
+
+ // CheckStyle:StaticVariableName| ON
+
+ private XmlModuleDescriptorUpdater() {
+ }
+
+ /**
+ * used to copy a module descriptor xml file (also known as ivy file) and update the revisions
+ * of its dependencies, its status and revision
+ *
+ * @param srcURL
+ * the url of the source module descriptor file
+ * @param destFile
+ * The file to which the updated module descriptor should be output
+ */
+ public static void update(URL srcURL, File destFile, UpdateOptions options) throws IOException,
+ SAXException {
+ if (destFile.getParentFile() != null) {
+ destFile.getParentFile().mkdirs();
+ }
+ OutputStream destStream = new FileOutputStream(destFile);
+ try {
+ update(srcURL, destStream, options);
+ } finally {
+ try {
+ destStream.close();
+ } catch (IOException e) {
+ Message.warn("failed to close a stream : " + e.toString());
+ }
+ }
+ }
+
+ public static void update(URL srcURL, OutputStream destFile, UpdateOptions options)
+ throws IOException, SAXException {
+ InputStream in = srcURL.openStream();
+ try {
+ update(srcURL, in, destFile, options);
+ } finally {
+ try {
+ in.close();
+ } catch (IOException e) {
+ Message.warn("failed to close a stream : " + e.toString());
+ }
+ try {
+ destFile.close();
+ } catch (IOException e) {
+ Message.warn("failed to close a stream : " + e.toString());
+ }
+ }
+
+ }
+
+ public static void update(InputStream in, Resource res, File destFile, UpdateOptions options)
+ throws IOException, SAXException {
+ if (destFile.getParentFile() != null) {
+ destFile.getParentFile().mkdirs();
+ }
+ OutputStream fos = new FileOutputStream(destFile);
+ try {
+ // TODO: use resource as input stream context?
+ URL inputStreamContext = null;
+ if (res instanceof URLResource) {
+ inputStreamContext = ((URLResource) res).getURL();
+ } else if (res instanceof FileResource) {
+ inputStreamContext = ((FileResource) res).getFile().toURI().toURL();
+ }
+ update(inputStreamContext, in, fos, options);
+ } finally {
+ try {
+ in.close();
+ } catch (IOException e) {
+ Message.warn("failed to close a stream : " + e.toString());
+ }
+ try {
+ fos.close();
+ } catch (IOException e) {
+ Message.warn("failed to close a stream : " + e.toString());
+ }
+ }
+ }
+
+ private static class UpdaterHandler extends DefaultHandler implements LexicalHandler {
+
+ /** standard attributes of ivy-module/info */
+ private static final Collection STD_ATTS = Arrays.asList(new String[] {"organisation",
+ "module", "branch", "revision", "status", "publication", "namespace"});
+
+ /** elements that may appear inside ivy-module, in expected order */
+ private static final List MODULE_ELEMENTS = Arrays.asList(new String[] {"info",
+ "configurations", "publications", "dependencies", "conflicts"});
+
+ /** element position of "configurations" inside "ivy-module" */
+ private static final int CONFIGURATIONS_POSITION = MODULE_ELEMENTS
+ .indexOf("configurations");
+
+ /** element position of "dependencies" inside "ivy-module" */
+ private static final int DEPENDENCIES_POSITION = MODULE_ELEMENTS.indexOf("dependencies");
+
+ /** elements that may appear inside of ivy-module/info */
+ private static final Collection INFO_ELEMENTS = Arrays.asList(new String[] {"extends",
+ "ivyauthor", "license", "repository", "description"});
+
+ private final ParserSettings settings;
+
+ private final PrintWriter out;
+
+ private final Map resolvedRevisions;
+
+ private final Map resolvedBranches;
+
+ private final String status;
+
+ private final String revision;
+
+ private final Date pubdate;
+
+ private final Namespace ns;
+
+ private final boolean replaceInclude;
+
+ private final boolean generateRevConstraint;
+
+ private boolean inHeader = true;
+
+ private final List confs;
+
+ private final URL relativePathCtx;
+
+ private final UpdateOptions options;
+
+ public UpdaterHandler(URL relativePathCtx, PrintWriter out, final UpdateOptions options) {
+ this.options = options;
+ this.settings = options.getSettings();
+ this.out = out;
+ this.resolvedRevisions = options.getResolvedRevisions();
+ this.resolvedBranches = options.getResolvedBranches();
+ this.status = options.getStatus();
+ this.revision = options.getRevision();
+ this.pubdate = options.getPubdate();
+ this.ns = options.getNamespace();
+ this.replaceInclude = options.isReplaceInclude();
+ this.generateRevConstraint = options.isGenerateRevConstraint();
+ this.relativePathCtx = relativePathCtx;
+ if (options.getConfsToExclude() != null) {
+ this.confs = Arrays.asList(options.getConfsToExclude());
+ } else {
+ this.confs = Collections.EMPTY_LIST;
+ }
+ }
+
+ // never print *ln* cause \n is found in copied characters stream
+ // nor do we need do handle indentation, original one is maintained except for attributes
+
+ private String organisation = null;
+
+ // defaultConfMapping of imported configurations, if any
+ private String defaultConfMapping = null;
+
+ // confMappingOverride of imported configurations, if any
+ private Boolean confMappingOverride = null;
+
+ // used to know if the last open tag was empty, to adjust termination
+ // with /> instead of ></qName>
+ private String justOpen = null;
+
+ // track the size of the left indent, so that inserted elements are formatted
+ // like nearby elements.
+
+ // true when we're reading indent whitespace
+ private boolean indenting;
+
+ private StringBuffer currentIndent = new StringBuffer();
+
+ private ArrayList indentLevels = new ArrayList(); // ArrayList<String>
+
+ // true if an ivy-module/info/description element has been found in the published descriptor
+ private boolean hasDescription = false;
+
+ // true if merged configurations have been written
+ private boolean mergedConfigurations = false;
+
+ // true if merged deps have been written
+ private boolean mergedDependencies = false;
+
+ // the new value of the defaultconf attribute on the publications tag
+ private String newDefaultConf = null;
+
+ private Stack context = new Stack();
+
+ private Stack buffers = new Stack();
+
+ private Stack confAttributeBuffers = new Stack();
+
+ public void startElement(String uri, String localName, String qName, Attributes attributes)
+ throws SAXException {
+ inHeader = false;
+ endIndent();
+ if (justOpen != null) {
+ write(">");
+ }
+
+ flushMergedElementsBefore(qName);
+
+ // according to ivy.xsd, all <dependency> elements must occur before
+ // the <exclude>, <override> or <conflict> elements
+ if (options.isMerge()
+ && ("exclude".equals(localName) || "override".equals(localName) || "conflict"
+ .equals(localName)) && "ivy-module/dependencies".equals(getContext())) {
+ ModuleDescriptor merged = options.getMergedDescriptor();
+ writeInheritedDependencies(merged);
+ out.println();
+ out.print(getIndent());
+ }
+
+ context.push(qName);
+
+ String path = getContext();
+ if ("info".equals(qName)) {
+ infoStarted(attributes);
+ } else if (replaceInclude && "include".equals(qName)
+ && context.contains("configurations")) {
+ // TODO, in the case of !replaceInclude, we should still replace the relative path
+ // by an absolute path.
+ includeStarted(attributes);
+ } else if ("ivy-module/info/extends".equals(path)) {
+ startExtends(attributes);
+ } else if ("ivy-module/dependencies/dependency".equals(path)) {
+ startElementInDependency(attributes);
+ } else if ("dependencies".equals(qName)) {
+ startDependencies(attributes);
+ } else if ("ivy-module/configurations/conf".equals(path)) {
+ startElementInConfigurationsConf(qName, attributes);
+ } else if ("ivy-module/publications/artifact/conf".equals(path)
+ || "ivy-module/dependencies/dependency/conf".equals(path)
+ || "ivy-module/dependencies/dependency/artifact/conf".equals(path)) {
+ buffers.push(new ExtendedBuffer(getContext()));
+ ((ExtendedBuffer) confAttributeBuffers.peek()).setDefaultPrint(false);
+ String confName = substitute(settings, attributes.getValue("name"));
+ if (!confs.contains(confName)) {
+ ((ExtendedBuffer) confAttributeBuffers.peek()).setPrint(true);
+ ((ExtendedBuffer) buffers.peek()).setPrint(true);
+ write("<" + qName);
+ for (int i = 0; i < attributes.getLength(); i++) {
+ write(" " + attributes.getQName(i) + "=\""
+ + substitute(settings, attributes.getValue(i)) + "\"");
+ }
+ }
+ } else if ("ivy-module/publications/artifact".equals(path)) {
+ ExtendedBuffer buffer = new ExtendedBuffer(getContext());
+ buffers.push(buffer);
+ confAttributeBuffers.push(buffer);
+ write("<" + qName);
+ buffer.setDefaultPrint(attributes.getValue("conf") == null
+ && ((newDefaultConf == null) || (newDefaultConf.length() > 0)));
+ for (int i = 0; i < attributes.getLength(); i++) {
+ String attName = attributes.getQName(i);
+ if ("conf".equals(attName)) {
+ String confName = substitute(settings, attributes.getValue("conf"));
+ String newConf = removeConfigurationsFromList(confName, confs);
+ if (newConf.length() > 0) {
+ write(" " + attributes.getQName(i) + "=\"" + newConf + "\"");
+ ((ExtendedBuffer) buffers.peek()).setPrint(true);
+ }
+ } else {
+ write(" " + attributes.getQName(i) + "=\""
+ + substitute(settings, attributes.getValue(i)) + "\"");
+ }
+ }
+ } else if ("ivy-module/dependencies/dependency/artifact".equals(path)) {
+ ExtendedBuffer buffer = new ExtendedBuffer(getContext());
+ buffers.push(buffer);
+ confAttributeBuffers.push(buffer);
+ write("<" + qName);
+ buffer.setDefaultPrint(attributes.getValue("conf") == null);
+ for (int i = 0; i < attributes.getLength(); i++) {
+ String attName = attributes.getQName(i);
+ if ("conf".equals(attName)) {
+ String confName = substitute(settings, attributes.getValue("conf"));
+ String newConf = removeConfigurationsFromList(confName, confs);
+ if (newConf.length() > 0) {
+ write(" " + attributes.getQName(i) + "=\"" + newConf + "\"");
+ ((ExtendedBuffer) buffers.peek()).setPrint(true);
+ }
+ } else {
+ write(" " + attributes.getQName(i) + "=\""
+ + substitute(settings, attributes.getValue(i)) + "\"");
+ }
+ }
+ } else if ("ivy-module/publications".equals(path)) {
+ startPublications(attributes);
+ } else {
+ if (options.isMerge() && path.startsWith("ivy-module/info")) {
+ ModuleDescriptor merged = options.getMergedDescriptor();
+ if (path.equals("ivy-module/info/description")) {
+ // if the descriptor already contains a description, don't bother printing
+ // the merged version.
+ hasDescription = true;
+ } else if (!INFO_ELEMENTS.contains(qName)) {
+ // according to the XSD, we should write description after all of the other
+ // standard <info> elements but before any extended elements.
+ writeInheritedDescription(merged);
+ }
+ }
+
+ // copy
+ write("<" + qName);
+ for (int i = 0; i < attributes.getLength(); i++) {
+ write(" " + attributes.getQName(i) + "=\""
+ + substitute(settings, attributes.getValue(i)) + "\"");
+ }
+ }
+ justOpen = qName;
+ // indent.append("\t");
+ }
+
+ private void startExtends(Attributes attributes) {
+ // in merge mode, comment out extends element
+ if (options.isMerge()) {
+ write("<!-- ");
+ }
+ write("<extends");
+
+ String org = substitute(settings, attributes.getValue("organisation"));
+ String module = substitute(settings, attributes.getValue("module"));
+ ModuleId parentId = new ModuleId(org, module);
+
+ for (int i = 0; i < attributes.getLength(); i++) {
+ String name = attributes.getQName(i);
+ String value = null;
+
+ if ("revision".equals(name)) {
+ // replace inline revision with resolved parent revision
+ ModuleDescriptor merged = options.getMergedDescriptor();
+ if (merged != null) {
+ ExtendsDescriptor[] parents = merged.getInheritedDescriptors();
+ for (int j = 0; value == null && j < parents.length; ++j) {
+ ModuleRevisionId resolvedId = parents[j].getResolvedParentRevisionId();
+ if (parentId.equals(resolvedId.getModuleId())) {
+ value = resolvedId.getRevision();
+ }
+ }
+ }
+ if (value == null) {
+ value = substitute(settings, attributes.getValue(i));
+ }
+ } else if ("organisation".equals(name)) {
+ value = org;
+ } else if ("module".equals(name)) {
+ value = module;
+ } else {
+ value = substitute(settings, attributes.getValue(i));
+ }
+ write(" " + name + "=\"" + value + "\"");
+ }
+ }
+
+ private void startElementInConfigurationsConf(String qName, Attributes attributes) {
+ buffers.push(new ExtendedBuffer(getContext()));
+ String confName = substitute(settings, attributes.getValue("name"));
+ if (!confs.contains(confName)) {
+ ((ExtendedBuffer) buffers.peek()).setPrint(true);
+ String extend = substitute(settings, attributes.getValue("extends"));
+ if (extend != null) {
+ for (StringTokenizer tok = new StringTokenizer(extend, ", "); tok
+ .hasMoreTokens();) {
+ String current = tok.nextToken();
+ if (confs.contains(current)) {
+ throw new IllegalArgumentException(
+ "Cannot exclude a configuration which is extended.");
+ }
+ }
+ }
+
+ write("<" + qName);
+ for (int i = 0; i < attributes.getLength(); i++) {
+ write(" " + attributes.getQName(i) + "=\""
+ + substitute(settings, attributes.getValue(i)) + "\"");
+ }
+ }
+ }
+
+ private void startDependencies(Attributes attributes) {
+ // copy
+ write("<dependencies");
+ for (int i = 0; i < attributes.getLength(); i++) {
+ String attName = attributes.getQName(i);
+ if ("defaultconfmapping".equals(attName)) {
+ String newMapping = removeConfigurationsFromMapping(
+ substitute(settings, attributes.getValue("defaultconfmapping")), confs);
+ if (newMapping.length() > 0) {
+ write(" " + attributes.getQName(i) + "=\"" + newMapping + "\"");
+ }
+ } else {
+ write(" " + attributes.getQName(i) + "=\""
+ + substitute(settings, attributes.getValue(i)) + "\"");
+ }
+ }
+ // add default conf mapping if needed
+ if (defaultConfMapping != null && attributes.getValue("defaultconfmapping") == null) {
+ String newMapping = removeConfigurationsFromMapping(defaultConfMapping, confs);
+ if (newMapping.length() > 0) {
+ write(" defaultconfmapping=\"" + newMapping + "\"");
+ }
+ }
+ // add confmappingoverride if needed
+ if (confMappingOverride != null && attributes.getValue("confmappingoverride") == null) {
+ write(" confmappingoverride=\"" + confMappingOverride.toString() + "\"");
+ }
+ }
+
+ private void startPublications(Attributes attributes) {
+ write("<publications");
+ for (int i = 0; i < attributes.getLength(); i++) {
+ String attName = attributes.getQName(i);
+ if ("defaultconf".equals(attName)) {
+ newDefaultConf = removeConfigurationsFromList(
+ substitute(settings, attributes.getValue("defaultconf")), confs);
+ if (newDefaultConf.length() > 0) {
+ write(" " + attributes.getQName(i) + "=\"" + newDefaultConf + "\"");
+ }
+ } else {
+ write(" " + attributes.getQName(i) + "=\""
+ + substitute(settings, attributes.getValue(i)) + "\"");
+ }
+ }
+ }
+
+ private void startElementInDependency(Attributes attributes) {
+ ExtendedBuffer buffer = new ExtendedBuffer(getContext());
+ buffers.push(buffer);
+ confAttributeBuffers.push(buffer);
+ buffer.setDefaultPrint(attributes.getValue("conf") == null
+ || attributes.getValue("conf").trim().length() == 0);
+ write("<dependency");
+ String org = substitute(settings, attributes.getValue("org"));
+ org = org == null ? organisation : org;
+ String module = substitute(settings, attributes.getValue("name"));
+ String branch = substitute(settings, attributes.getValue("branch"));
+ String branchConstraint = substitute(settings, attributes.getValue("branchConstraint"));
+ branchConstraint = branchConstraint == null ? branch : branchConstraint;
+
+ // look for the branch used in resolved revisions
+ if (branch == null) {
+ ModuleId mid = ModuleId.newInstance(org, module);
+ if (ns != null) {
+ mid = NameSpaceHelper.transform(mid, ns.getToSystemTransformer());
+ }
+ for (Iterator iter = resolvedRevisions.keySet().iterator(); iter.hasNext();) {
+ ModuleRevisionId mrid = (ModuleRevisionId) iter.next();
+ if (mrid.getModuleId().equals(mid)) {
+ branch = mrid.getBranch();
+ break;
+ }
+ }
+ }
+
+ String revision = substitute(settings, attributes.getValue("rev"));
+ String revisionConstraint = substitute(settings, attributes.getValue("revConstraint"));
+ Map extraAttributes = ExtendableItemHelper.getExtraAttributes(settings, attributes,
+ XmlModuleDescriptorParser.DEPENDENCY_REGULAR_ATTRIBUTES);
+ ModuleRevisionId localMrid = ModuleRevisionId.newInstance(org, module, branch,
+ revision, extraAttributes);
+ ModuleRevisionId systemMrid = ns == null ? localMrid : ns.getToSystemTransformer()
+ .transform(localMrid);
+
+ String newBranch = (String) resolvedBranches.get(systemMrid);
+
+ for (int i = 0; i < attributes.getLength(); i++) {
+ String attName = attributes.getQName(i);
+ if ("rev".equals(attName)) {
+ String rev = (String) resolvedRevisions.get(systemMrid);
+ if (rev != null) {
+ write(" rev=\"" + rev + "\"");
+ if (attributes.getIndex("branchConstraint") == -1
+ && branchConstraint != null) {
+ write(" branchConstraint=\"" + branchConstraint + "\"");
+ }
+ if (generateRevConstraint && attributes.getIndex("revConstraint") == -1
+ && !rev.equals(systemMrid.getRevision())) {
+ write(" revConstraint=\"" + systemMrid.getRevision() + "\"");
+ }
+ } else {
+ write(" rev=\"" + systemMrid.getRevision() + "\"");
+ }
+ } else if ("revConstraint".equals(attName)) {
+ write(" revConstraint=\"" + revisionConstraint + "\"");
+ } else if ("org".equals(attName)) {
+ write(" org=\"" + systemMrid.getOrganisation() + "\"");
+ } else if ("name".equals(attName)) {
+ write(" name=\"" + systemMrid.getName() + "\"");
+ } else if ("branch".equals(attName)) {
+ if (newBranch != null) {
+ write(" branch=\"" + newBranch + "\"");
+ } else if (!resolvedBranches.containsKey(systemMrid)) {
+ write(" branch=\"" + systemMrid.getBranch() + "\"");
+ } else {
+ // if resolvedBranches contains the systemMrid, but the new branch is null,
+ // the branch attribute will be removed altogether
+ }
+ } else if ("branchConstraint".equals(attName)) {
+ write(" branchConstraint=\"" + branchConstraint + "\"");
+ } else if ("conf".equals(attName)) {
+ String oldMapping = substitute(settings, attributes.getValue("conf"));
+ if (oldMapping.length() > 0) {
+ String newMapping = removeConfigurationsFromMapping(oldMapping, confs);
+ if (newMapping.length() > 0) {
+ write(" conf=\"" + newMapping + "\"");
+ ((ExtendedBuffer) buffers.peek()).setPrint(true);
+ }
+ }
+ } else {
+ write(" " + attName + "=\""
+ + substitute(settings, attributes.getValue(attName)) + "\"");
+ }
+ }
+
+ if (attributes.getIndex("branch") == -1) {
+ if (newBranch != null) {
+ // erase an existing branch attribute if its new value is blank
+ if (!newBranch.trim().equals("")) {
+ write(" branch=\"" + newBranch + "\"");
+ }
+ } else if (options.isUpdateBranch() && systemMrid.getBranch() != null) {
+ // this dependency is on a specific branch, we set it explicitly in the updated
+ // file
+ write(" branch=\"" + systemMrid.getBranch() + "\"");
+ }
+ }
+ }
+
+ private void includeStarted(Attributes attributes) throws SAXException {
+ final ExtendedBuffer buffer = new ExtendedBuffer(getContext());
+ buffers.push(buffer);
+ try {
+ URL url;
+ if (settings != null) {
+ url = settings.getRelativeUrlResolver().getURL(relativePathCtx,
+ settings.substitute(attributes.getValue("file")),
+ settings.substitute(attributes.getValue("url")));
+ } else {
+ // TODO : settings can be null, but I don't why.
+ // Check if the next code is correct in that case
+ String fileName = attributes.getValue("file");
+ if (fileName == null) {
+ String urlStr = attributes.getValue("url");
+ url = new URL(urlStr);
+ } else {
+ url = Checks.checkAbsolute(fileName, "settings.include").toURI().toURL();
+ }
+ }
+ XMLHelper.parse(url, null, new DefaultHandler() {
+ private boolean insideConfigurations = false;
+
+ private boolean doIndent = false;
+
+ public void startElement(String uri, String localName, String qName,
+ Attributes attributes) throws SAXException {
+ if ("configurations".equals(qName)) {
+ insideConfigurations = true;
+ String defaultconf = substitute(settings,
+ attributes.getValue("defaultconfmapping"));
+ if (defaultconf != null) {
+ defaultConfMapping = defaultconf;
+ }
+ String mappingOverride = substitute(settings,
+ attributes.getValue("confmappingoverride"));
+ if (mappingOverride != null) {
+ confMappingOverride = Boolean.valueOf(mappingOverride);
+ }
+ } else if ("conf".equals(qName) && insideConfigurations) {
+ String confName = substitute(settings, attributes.getValue("name"));
+ if (!confs.contains(confName)) {
+ buffer.setPrint(true);
+ if (doIndent) {
+ write("/>\n\t\t");
+ }
+ String extend = substitute(settings, attributes.getValue("extends"));
+ if (extend != null) {
+ for (StringTokenizer tok = new StringTokenizer(extend, ", "); tok
+ .hasMoreTokens();) {
+ String current = tok.nextToken();
+ if (confs.contains(current)) {
+ throw new IllegalArgumentException("Cannot exclude a "
+ + "configuration which is extended.");
+ }
+ }
+
+ }
+
+ write("<" + qName);
+ for (int i = 0; i < attributes.getLength(); i++) {
+ write(" " + attributes.getQName(i) + "=\""
+ + substitute(settings, attributes.getValue(i)) + "\"");
+ }
+ doIndent = true;
+ }
+ }
+ }
+
+ public void endElement(String uri, String localName, String name)
+ throws SAXException {
+ if ("configurations".equals(name)) {
+ insideConfigurations = false;
+ }
+ }
+ });
+ } catch (Exception e) {
+ Message.warn("exception occurred while importing configurations: " + e.getMessage());
+ throw new SAXException(e);
+ }
+ }
+
+ private void infoStarted(Attributes attributes) {
+
+ String module = substitute(settings, attributes.getValue("module"));
+ String rev = null;
+ String branch = null;
+ String status = null;
+ String namespace = null;
+ Map/* <String,String> */extraAttributes = null;
+
+ if (options.isMerge()) {
+ // get attributes from merged descriptor, ignoring raw XML
+ ModuleDescriptor merged = options.getMergedDescriptor();
+ ModuleRevisionId mergedMrid = merged.getModuleRevisionId();
+ organisation = mergedMrid.getOrganisation();
+ branch = mergedMrid.getBranch();
+ rev = mergedMrid.getRevision();
+ status = merged.getStatus();
+
+ // TODO: should namespace be added to ModuleDescriptor interface, so we don't
+ // have to do this kind of check?
+ if (merged instanceof DefaultModuleDescriptor) {
+ Namespace ns = ((DefaultModuleDescriptor) merged).getNamespace();
+ if (ns != null) {
+ namespace = ns.getName();
+ }
+ }
+ if (namespace == null) {
+ namespace = attributes.getValue("namespace");
+ }
+
+ extraAttributes = merged.getQualifiedExtraAttributes();
+ } else {
+ // get attributes from raw XML, performing property substitution
+ organisation = substitute(settings, attributes.getValue("organisation"));
+ rev = substitute(settings, attributes.getValue("revision"));
+ branch = substitute(settings, attributes.getValue("branch"));
+ status = substitute(settings, attributes.getValue("status"));
+ namespace = substitute(settings, attributes.getValue("namespace"));
+ extraAttributes = new LinkedHashMap(attributes.getLength());
+ for (int i = 0; i < attributes.getLength(); i++) {
+ String qname = attributes.getQName(i);
+ if (!STD_ATTS.contains(qname)) {
+ extraAttributes.put(qname, substitute(settings, attributes.getValue(i)));
+ }
+ }
+ }
+
+ // apply override values provided in options
+ if (revision != null) {
+ rev = revision;
+ }
+ if (options.getBranch() != null) {
+ branch = options.getBranch();
+ }
+ if (this.status != null) {
+ status = this.status;
+ }
+
+ // if necessary translate mrid using optional namespace argument
+ ModuleRevisionId localMid = ModuleRevisionId.newInstance(organisation, module, branch,
+ rev, ExtendableItemHelper
+ .getExtraAttributes(settings, attributes, new String[] {"organisation",
+ "module", "revision", "status", "publication", "namespace"}));
+ ModuleRevisionId systemMid = ns == null ? localMid : ns.getToSystemTransformer()
+ .transform(localMid);
+
+ write("<info");
+ if (organisation != null) {
+ write(" organisation=\"" + XMLHelper.escape(systemMid.getOrganisation()) + "\"");
+ }
+ write(" module=\"" + XMLHelper.escape(systemMid.getName()) + "\"");
+ if (branch != null) {
+ write(" branch=\"" + XMLHelper.escape(systemMid.getBranch()) + "\"");
+ }
+ if (systemMid.getRevision() != null) {
+ write(" revision=\"" + XMLHelper.escape(systemMid.getRevision()) + "\"");
+ }
+ write(" status=\"" + XMLHelper.escape(status) + "\"");
+ if (pubdate != null) {
+ write(" publication=\"" + DateUtil.format(pubdate) + "\"");
+ } else if (attributes.getValue("publication") != null) {
+ write(" publication=\"" + substitute(settings, attributes.getValue("publication"))
+ + "\"");
+ }
+ if (namespace != null) {
+ write(" namespace=\"" + namespace + "\"");
+ }
+
+ for (Iterator extras = extraAttributes.entrySet().iterator(); extras.hasNext();) {
+ Map.Entry extra = (Map.Entry) extras.next();
+ write(" " + extra.getKey() + "=\"" + extra.getValue() + "\"");
+ }
+ }
+
+ private void write(String content) {
+ getWriter().print(content);
+ }
+
+ private PrintWriter getWriter() {
+ return buffers.isEmpty() ? out : ((ExtendedBuffer) buffers.peek()).getWriter();
+ }
+
+ private String getContext() {
+ StringBuffer buf = new StringBuffer();
+ for (Iterator iter = context.iterator(); iter.hasNext();) {
+ String ctx = (String) iter.next();
+ buf.append(ctx).append("/");
+ }
+ if (buf.length() > 0) {
+ buf.setLength(buf.length() - 1);
+ }
+ return buf.toString();
+ }
+
+ private String substitute(ParserSettings ivy, String value) {
+ String result = ivy == null ? value : ivy.substitute(value);
+ return XMLHelper.escape(result);
+ }
+
+ private String removeConfigurationsFromMapping(String mapping, List confsToRemove) {
+ StringBuffer newMapping = new StringBuffer();
+ String mappingSep = "";
+ for (StringTokenizer tokenizer = new StringTokenizer(mapping, ";"); tokenizer
+ .hasMoreTokens();) {
+ String current = tokenizer.nextToken();
+ String[] ops = current.split("->");
+ String[] lhs = ops[0].split(",");
+ List confsToWrite = new ArrayList();
+ for (int j = 0; j < lhs.length; j++) {
+ if (!confs.contains(lhs[j].trim())) {
+ confsToWrite.add(lhs[j]);
+ }
+ }
+ if (!confsToWrite.isEmpty()) {
+ newMapping.append(mappingSep);
+
+ String sep = "";
+ for (Iterator it = confsToWrite.iterator(); it.hasNext();) {
+ newMapping.append(sep);
+ newMapping.append(it.next());
+ sep = ",";
+ }
+ if (ops.length == 2) {
+ newMapping.append("->");
+ newMapping.append(ops[1]);
+ }
+ mappingSep = ";";
+ }
+ }
+
+ return newMapping.toString();
+ }
+
+ private String removeConfigurationsFromList(String list, List confsToRemove) {
+ StringBuffer newList = new StringBuffer();
+ String listSep = "";
+ for (StringTokenizer tokenizer = new StringTokenizer(list, ","); tokenizer
+ .hasMoreTokens();) {
+ String current = tokenizer.nextToken();
+ if (!confsToRemove.contains(current.trim())) {
+ newList.append(listSep);
+ newList.append(current);
+ listSep = ",";
+ }
+ }
+
+ return newList.toString();
+ }
+
+ public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {
+ characters(ch, start, length);
+ }
+
+ public void characters(char[] ch, int start, int length) throws SAXException {
+ if (justOpen != null) {
+ write(">");
+ justOpen = null;
+ }
+ write(XMLHelper.escape(String.valueOf(ch, start, length)));
+
+ // examine characters for current indent level, keeping in mind
+ // that our indent might be split across multiple calls to characters()
+ for (int i = start, end = start + length; i < end; ++i) {
+ char c = ch[i];
+ if (c == '\r' || c == '\n') {
+ // newline resets the indent level
+ currentIndent.setLength(0);
+ indenting = true;
+ } else if (indenting) {
+ // indent continues until first non-whitespace character
+ if (Character.isWhitespace(c)) {
+ currentIndent.append(c);
+ } else {
+ endIndent();
+ }
+ }
+ }
+ }
+
+ /** record the current indent level for future elements that appear at the same depth */
+ private void endIndent() {
+ if (indenting) {
+ // record the indent at this level. if we insert any elements at
+ // this level, we'll use the same indent.
+ setIndent(context.size() - 1, currentIndent.toString());
+ indenting = false;
+ }
+ }
+
+ /**
+ * Set the indent for the given depth. Indents less than the provided depth will be
+ * calculated automatically, if they have not already been defined.
+ */
+ private void setIndent(int level, String indent) {
+ fillIndents(level);
+ indentLevels.set(level, indent);
+ }
+
+ /**
+ * Guarantee that indent levels have been calculated up to and including the given depth
+ * (starting at 0).
+ */
+ private void fillIndents(int level) {
+ if (indentLevels.isEmpty()) {
+ // add a default single-level indent until we see indents in the document
+ indentLevels.add(" ");
+ }
+ String oneLevel = (String) indentLevels.get(0);
+ for (int fill = indentLevels.size(); fill <= level; ++fill) {
+ indentLevels.add(indentLevels.get(fill - 1) + oneLevel);
+ }
+ }
+
+ /** get the whitespace that should precede new elements at the current depth in the document */
+ private String getIndent() {
+ int level = context.size() - 1;
+ fillIndents(level);
+ return (String) indentLevels.get(level);
+ }
+
+ /**
+ * Write XML elements that do not appear in the source descriptor, but have been copied in
+ * from a parent module descriptor via <extends> declaration.
+ *
+ * @param merged
+ * child descriptor containing the merged data
+ * @param items
+ * the list of inherited items to print
+ * @param printer
+ * a printer that knows how to write the given type of item
+ * @param itemName
+ * the name of the container element, e.g. "configurations"
+ * @param includeContainer
+ * if true, include an enclosing element named <code>itemName</code>. Otherwise
+ * just write the inherited items inline, with a comment indicating where they
+ * came from.
+ */
+ private void writeInheritedItems(ModuleDescriptor merged, InheritableItem[] items,
+ ItemPrinter printer, String itemName, boolean includeContainer) {
+ // first categorize inherited items by their source module, so that
+ // we can add some useful comments
+ PrintWriter out = getWriter();
+
+ Map inheritedItems = collateInheritedItems(merged, items);
+ boolean hasItems = !inheritedItems.isEmpty();
+
+ if (hasItems && includeContainer) {
+ if (currentIndent.length() == 0) {
+ out.print(getIndent());
+ }
+ out.print("<" + itemName + ">");
+ context.push(itemName);
+ justOpen = null;
+ }
+
+ for (Iterator parents = inheritedItems.entrySet().iterator(); parents.hasNext();) {
+ Map.Entry entry = (Map.Entry) parents.next();
+ ModuleRevisionId parent = (ModuleRevisionId) entry.getKey();
+ List list = (List) entry.getValue();
+
+ if (justOpen != null) {
+ out.println(">");
+ justOpen = null; // helps endElement() decide how to write close tags
+ }
+ writeInheritanceComment(itemName, parent);
+ for (int c = 0; c < list.size(); ++c) {
+ InheritableItem item = (InheritableItem) list.get(c);
+ out.print(getIndent());
+ printer.print(merged, item, out);
+ }
+ }
+
+ if (hasItems) {
+ if (includeContainer) {
+ context.pop();
+ out.println(getIndent() + "</" + itemName + ">");
+ out.println();
+ }
+ // restore the prior indent
+ out.print(currentIndent);
+ }
+ }
+
+ private void writeInheritanceComment(String itemDescription, Object parentInfo) {
+ PrintWriter out = getWriter();
+ out.println();
+ out.println(getIndent() + "<!-- " + itemDescription + " inherited from " + parentInfo
+ + " -->");
+ }
+
+ /**
+ * Collect the given list of inherited descriptor items into lists keyed by parent Id. Thus
+ * all of the items inherited from parent A can be written together, then all of the items
+ * from parent B, and so on.
+ *
+ * @param merged
+ * the merged child descriptor
+ * @param items
+ * the inherited items to collate
+ * @return maps parent ModuleRevisionId to a List of InheritedItems imported from that
+ * parent
+ */
+ private Map/* <ModuleRevisionId,List> */collateInheritedItems(ModuleDescriptor merged,
+ InheritableItem[] items) {
+ LinkedHashMap/* <ModuleRevisionId,List> */inheritedItems = new LinkedHashMap();
+ for (int i = 0; i < items.length; ++i) {
+ ModuleRevisionId source = items[i].getSourceModule();
+ // ignore items that are defined directly in the child descriptor
+ if (source != null
+ && !source.getModuleId().equals(merged.getModuleRevisionId().getModuleId())) {
+ List accum = (List) inheritedItems.get(source);
+ if (accum == null) {
+ accum = new ArrayList();
+ inheritedItems.put(source, accum);
+ }
+ accum.add(items[i]);
+ }
+ }
+ return inheritedItems;
+ }
+
+ /**
+ * If no info/description element has yet been written, write the description inherited from
+ * the parent descriptor, if any. Calling this method more than once has no affect.
+ */
+ private void writeInheritedDescription(ModuleDescriptor merged) {
+ if (!hasDescription) {
+ hasDescription = true;
+ String description = merged.getDescription();
+ if ((description != null) && (description.length() > 0)) {
+ PrintWriter writer = getWriter();
+ if (justOpen != null) {
+ writer.println(">");
+ }
+ writeInheritanceComment("description", "parent");
+ writer.println(getIndent() + "<description>" + XMLHelper.escape(description)
+ + "</description>");
+ // restore the indent that existed before we wrote the extra elements
+ writer.print(currentIndent);
+ justOpen = null;
+ }
+ }
+ }
+
+ private void writeInheritedConfigurations(ModuleDescriptor merged) {
+ if (!mergedConfigurations) {
+ mergedConfigurations = true;
+ writeInheritedItems(merged, merged.getConfigurations(),
+ ConfigurationPrinter.INSTANCE, "configurations", false);
+ }
+ }
+
+ private void writeInheritedDependencies(ModuleDescriptor merged) {
+ if (!mergedDependencies) {
+ mergedDependencies = true;
+ writeInheritedItems(merged, merged.getDependencies(), DependencyPrinter.INSTANCE,
+ "dependencies", false);
+ }
+ }
+
+ /**
+ * <p>
+ * If publishing in merge mode, guarantee that any merged elements appearing before
+ * <code>moduleElement</code> have been written. This method should be called <i>before</i>
+ * we write the start tag of <code>moduleElement</code>. This covers cases where merged
+ * elements like "configurations" and "dependencies" appear in the parent descriptor, but
+ * are completely missing in the child descriptor.
+ * </p>
+ *
+ * <p>
+ * For example, if "moduleElement" is "dependencies", guarantees that "configurations" has
+ * been written. If <code>moduleElement</code> is <code>null</code>, then all missing merged
+ * elements will be flushed.
+ * </p>
+ *
+ * @param moduleElement
+ * a descriptor element name, for example "configurations" or "info"
+ */
+ private void flushMergedElementsBefore(String moduleElement) {
+ if (options.isMerge() && context.size() == 1 && "ivy-module".equals(context.peek())
+ && !(mergedConfigurations && mergedDependencies)) {
+
+ // calculate the position of the element in ivy-module
+ int position = moduleElement == null ? MODULE_ELEMENTS.size() : MODULE_ELEMENTS
+ .indexOf(moduleElement);
+
+ ModuleDescriptor merged = options.getMergedDescriptor();
+
+ // see if we should write <configurations>
+ if (!mergedConfigurations && position > CONFIGURATIONS_POSITION
+ && merged.getConfigurations().length > 0) {
+
+ mergedConfigurations = true;
+ writeInheritedItems(merged, merged.getConfigurations(),
+ ConfigurationPrinter.INSTANCE, "configurations", true);
+
+ }
+ // see if we should write <dependencies>
+ if (!mergedDependencies && position > DEPENDENCIES_POSITION
+ && merged.getDependencies().length > 0) {
+
+ mergedDependencies = true;
+ writeInheritedItems(merged, merged.getDependencies(),
+ DependencyPrinter.INSTANCE, "dependencies", true);
+
+ }
+ }
+ }
+
+ private void flushAllMergedElements() {
+ flushMergedElementsBefore(null);
+ }
+
+ public void endElement(String uri, String localName, String qName) throws SAXException {
+
+ String path = getContext();
+ if (options.isMerge()) {
+ ModuleDescriptor merged = options.getMergedDescriptor();
+
+ if ("ivy-module/info".equals(path)) {
+ // guarantee that inherited description has been written before
+ // info element closes.
+ writeInheritedDescription(merged);
+ } else if ("ivy-module/configurations".equals(path)) {
+ // write inherited configurations after all child configurations
+ writeInheritedConfigurations(merged);
+ } else if ("ivy-module/dependencies".equals(path)) {
+ // write inherited dependencies after all child dependencies
+ writeInheritedDependencies(merged);
+ } else if ("ivy-module".equals(path)) {
+ // write any remaining inherited data before we close the
+ // descriptor.
+ flushAllMergedElements();
+ }
+ }
+
+ if (qName.equals(justOpen)) {
+ write("/>");
+ } else {
+ write("</" + qName + ">");
+ }
+
+ if (!buffers.isEmpty()) {
+ ExtendedBuffer buffer = (ExtendedBuffer) buffers.peek();
+ if (buffer.getContext().equals(path)) {
+ buffers.pop();
+ if (buffer.isPrint()) {
+ write(buffer.toString());
+ }
+ }
+ }
+
+ if (!confAttributeBuffers.isEmpty()) {
+ ExtendedBuffer buffer = (ExtendedBuffer) confAttributeBuffers.peek();
+ if (buffer.getContext().equals(path)) {
+ confAttributeBuffers.pop();
+ }
+ }
+
+ // <extends> element is commented out when in merge mode.
+ if (options.isMerge() && "ivy-module/info/extends".equals(path)) {
+ write(" -->");
+ }
+
+ justOpen = null;
+ context.pop();
+ }
+
+ public void endDocument() throws SAXException {
+ out.print(LINE_SEPARATOR);
+ out.flush();
+ out.close();
+ }
+
+ public void processingInstruction(String target, String data) throws SAXException {
+ write("<?");
+ write(target);
+ write(" ");
+ write(data);
+ write("?>");
+ write(LINE_SEPARATOR);
+ }
+
+ public void warning(SAXParseException e) throws SAXException {
+ throw e;
+ }
+
+ public void error(SAXParseException e) throws SAXException {
+ throw e;
+ }
+
+ public void fatalError(SAXParseException e) throws SAXException {
+ throw e;
+ }
+
+ public void endCDATA() throws SAXException {
+ }
+
+ public void endDTD() throws SAXException {
+ }
+
+ public void startCDATA() throws SAXException {
+ }
+
+ public void comment(char[] ch, int start, int length) throws SAXException {
+ if (justOpen != null) {
+ write(">");
+ justOpen = null;
+ }
+
+ StringBuffer comment = new StringBuffer();
+ comment.append(ch, start, length);
+ write("<!--");
+ write(comment.toString());
+ write("-->");
+
+ if (inHeader) {
+ write(LINE_SEPARATOR);
+ }
+ }
+
+ public void endEntity(String name) throws SAXException {
+ }
+
+ public void startEntity(String name) throws SAXException {
+ }
+
+ public void startDTD(String name, String publicId, String systemId) throws SAXException {
+ }
+
+ }
+
+ public static void update(URL inStreamCtx, InputStream inStream, OutputStream outStream,
+ final UpdateOptions options) throws IOException, SAXException {
+ final PrintWriter out = new PrintWriter(new OutputStreamWriter(outStream, "UTF-8"));
+ out.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
+ out.write(LINE_SEPARATOR);
+
+ try {
+ UpdaterHandler updaterHandler = new UpdaterHandler(inStreamCtx, out, options);
+ InputSource inSrc = new InputSource(new BufferedInputStream(inStream));
+ if (inStreamCtx != null) {
+ inSrc.setSystemId(inStreamCtx.toExternalForm());
+ }
+ XMLHelper.parse(inSrc, null, updaterHandler, updaterHandler);
+ } catch (ParserConfigurationException e) {
+ IllegalStateException ise = new IllegalStateException(
+ "impossible to update Ivy files: parser problem");
+ ise.initCause(e);
+ throw ise;
+ }
+ }
+
+ private static class ExtendedBuffer {
+ private String context = null;
+
+ private Boolean print = null;
+
+ private boolean defaultPrint = false;
+
+ private StringWriter buffer = new StringWriter();
+
+ private PrintWriter writer = new PrintWriter(buffer);
+
+ ExtendedBuffer(String context) {
+ this.context = context;
+ }
+
+ boolean isPrint() {
+ if (print == null) {
+ return defaultPrint;
+ }
+ return print.booleanValue();
+ }
+
+ void setPrint(boolean print) {
+ this.print = Boolean.valueOf(print);
+ }
+
+ void setDefaultPrint(boolean print) {
+ this.defaultPrint = print;
+ }
+
+ PrintWriter getWriter() {
+ return writer;
+ }
+
+ String getContext() {
+ return context;
+ }
+
+ public String toString() {
+ writer.flush();
+ return buffer.toString();
+ }
+ }
+
+ /**
+ * Prints a descriptor item's XML representation
+ */
+ protected static interface ItemPrinter {
+ /**
+ * Print an XML representation of <code>item</code> to <code>out</code>.
+ *
+ * @param parent
+ * the module descriptor containing <code>item</code>
+ * @param item
+ * subcomponent of the descriptor, for example a {@link DependencyDescriptor} or
+ * {@link Configuration}
+ */
+ public void print(ModuleDescriptor parent, Object item, PrintWriter out);
+ }
+
+ protected static class DependencyPrinter implements ItemPrinter {
+
+ public static final DependencyPrinter INSTANCE = new DependencyPrinter();
+
+ public void print(ModuleDescriptor parent, Object item, PrintWriter out) {
+ XmlModuleDescriptorWriter.printDependency(parent, (DependencyDescriptor) item, out);
+ }
+ }
+
+ protected static class ConfigurationPrinter implements ItemPrinter {
+
+ public static final ConfigurationPrinter INSTANCE = new ConfigurationPrinter();
+
+ public void print(ModuleDescriptor parent, Object item, PrintWriter out) {
+ XmlModuleDescriptorWriter.printConfiguration((Configuration) item, out);
+ }
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/parser/xml/XmlModuleDescriptorWriter.java b/src/java/org/apache/ivy/plugins/parser/xml/XmlModuleDescriptorWriter.java
new file mode 100644
index 0000000..2206663
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/parser/xml/XmlModuleDescriptorWriter.java
@@ -0,0 +1,576 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.parser.xml;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.ivy.core.IvyPatternHelper;
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.Configuration;
+import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
+import org.apache.ivy.core.module.descriptor.DependencyArtifactDescriptor;
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.DependencyDescriptorMediator;
+import org.apache.ivy.core.module.descriptor.ExcludeRule;
+import org.apache.ivy.core.module.descriptor.ExtendsDescriptor;
+import org.apache.ivy.core.module.descriptor.ExtraInfoHolder;
+import org.apache.ivy.core.module.descriptor.IncludeRule;
+import org.apache.ivy.core.module.descriptor.License;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.descriptor.OverrideDependencyDescriptorMediator;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.plugins.matcher.MapMatcher;
+import org.apache.ivy.util.DateUtil;
+import org.apache.ivy.util.Message;
+import org.apache.ivy.util.StringUtils;
+import org.apache.ivy.util.XMLHelper;
+import org.apache.ivy.util.extendable.ExtendableItem;
+
+/**
+ *
+ */
+public final class XmlModuleDescriptorWriter {
+
+ private XmlModuleDescriptorWriter() {
+ // Utility class
+ }
+
+ public static void write(ModuleDescriptor md, File output) throws IOException {
+ write(md, null, output);
+ }
+
+ public static void write(ModuleDescriptor md, String licenseHeader, File output)
+ throws IOException {
+ if (output.getParentFile() != null) {
+ output.getParentFile().mkdirs();
+ }
+ PrintWriter out = new PrintWriter(new OutputStreamWriter(new FileOutputStream(output),
+ "UTF-8"));
+ try {
+ out.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
+ if (licenseHeader != null) {
+ out.print(licenseHeader);
+ }
+ StringBuffer xmlNamespace = new StringBuffer();
+ Map namespaces = md.getExtraAttributesNamespaces();
+ for (Iterator iter = namespaces.entrySet().iterator(); iter.hasNext();) {
+ Entry ns = (Entry) iter.next();
+ xmlNamespace.append(" xmlns:").append(ns.getKey()).append("=\"")
+ .append(ns.getValue()).append("\"");
+ }
+
+ String version = "2.0";
+ if (md.getInheritedDescriptors().length > 0) {
+ version = "2.2";
+ }
+
+ out.println("<ivy-module version=\"" + version + "\"" + xmlNamespace + ">");
+ printInfoTag(md, out);
+ printConfigurations(md, out);
+ printPublications(md, out);
+ printDependencies(md, out);
+ out.println("</ivy-module>");
+ } finally {
+ out.close();
+ }
+ }
+
+ private static void printDependencies(ModuleDescriptor md, PrintWriter out) {
+ DependencyDescriptor[] dds = md.getDependencies();
+ if (dds.length > 0) {
+ out.println("\t<dependencies>");
+ for (int i = 0; i < dds.length; i++) {
+ DependencyDescriptor dep = dds[i];
+ out.print("\t\t");
+ printDependency(md, dep, out);
+ }
+ printAllExcludes(md, out);
+ printAllMediators(md, out);
+ out.println("\t</dependencies>");
+ }
+ }
+
+ protected static void printDependency(ModuleDescriptor md, DependencyDescriptor dep,
+ PrintWriter out) {
+ out.print("<dependency");
+ out.print(" org=\"" + XMLHelper.escape(dep.getDependencyRevisionId().getOrganisation())
+ + "\"");
+ out.print(" name=\"" + XMLHelper.escape(dep.getDependencyRevisionId().getName()) + "\"");
+ if (dep.getDependencyRevisionId().getBranch() != null) {
+ out.print(" branch=\"" + XMLHelper.escape(dep.getDependencyRevisionId().getBranch())
+ + "\"");
+ }
+ out.print(" rev=\"" + XMLHelper.escape(dep.getDependencyRevisionId().getRevision()) + "\"");
+ if (!dep.getDynamicConstraintDependencyRevisionId().equals(dep.getDependencyRevisionId())) {
+ if (dep.getDynamicConstraintDependencyRevisionId().getBranch() != null) {
+ out.print(" branchConstraint=\""
+ + XMLHelper.escape(dep.getDynamicConstraintDependencyRevisionId()
+ .getBranch()) + "\"");
+ }
+ out.print(" revConstraint=\""
+ + XMLHelper
+ .escape(dep.getDynamicConstraintDependencyRevisionId().getRevision())
+ + "\"");
+ }
+ if (dep.isForce()) {
+ out.print(" force=\"" + dep.isForce() + "\"");
+ }
+ if (dep.isChanging()) {
+ out.print(" changing=\"" + dep.isChanging() + "\"");
+ }
+ if (!dep.isTransitive()) {
+ out.print(" transitive=\"" + dep.isTransitive() + "\"");
+ }
+ out.print(" conf=\"");
+ String[] modConfs = dep.getModuleConfigurations();
+ for (int j = 0; j < modConfs.length; j++) {
+ String[] depConfs = dep.getDependencyConfigurations(modConfs[j]);
+ out.print(XMLHelper.escape(modConfs[j]) + "->");
+ for (int k = 0; k < depConfs.length; k++) {
+ out.print(XMLHelper.escape(depConfs[k]));
+ if (k + 1 < depConfs.length) {
+ out.print(",");
+ }
+ }
+ if (j + 1 < modConfs.length) {
+ out.print(";");
+ }
+ }
+ out.print("\"");
+
+ printExtraAttributes(dep, out, " ");
+
+ DependencyArtifactDescriptor[] depArtifacts = dep.getAllDependencyArtifacts();
+ if (depArtifacts.length > 0) {
+ out.println(">");
+ }
+ printDependencyArtefacts(md, out, depArtifacts);
+
+ IncludeRule[] includes = dep.getAllIncludeRules();
+ if (includes.length > 0 && depArtifacts.length == 0) {
+ out.println(">");
+ }
+ printDependencyIncludeRules(md, out, includes);
+
+ ExcludeRule[] excludes = dep.getAllExcludeRules();
+ if (excludes.length > 0 && includes.length == 0 && depArtifacts.length == 0) {
+ out.println(">");
+ }
+ printDependencyExcludeRules(md, out, excludes);
+ if (includes.length + excludes.length + depArtifacts.length == 0) {
+ out.println("/>");
+ } else {
+ out.println("\t\t</dependency>");
+ }
+ }
+
+ private static void printAllMediators(ModuleDescriptor md, PrintWriter out) {
+ Map/* <MapMatcher, DependencyDescriptorMediator> */mediators = md
+ .getAllDependencyDescriptorMediators().getAllRules();
+
+ for (Iterator iterator = mediators.entrySet().iterator(); iterator.hasNext();) {
+ Map.Entry mediatorRule = (Map.Entry) iterator.next();
+ MapMatcher matcher = (MapMatcher) mediatorRule.getKey();
+ DependencyDescriptorMediator mediator = (DependencyDescriptorMediator) mediatorRule
+ .getValue();
+
+ if (mediator instanceof OverrideDependencyDescriptorMediator) {
+ OverrideDependencyDescriptorMediator oddm = (OverrideDependencyDescriptorMediator) mediator;
+
+ out.print("\t\t<override");
+ out.print(" org=\""
+ + XMLHelper.escape((String) matcher.getAttributes().get(
+ IvyPatternHelper.ORGANISATION_KEY)) + "\"");
+ out.print(" module=\""
+ + XMLHelper.escape((String) matcher.getAttributes().get(
+ IvyPatternHelper.MODULE_KEY)) + "\"");
+ out.print(" matcher=\"" + XMLHelper.escape(matcher.getPatternMatcher().getName())
+ + "\"");
+ if (oddm.getBranch() != null) {
+ out.print(" branch=\"" + XMLHelper.escape(oddm.getBranch()) + "\"");
+ }
+ if (oddm.getVersion() != null) {
+ out.print(" rev=\"" + XMLHelper.escape(oddm.getVersion()) + "\"");
+ }
+ out.println("/>");
+ } else {
+ Message.verbose("ignoring unhandled DependencyDescriptorMediator: "
+ + mediator.getClass());
+ }
+ }
+ }
+
+ private static void printAllExcludes(ModuleDescriptor md, PrintWriter out) {
+ ExcludeRule[] excludes = md.getAllExcludeRules();
+ if (excludes.length > 0) {
+ for (int j = 0; j < excludes.length; j++) {
+ out.print("\t\t<exclude");
+ out.print(" org=\""
+ + XMLHelper.escape(excludes[j].getId().getModuleId().getOrganisation())
+ + "\"");
+ out.print(" module=\""
+ + XMLHelper.escape(excludes[j].getId().getModuleId().getName()) + "\"");
+ out.print(" artifact=\"" + XMLHelper.escape(excludes[j].getId().getName()) + "\"");
+ out.print(" type=\"" + XMLHelper.escape(excludes[j].getId().getType()) + "\"");
+ out.print(" ext=\"" + XMLHelper.escape(excludes[j].getId().getExt()) + "\"");
+ String[] ruleConfs = excludes[j].getConfigurations();
+ if (!Arrays.asList(ruleConfs).equals(Arrays.asList(md.getConfigurationsNames()))) {
+ out.print(" conf=\"");
+ for (int k = 0; k < ruleConfs.length; k++) {
+ out.print(XMLHelper.escape(ruleConfs[k]));
+ if (k + 1 < ruleConfs.length) {
+ out.print(",");
+ }
+ }
+ out.print("\"");
+ }
+ out.print(" matcher=\"" + XMLHelper.escape(excludes[j].getMatcher().getName())
+ + "\"");
+ out.println("/>");
+ }
+ }
+ }
+
+ private static void printDependencyExcludeRules(ModuleDescriptor md, PrintWriter out,
+ ExcludeRule[] excludes) {
+ if (excludes.length > 0) {
+ for (int j = 0; j < excludes.length; j++) {
+ out.print("\t\t\t<exclude");
+ out.print(" org=\""
+ + XMLHelper.escape(excludes[j].getId().getModuleId().getOrganisation())
+ + "\"");
+ out.print(" module=\""
+ + XMLHelper.escape(excludes[j].getId().getModuleId().getName()) + "\"");
+ out.print(" name=\"" + XMLHelper.escape(excludes[j].getId().getName()) + "\"");
+ out.print(" type=\"" + XMLHelper.escape(excludes[j].getId().getType()) + "\"");
+ out.print(" ext=\"" + XMLHelper.escape(excludes[j].getId().getExt()) + "\"");
+ String[] ruleConfs = excludes[j].getConfigurations();
+ if (!Arrays.asList(ruleConfs).equals(Arrays.asList(md.getConfigurationsNames()))) {
+ out.print(" conf=\"");
+ for (int k = 0; k < ruleConfs.length; k++) {
+ out.print(XMLHelper.escape(ruleConfs[k]));
+ if (k + 1 < ruleConfs.length) {
+ out.print(",");
+ }
+ }
+ out.print("\"");
+ }
+ out.print(" matcher=\"" + XMLHelper.escape(excludes[j].getMatcher().getName())
+ + "\"");
+ out.println("/>");
+ }
+ }
+ }
+
+ private static void printDependencyIncludeRules(ModuleDescriptor md, PrintWriter out,
+ IncludeRule[] includes) {
+ if (includes.length > 0) {
+ for (int j = 0; j < includes.length; j++) {
+ out.print("\t\t\t<include");
+ out.print(" name=\"" + XMLHelper.escape(includes[j].getId().getName()) + "\"");
+ out.print(" type=\"" + XMLHelper.escape(includes[j].getId().getType()) + "\"");
+ out.print(" ext=\"" + XMLHelper.escape(includes[j].getId().getExt()) + "\"");
+ String[] ruleConfs = includes[j].getConfigurations();
+ if (!Arrays.asList(ruleConfs).equals(Arrays.asList(md.getConfigurationsNames()))) {
+ out.print(" conf=\"");
+ for (int k = 0; k < ruleConfs.length; k++) {
+ out.print(XMLHelper.escape(ruleConfs[k]));
+ if (k + 1 < ruleConfs.length) {
+ out.print(",");
+ }
+ }
+ out.print("\"");
+ }
+ out.print(" matcher=\"" + XMLHelper.escape(includes[j].getMatcher().getName())
+ + "\"");
+ out.println("/>");
+ }
+ }
+ }
+
+ private static void printDependencyArtefacts(ModuleDescriptor md, PrintWriter out,
+ DependencyArtifactDescriptor[] depArtifacts) {
+ if (depArtifacts.length > 0) {
+ for (int j = 0; j < depArtifacts.length; j++) {
+ out.print("\t\t\t<artifact");
+ out.print(" name=\"" + XMLHelper.escape(depArtifacts[j].getName()) + "\"");
+ out.print(" type=\"" + XMLHelper.escape(depArtifacts[j].getType()) + "\"");
+ out.print(" ext=\"" + XMLHelper.escape(depArtifacts[j].getExt()) + "\"");
+ String[] dadconfs = depArtifacts[j].getConfigurations();
+ if (!Arrays.asList(dadconfs).equals(Arrays.asList(md.getConfigurationsNames()))) {
+ out.print(" conf=\"");
+ for (int k = 0; k < dadconfs.length; k++) {
+ out.print(XMLHelper.escape(dadconfs[k]));
+ if (k + 1 < dadconfs.length) {
+ out.print(",");
+ }
+ }
+ out.print("\"");
+ }
+ printExtraAttributes(depArtifacts[j], out, " ");
+ out.println("/>");
+ }
+ }
+ }
+
+ /**
+ * Writes the extra attributes of the given {@link ExtendableItem} to the given
+ * <tt>PrintWriter</tt>.
+ *
+ * @param item
+ * the {@link ExtendableItem}, cannot be <tt>null</tt>
+ * @param out
+ * the writer to use
+ * @param prefix
+ * the string to write before writing the attributes (if any)
+ */
+ private static void printExtraAttributes(ExtendableItem item, PrintWriter out, String prefix) {
+ printExtraAttributes(item.getQualifiedExtraAttributes(), out, prefix);
+ }
+
+ /**
+ * Writes the specified <tt>Map</tt> containing the extra attributes to the given
+ * <tt>PrintWriter</tt>.
+ *
+ * @param extra
+ * the extra attributes, can be <tt>null</tt>
+ * @param out
+ * the writer to use
+ * @param prefix
+ * the string to write before writing the attributes (if any)
+ */
+ private static void printExtraAttributes(Map extra, PrintWriter out, String prefix) {
+ if (extra == null) {
+ return;
+ }
+
+ String delim = prefix;
+ for (Iterator iter = extra.entrySet().iterator(); iter.hasNext();) {
+ Map.Entry entry = (Map.Entry) iter.next();
+ out.print(delim + entry.getKey() + "=\""
+ + XMLHelper.escape(entry.getValue().toString()) + "\"");
+ delim = " ";
+ }
+ }
+
+ private static void printPublications(ModuleDescriptor md, PrintWriter out) {
+ out.println("\t<publications>");
+ Artifact[] artifacts = md.getAllArtifacts();
+ for (int i = 0; i < artifacts.length; i++) {
+ out.print("\t\t<artifact");
+ out.print(" name=\"" + XMLHelper.escape(artifacts[i].getName()) + "\"");
+ out.print(" type=\"" + XMLHelper.escape(artifacts[i].getType()) + "\"");
+ out.print(" ext=\"" + XMLHelper.escape(artifacts[i].getExt()) + "\"");
+ out.print(" conf=\"" + XMLHelper.escape(getConfs(md, artifacts[i])) + "\"");
+ printExtraAttributes(artifacts[i], out, " ");
+ out.println("/>");
+ }
+ out.println("\t</publications>");
+ }
+
+ private static void printConfigurations(ModuleDescriptor md, PrintWriter out) {
+ Configuration[] confs = md.getConfigurations();
+ if (confs.length > 0) {
+ out.println("\t<configurations>");
+ for (int i = 0; i < confs.length; i++) {
+ Configuration conf = confs[i];
+ out.print("\t\t");
+ printConfiguration(conf, out);
+ }
+ out.println("\t</configurations>");
+ }
+ }
+
+ protected static void printConfiguration(Configuration conf, PrintWriter out) {
+ out.print("<conf");
+ out.print(" name=\"" + XMLHelper.escape(conf.getName()) + "\"");
+ out.print(" visibility=\"" + XMLHelper.escape(conf.getVisibility().toString()) + "\"");
+ if (conf.getDescription() != null) {
+ out.print(" description=\"" + XMLHelper.escape(conf.getDescription()) + "\"");
+ }
+ String[] exts = conf.getExtends();
+ if (exts.length > 0) {
+ out.print(" extends=\"");
+ for (int j = 0; j < exts.length; j++) {
+ out.print(XMLHelper.escape(exts[j]));
+ if (j + 1 < exts.length) {
+ out.print(",");
+ }
+ }
+ out.print("\"");
+ }
+ if (!conf.isTransitive()) {
+ out.print(" transitive=\"false\"");
+ }
+ if (conf.getDeprecated() != null) {
+ out.print(" deprecated=\"" + XMLHelper.escape(conf.getDeprecated()) + "\"");
+ }
+ printExtraAttributes(conf, out, " ");
+ out.println("/>");
+ }
+
+ private static void printInfoTag(ModuleDescriptor md, PrintWriter out) {
+ out.println("\t<info organisation=\""
+ + XMLHelper.escape(md.getModuleRevisionId().getOrganisation()) + "\"");
+ out.println("\t\tmodule=\"" + XMLHelper.escape(md.getModuleRevisionId().getName()) + "\"");
+ String branch = md.getResolvedModuleRevisionId().getBranch();
+ if (branch != null) {
+ out.println("\t\tbranch=\"" + XMLHelper.escape(branch) + "\"");
+ }
+ String revision = md.getResolvedModuleRevisionId().getRevision();
+ if (revision != null) {
+ out.println("\t\trevision=\"" + XMLHelper.escape(revision) + "\"");
+ }
+ out.println("\t\tstatus=\"" + XMLHelper.escape(md.getStatus()) + "\"");
+ out.println("\t\tpublication=\"" + DateUtil.format(md.getResolvedPublicationDate()) + "\"");
+ if (md.isDefault()) {
+ out.println("\t\tdefault=\"true\"");
+ }
+ if (md instanceof DefaultModuleDescriptor) {
+ DefaultModuleDescriptor dmd = (DefaultModuleDescriptor) md;
+ if (dmd.getNamespace() != null && !dmd.getNamespace().getName().equals("system")) {
+ out.println("\t\tnamespace=\"" + XMLHelper.escape(dmd.getNamespace().getName())
+ + "\"");
+ }
+ }
+ if (!md.getExtraAttributes().isEmpty()) {
+ printExtraAttributes(md, out, "\t\t");
+ out.println();
+ }
+ if (requireInnerInfoElement(md)) {
+ out.println("\t>");
+ ExtendsDescriptor[] parents = md.getInheritedDescriptors();
+ for (int i = 0; i < parents.length; i++) {
+ ExtendsDescriptor parent = parents[i];
+ ModuleRevisionId mrid = parent.getParentRevisionId();
+ out.print("\t\t<extends organisation=\"" + XMLHelper.escape(mrid.getOrganisation())
+ + "\"" + " module=\"" + XMLHelper.escape(mrid.getName()) + "\""
+ + " revision=\"" + XMLHelper.escape(mrid.getRevision()) + "\"");
+
+ String location = parent.getLocation();
+ if (location != null) {
+ out.print(" location=\"" + XMLHelper.escape(location) + "\"");
+ }
+ out.print(" extendType=\"" + StringUtils.join(parent.getExtendsTypes(), ",") + "\"");
+ out.println("/>");
+ }
+ License[] licenses = md.getLicenses();
+ for (int i = 0; i < licenses.length; i++) {
+ License license = licenses[i];
+ out.print("\t\t<license ");
+ if (license.getName() != null) {
+ out.print("name=\"" + XMLHelper.escape(license.getName()) + "\" ");
+ }
+ if (license.getUrl() != null) {
+ out.print("url=\"" + XMLHelper.escape(license.getUrl()) + "\" ");
+ }
+ out.println("/>");
+ }
+ if (md.getHomePage() != null || md.getDescription() != null) {
+ out.print("\t\t<description");
+ if (md.getHomePage() != null) {
+ out.print(" homepage=\"" + XMLHelper.escape(md.getHomePage()) + "\"");
+ }
+ if (md.getDescription() != null && md.getDescription().trim().length() > 0) {
+ out.println(">");
+ out.println("\t\t" + XMLHelper.escape(md.getDescription()));
+ out.println("\t\t</description>");
+ } else {
+ out.println(" />");
+ }
+ }
+ for (ExtraInfoHolder extraInfo : md.getExtraInfos()) {
+ printExtraInfoElement(out, extraInfo, 2);
+ }
+ out.println("\t</info>");
+ } else {
+ out.println("\t/>");
+ }
+
+ }
+
+ private static void printExtraInfoElement(PrintWriter out, ExtraInfoHolder extraInfo, int indent) {
+ for (int i = 1; i <= indent; i++) {
+ out.print("\t");
+ }
+ out.print("<");
+ out.print(extraInfo.getName());
+ for (Entry<String, String> entry : extraInfo.getAttributes().entrySet()) {
+ out.print(" ");
+ out.print(entry.getKey());
+ out.print("=");
+ out.print("\"");
+ out.print(entry.getValue());
+ out.print("\"");
+ }
+ boolean requireClosingTag = false;
+ if (extraInfo.getContent() != null && extraInfo.getContent().trim().length() > 0) {
+ out.print(">");
+ out.print(XMLHelper.escape(extraInfo.getContent()));
+ requireClosingTag = true;
+ }
+ if (!extraInfo.getNestedExtraInfoHolder().isEmpty()) {
+ out.println(">");
+ for (ExtraInfoHolder nestedElement : extraInfo.getNestedExtraInfoHolder()) {
+ printExtraInfoElement(out, nestedElement, indent + 1);
+ }
+ requireClosingTag = true;
+ // prepare indentation for closing tag
+ for (int i = 1; i <= indent; i++) {
+ out.print("\t");
+ }
+ }
+ if (requireClosingTag) {
+ out.print("</");
+ out.print(extraInfo.getName());
+ out.println(">");
+ } else {
+ out.println("/>");
+ }
+ }
+
+ private static boolean requireInnerInfoElement(ModuleDescriptor md) {
+ return md.getExtraInfo().size() > 0 || md.getExtraInfos().size() > 0
+ || md.getHomePage() != null
+ || (md.getDescription() != null && md.getDescription().trim().length() > 0)
+ || md.getLicenses().length > 0 || md.getInheritedDescriptors().length > 0;
+ }
+
+ private static String getConfs(ModuleDescriptor md, Artifact artifact) {
+ StringBuffer ret = new StringBuffer();
+
+ String[] confs = md.getConfigurationsNames();
+ for (int i = 0; i < confs.length; i++) {
+ if (Arrays.asList(md.getArtifacts(confs[i])).contains(artifact)) {
+ ret.append(confs[i]).append(",");
+ }
+ }
+ if (ret.length() > 0) {
+ ret.setLength(ret.length() - 1);
+ }
+ return ret.toString();
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/parser/xml/ivy.xsd b/src/java/org/apache/ivy/plugins/parser/xml/ivy.xsd
new file mode 100644
index 0000000..fabad55
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/parser/xml/ivy.xsd
@@ -0,0 +1,291 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
+
+ <xs:complexType name="configurations-conf">
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ <xs:attribute name="transitive" type="xs:boolean"/>
+ <xs:attribute name="extends" type="xs:string"/>
+ <xs:attribute name="description" type="xs:string"/>
+ <xs:attribute name="deprecated" type="xs:string"/>
+ <xs:attribute name="visibility" default="public">
+ <xs:simpleType>
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="private"/>
+ <xs:enumeration value="public"/>
+ </xs:restriction>
+ </xs:simpleType>
+ </xs:attribute>
+ <xs:anyAttribute namespace="##other" processContents="lax" />
+ </xs:complexType>
+
+ <xs:complexType name="global-exclude">
+ <xs:sequence>
+ <xs:element name="conf" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ <xs:attribute name="org" type="xs:string"/>
+ <xs:attribute name="module" type="xs:string"/>
+ <xs:attribute name="artifact" type="xs:string"/>
+ <xs:attribute name="type" type="xs:string"/>
+ <xs:attribute name="ext" type="xs:string"/>
+ <xs:attribute name="conf" type="xs:string"/>
+ <xs:attribute name="matcher" type="xs:string"/>
+ <xs:anyAttribute namespace="##other" processContents="lax" />
+ </xs:complexType>
+
+ <xs:element name="ivy-module">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="info">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="extends" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:attribute name="organisation" type="xs:string" use="required"/>
+ <xs:attribute name="module" type="xs:string" use="required"/>
+ <xs:attribute name="revision" type="xs:string" use="required"/>
+ <xs:attribute name="location" type="xs:string" />
+ <xs:attribute name="extendType" type="xs:string" />
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="license" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ <xs:attribute name="url" type="xs:string"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="ivyauthor" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ <xs:attribute name="url" type="xs:string"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="repository" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ <xs:attribute name="url" type="xs:string"/>
+ <xs:attribute name="pattern" type="xs:string"/>
+ <xs:attribute name="ivys" type="xs:boolean"/>
+ <xs:attribute name="artifacts" type="xs:boolean"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="description" minOccurs="0" maxOccurs="1">
+ <xs:complexType mixed="true">
+ <xs:sequence>
+ <xs:any minOccurs="0" maxOccurs="unbounded" processContents="lax"/>
+ </xs:sequence>
+ <xs:attribute name="homepage" type="xs:string"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:any minOccurs="0" maxOccurs="unbounded" namespace="##other" processContents="lax"/>
+ </xs:sequence>
+ <xs:attribute name="organisation" type="xs:string"/>
+ <xs:attribute name="module" type="xs:string" use="required"/>
+ <xs:attribute name="branch" type="xs:string"/>
+ <xs:attribute name="revision" type="xs:string"/>
+ <xs:attribute name="status" type="xs:string"/>
+ <xs:attribute name="publication" type="xs:string"/>
+ <xs:attribute name="resolver" type="xs:string"/>
+ <xs:attribute name="namespace" type="xs:string"/>
+ <xs:attribute name="default" type="xs:boolean"/>
+ <xs:anyAttribute namespace="##other" processContents="lax" />
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="configurations" minOccurs="0">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:choice minOccurs="0" maxOccurs="unbounded">
+ <xs:element name="conf" type="configurations-conf"/>
+ <xs:element name="include">
+ <xs:complexType>
+ <xs:attribute name="file" type="xs:string"/>
+ <xs:attribute name="url" type="xs:string"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:choice>
+ </xs:sequence>
+ <xs:attribute name="defaultconf" type="xs:string"/>
+ <xs:attribute name="defaultconfmapping" type="xs:string"/>
+ <xs:attribute name="confmappingoverride" type="xs:boolean" />
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="publications" minOccurs="0">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="artifact" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="conf" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ <xs:attribute name="name" type="xs:string"/>
+ <xs:attribute name="type" type="xs:string"/>
+ <xs:attribute name="ext" type="xs:string"/>
+ <xs:attribute name="conf" type="xs:string"/>
+ <xs:attribute name="url" type="xs:string"/>
+ <xs:attribute name="packaging" type="xs:string"/>
+ <xs:anyAttribute namespace="##other" processContents="lax" />
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ <xs:attribute name="defaultconf" type="xs:string"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="dependencies" minOccurs="0">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="dependency" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="conf" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="mapped" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ <xs:attribute name="mapped" type="xs:string"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="artifact" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="conf" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ <xs:attribute name="type" type="xs:string"/>
+ <xs:attribute name="ext" type="xs:string"/>
+ <xs:attribute name="conf" type="xs:string"/>
+ <xs:attribute name="url" type="xs:string"/>
+ <xs:anyAttribute namespace="##other" processContents="lax" />
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="include" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="conf" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ <xs:attribute name="name" type="xs:string"/>
+ <xs:attribute name="type" type="xs:string"/>
+ <xs:attribute name="ext" type="xs:string"/>
+ <xs:attribute name="conf" type="xs:string"/>
+ <xs:attribute name="matcher" type="xs:string"/>
+ <xs:anyAttribute namespace="##other" processContents="lax" />
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="exclude" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="conf" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ <xs:attribute name="org" type="xs:string"/>
+ <xs:attribute name="module" type="xs:string"/>
+ <xs:attribute name="name" type="xs:string"/>
+ <xs:attribute name="type" type="xs:string"/>
+ <xs:attribute name="ext" type="xs:string"/>
+ <xs:attribute name="conf" type="xs:string"/>
+ <xs:attribute name="matcher" type="xs:string"/>
+ <xs:anyAttribute namespace="##other" processContents="lax" />
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ <xs:attribute name="org" type="xs:string"/>
+ <xs:attribute name="name" type="xs:string" use="required"/>
+ <xs:attribute name="branch" type="xs:string"/>
+ <xs:attribute name="branchConstraint" type="xs:string"/>
+ <xs:attribute name="rev" type="xs:string" use="required"/>
+ <xs:attribute name="revConstraint" type="xs:string"/>
+ <xs:attribute name="force" type="xs:boolean"/>
+ <xs:attribute name="changing" type="xs:boolean" default="false"/>
+ <xs:attribute name="transitive" type="xs:boolean" default="true"/>
+ <xs:attribute name="conf" type="xs:string"/>
+ <xs:anyAttribute namespace="##other" processContents="lax" />
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="exclude" type="global-exclude" minOccurs="0" maxOccurs="unbounded" />
+ <xs:element name="override" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:attribute name="org" type="xs:string"/>
+ <xs:attribute name="module" type="xs:string"/>
+ <xs:attribute name="matcher" type="xs:string"/>
+ <xs:attribute name="rev" type="xs:string"/>
+ <xs:attribute name="branch" type="xs:string"/>
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="conflict" minOccurs="0" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:attribute name="org" type="xs:string"/>
+ <xs:attribute name="module" type="xs:string"/>
+ <xs:attribute name="manager" type="xs:string"/>
+ <xs:attribute name="rev" type="xs:string"/>
+ <xs:attribute name="matcher" type="xs:string"/>
+ <xs:anyAttribute namespace="##other" processContents="lax" />
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ <xs:attribute name="defaultconf" type="xs:string"/>
+ <xs:attribute name="defaultconfmapping" type="xs:string"/>
+ <xs:attribute name="confmappingoverride" type="xs:boolean" />
+ </xs:complexType>
+ </xs:element>
+ <xs:element name="conflicts" minOccurs="0">
+ <xs:complexType>
+ <xs:sequence>
+ <xs:element name="manager" maxOccurs="unbounded">
+ <xs:complexType>
+ <xs:attribute name="org" type="xs:string"/>
+ <xs:attribute name="module" type="xs:string"/>
+ <xs:attribute name="name" type="xs:string"/>
+ <xs:attribute name="rev" type="xs:string"/>
+ <xs:attribute name="matcher" type="xs:string"/>
+ <xs:anyAttribute namespace="##other" processContents="lax" />
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
+ </xs:element>
+ </xs:sequence>
+ <xs:attribute name="version" type="xs:string" use="required"/>
+ </xs:complexType>
+ </xs:element>
+
+</xs:schema>
diff --git a/src/java/org/apache/ivy/plugins/report/LogReportOutputter.java b/src/java/org/apache/ivy/plugins/report/LogReportOutputter.java
new file mode 100644
index 0000000..c2b853b
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/report/LogReportOutputter.java
@@ -0,0 +1,199 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.report;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.ivy.core.IvyContext;
+import org.apache.ivy.core.cache.ResolutionCacheManager;
+import org.apache.ivy.core.report.ArtifactDownloadReport;
+import org.apache.ivy.core.report.ConfigurationResolveReport;
+import org.apache.ivy.core.report.ResolveReport;
+import org.apache.ivy.core.resolve.IvyNode;
+import org.apache.ivy.core.resolve.IvyNodeEviction.EvictionData;
+import org.apache.ivy.core.resolve.ResolveOptions;
+import org.apache.ivy.core.settings.IvySettings;
+import org.apache.ivy.util.Message;
+
+/**
+ *
+ */
+public class LogReportOutputter implements ReportOutputter {
+
+ public String getName() {
+ return CONSOLE;
+ }
+
+ public void output(ResolveReport report, ResolutionCacheManager cacheMgr, ResolveOptions options)
+ throws IOException {
+ IvySettings settings = IvyContext.getContext().getSettings();
+
+ if (settings.logModulesInUse() && ResolveOptions.LOG_DEFAULT.equals(options.getLog())) {
+ Message.info("\t:: modules in use:");
+ List dependencies = new ArrayList(report.getDependencies());
+ Collections.sort(dependencies);
+ if (dependencies.size() > 0) {
+ String[] confs = report.getConfigurations();
+ for (int i = 0; i < dependencies.size(); i++) {
+ IvyNode node = (IvyNode) dependencies.get(i);
+ if (node.isCompletelyEvicted() || node.hasProblem()) {
+ continue;
+ }
+ List nodeConfs = new ArrayList(confs.length);
+ for (int j = 0; j < confs.length; j++) {
+ String conf = confs[j];
+ if (report.getConfigurationReport(conf).getModuleRevisionIds()
+ .contains(node.getResolvedId())) {
+ nodeConfs.add(conf);
+ }
+ }
+ Message.info("\t" + node + " from "
+ + node.getModuleRevision().getResolver().getName() + " in " + nodeConfs);
+ }
+ }
+ }
+
+ IvyNode[] evicted = report.getEvictedNodes();
+
+ if (evicted.length > 0 && ResolveOptions.LOG_DEFAULT.equals(options.getLog())) {
+ Message.info("\t:: evicted modules:");
+ for (int i = 0; i < evicted.length; i++) {
+ Collection allEvictingNodes = evicted[i].getAllEvictingNodesDetails();
+ if (allEvictingNodes == null) {
+ Message.info("\t" + evicted[i] + " transitively in "
+ + Arrays.asList(evicted[i].getEvictedConfs()));
+ } else if (allEvictingNodes.isEmpty()) {
+ Message.info("\t" + evicted[i] + " by [] ("
+ + evicted[i].getAllEvictingConflictManagers() + ") in "
+ + Arrays.asList(evicted[i].getEvictedConfs()));
+ } else {
+ Message.info("\t" + evicted[i] + " by " + allEvictingNodes + " in "
+ + Arrays.asList(evicted[i].getEvictedConfs()));
+ }
+ String[] confs = evicted[i].getEvictedConfs();
+ for (int j = 0; j < confs.length; j++) {
+ EvictionData evictedData = evicted[i].getEvictedData(confs[j]);
+ if (evictedData.getParent() != null) {
+ Message.verbose("\t in " + evictedData.getParent() + " with "
+ + evictedData.getConflictManager());
+ }
+ }
+ }
+ }
+
+ if (ResolveOptions.LOG_DEFAULT.equals(options.getLog())) {
+ // CheckStyle:MagicNumber| OFF
+ char[] sep = new char[69];
+ Arrays.fill(sep, '-');
+ Message.rawinfo("\t" + new String(sep));
+ StringBuffer line = new StringBuffer("\t");
+ append(line, "", 18);
+ append(line, "modules", 31);
+ line.append("|");
+ append(line, "artifacts", 15);
+ line.append("|");
+ Message.rawinfo(line.toString());
+
+ line = new StringBuffer("\t");
+ append(line, "conf", 18);
+ append(line, "number", 7);
+ append(line, "search", 7);
+ append(line, "dwnlded", 7);
+ append(line, "evicted", 7);
+ line.append("|");
+ append(line, "number", 7);
+ append(line, "dwnlded", 7);
+ // CheckStyle:MagicNumber| ON
+ line.append("|");
+ Message.rawinfo(line.toString());
+ Message.rawinfo("\t" + new String(sep));
+
+ String[] confs = report.getConfigurations();
+ for (int i = 0; i < confs.length; i++) {
+ output(report.getConfigurationReport(confs[i]));
+ }
+ Message.rawinfo("\t" + new String(sep));
+ }
+
+ IvyNode[] unresolved = report.getUnresolvedDependencies();
+ if (unresolved.length > 0) {
+ Message.warn("\t::::::::::::::::::::::::::::::::::::::::::::::");
+ Message.warn("\t:: UNRESOLVED DEPENDENCIES ::");
+ Message.warn("\t::::::::::::::::::::::::::::::::::::::::::::::");
+ }
+ for (int i = 0; i < unresolved.length; i++) {
+ Message.warn("\t:: " + unresolved[i] + ": " + unresolved[i].getProblemMessage());
+ }
+ if (unresolved.length > 0) {
+ Message.warn("\t::::::::::::::::::::::::::::::::::::::::::::::\n");
+ }
+
+ ArtifactDownloadReport[] errors = report.getFailedArtifactsReports();
+ if (errors.length > 0) {
+ Message.warn("\t::::::::::::::::::::::::::::::::::::::::::::::");
+ Message.warn("\t:: FAILED DOWNLOADS ::");
+ Message.warn("\t:: ^ see resolution messages for details ^ ::");
+ Message.warn("\t::::::::::::::::::::::::::::::::::::::::::::::");
+ }
+ for (int i = 0; i < errors.length; i++) {
+ Message.warn("\t:: " + errors[i].getArtifact());
+ }
+ if (errors.length > 0) {
+ Message.warn("\t::::::::::::::::::::::::::::::::::::::::::::::\n");
+ }
+ }
+
+ public void output(ConfigurationResolveReport report) {
+ StringBuffer line = new StringBuffer("\t");
+ // CheckStyle:MagicNumber| OFF
+ append(line, report.getConfiguration(), 18);
+ append(line, String.valueOf(report.getNodesNumber()), 7);
+ append(line, String.valueOf(report.getSearchedNodes().length), 7);
+ append(line, String.valueOf(report.getDownloadedNodes().length), 7);
+ append(line, String.valueOf(report.getEvictedNodes().length), 7);
+ line.append("|");
+ append(line, String.valueOf(report.getArtifactsNumber()), 7);
+ append(line, String.valueOf(report.getDownloadedArtifactsReports().length), 7);
+ // CheckStyle:MagicNumber| ON
+ line.append("|");
+
+ Message.rawinfo(line.toString());
+ }
+
+ private void append(StringBuffer line, Object o, int limit) {
+ String v = String.valueOf(o);
+ if (v.length() >= limit) {
+ v = v.substring(0, limit);
+ } else {
+ int missing = limit - v.length();
+ int half = missing / 2;
+ char[] c = new char[limit];
+ Arrays.fill(c, ' ');
+ System.arraycopy(v.toCharArray(), 0, c, missing - half, v.length());
+ v = new String(c);
+ }
+ line.append("|");
+ line.append(v);
+ }
+
+}
diff --git a/src/java/org/apache/ivy/plugins/report/ReportOutputter.java b/src/java/org/apache/ivy/plugins/report/ReportOutputter.java
new file mode 100644
index 0000000..f7d4ec3
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/report/ReportOutputter.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.report;
+
+import java.io.IOException;
+
+import org.apache.ivy.core.cache.ResolutionCacheManager;
+import org.apache.ivy.core.report.ResolveReport;
+import org.apache.ivy.core.resolve.ResolveOptions;
+
+/**
+ *
+ */
+public interface ReportOutputter {
+ public static final String CONSOLE = "console";
+
+ public static final String XML = "xml";
+
+ public abstract void output(ResolveReport report, ResolutionCacheManager cacheMgr,
+ ResolveOptions options) throws IOException;
+
+ public abstract String getName();
+}
diff --git a/src/java/org/apache/ivy/plugins/report/XmlReportOutputter.java b/src/java/org/apache/ivy/plugins/report/XmlReportOutputter.java
new file mode 100644
index 0000000..0c5d9f7
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/report/XmlReportOutputter.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.report;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.apache.ivy.core.cache.ResolutionCacheManager;
+import org.apache.ivy.core.report.ConfigurationResolveReport;
+import org.apache.ivy.core.report.ResolveReport;
+import org.apache.ivy.core.resolve.ResolveOptions;
+import org.apache.ivy.util.FileUtil;
+import org.apache.ivy.util.Message;
+
+/**
+ * A Report outputter implementation using {@link XmlReportWriter} to write xml reports to the
+ * resolution cache.
+ */
+public class XmlReportOutputter implements ReportOutputter {
+ private XmlReportWriter writer = new XmlReportWriter();
+
+ public String getName() {
+ return XML;
+ }
+
+ public void output(ResolveReport report, ResolutionCacheManager cacheMgr, ResolveOptions options)
+ throws IOException {
+ String[] confs = report.getConfigurations();
+ for (int i = 0; i < confs.length; i++) {
+ output(report.getConfigurationReport(confs[i]), report.getResolveId(), confs, cacheMgr);
+ }
+ }
+
+ public void output(ConfigurationResolveReport report, String resolveId, String[] confs,
+ ResolutionCacheManager cacheMgr) throws IOException {
+ File reportFile = cacheMgr.getConfigurationResolveReportInCache(resolveId,
+ report.getConfiguration());
+ File reportParentDir = reportFile.getParentFile();
+ reportParentDir.mkdirs();
+ OutputStream stream = new FileOutputStream(reportFile);
+ writer.output(report, confs, stream);
+ stream.close();
+
+ Message.verbose("\treport for " + report.getModuleDescriptor().getModuleRevisionId() + " "
+ + report.getConfiguration() + " produced in " + reportFile);
+
+ File reportXsl = new File(reportParentDir, "ivy-report.xsl");
+ File reportCss = new File(reportParentDir, "ivy-report.css");
+ if (!reportXsl.exists()) {
+ FileUtil.copy(XmlReportOutputter.class.getResourceAsStream("ivy-report.xsl"),
+ reportXsl, null);
+ }
+ if (!reportCss.exists()) {
+ FileUtil.copy(XmlReportOutputter.class.getResourceAsStream("ivy-report.css"),
+ reportCss, null);
+ }
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/report/XmlReportParser.java b/src/java/org/apache/ivy/plugins/report/XmlReportParser.java
new file mode 100644
index 0000000..633eb56
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/report/XmlReportParser.java
@@ -0,0 +1,333 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.report;
+
+import java.io.File;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.apache.ivy.core.cache.ArtifactOrigin;
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.DefaultArtifact;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.report.ArtifactDownloadReport;
+import org.apache.ivy.core.report.DownloadStatus;
+import org.apache.ivy.core.report.MetadataArtifactDownloadReport;
+import org.apache.ivy.util.DateUtil;
+import org.apache.ivy.util.extendable.ExtendableItemHelper;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+public class XmlReportParser {
+ private static class SaxXmlReportParser {
+ private final class XmlReportParserHandler extends DefaultHandler {
+ private String organisation;
+
+ private String module;
+
+ private String branch;
+
+ private String revision;
+
+ private int position;
+
+ private Date pubdate;
+
+ private boolean skip;
+
+ private ModuleRevisionId mrid;
+
+ private boolean isDefault;
+
+ // Use a TreeMap to order by
+ private SortedMap<Integer, List<ArtifactDownloadReport>> revisionsMap = new TreeMap<Integer, List<ArtifactDownloadReport>>();
+
+ private List<ArtifactDownloadReport> revisionArtifacts = null;
+
+ public void startElement(String uri, String localName, String qName,
+ Attributes attributes) throws SAXException {
+ if ("module".equals(qName)) {
+ organisation = attributes.getValue("organisation");
+ module = attributes.getValue("name");
+ } else if ("revision".equals(qName)) {
+ revisionArtifacts = new ArrayList<ArtifactDownloadReport>();
+ branch = attributes.getValue("branch");
+ revision = attributes.getValue("name");
+ isDefault = Boolean.valueOf(attributes.getValue("default")).booleanValue();
+ // retrieve position from file. If no position is found, it may be an old
+ // report generated with a previous version,
+ // in which case, we put it at the last position
+ String pos = attributes.getValue("position");
+ position = pos == null ? getMaxPos() + 1 : Integer.valueOf(pos).intValue();
+ if (attributes.getValue("error") != null) {
+ hasError = true;
+ skip = true;
+ } else if (attributes.getValue("evicted") != null) {
+ skip = true;
+ } else {
+ revisionsMap.put(new Integer(position), revisionArtifacts);
+ mrid = ModuleRevisionId.newInstance(organisation, module, branch, revision,
+ ExtendableItemHelper.getExtraAttributes(attributes, "extra-"));
+ mrids.add(mrid);
+ if (isDefault) {
+ defaultMrids.add(mrid);
+ } else {
+ Artifact metadataArtifact = DefaultArtifact.newIvyArtifact(mrid,
+ pubdate);
+ MetadataArtifactDownloadReport madr = new MetadataArtifactDownloadReport(
+ metadataArtifact);
+ metadataReports.put(mrid, madr);
+ realMrids.add(mrid);
+ }
+ try {
+ String pubDateAttr = attributes.getValue("pubdate");
+ if (pubDateAttr != null) {
+ pubdate = DateUtil.parse(pubDateAttr);
+ }
+ skip = false;
+ } catch (ParseException e) {
+ throw new IllegalArgumentException("invalid publication date for "
+ + organisation + " " + module + " " + revision + ": "
+ + attributes.getValue("pubdate"));
+ }
+ }
+ } else if ("metadata-artifact".equals(qName)) {
+ if (skip) {
+ return;
+ }
+ MetadataArtifactDownloadReport madr = metadataReports.get(mrid);
+ if (madr != null) {
+ madr.setDownloadStatus(DownloadStatus.fromString(attributes
+ .getValue("status")));
+ madr.setDownloadDetails(attributes.getValue("details"));
+ madr.setSize(Long.parseLong(attributes.getValue("size")));
+ madr.setDownloadTimeMillis(Long.parseLong(attributes.getValue("time")));
+ madr.setSearched(parseBoolean(attributes.getValue("searched")));
+ if (attributes.getValue("location") != null) {
+ madr.setLocalFile(new File(attributes.getValue("location")));
+ }
+ if (attributes.getValue("original-local-location") != null) {
+ madr.setOriginalLocalFile(new File(attributes
+ .getValue("original-local-location")));
+ }
+ if (attributes.getValue("origin-location") != null) {
+ if (ArtifactOrigin.isUnknown(attributes.getValue("origin-location"))) {
+ madr.setArtifactOrigin(ArtifactOrigin.unkwnown(madr.getArtifact()));
+ } else {
+ madr.setArtifactOrigin(new ArtifactOrigin(madr.getArtifact(),
+ parseBoolean(attributes.getValue("origin-is-local")),
+ attributes.getValue("origin-location")));
+ }
+ }
+ }
+ } else if ("artifact".equals(qName)) {
+ if (skip) {
+ return;
+ }
+ String status = attributes.getValue("status");
+ String artifactName = attributes.getValue("name");
+ String type = attributes.getValue("type");
+ String ext = attributes.getValue("ext");
+ Artifact artifact = new DefaultArtifact(mrid, pubdate, artifactName, type, ext,
+ ExtendableItemHelper.getExtraAttributes(attributes, "extra-"));
+ ArtifactDownloadReport aReport = new ArtifactDownloadReport(artifact);
+ aReport.setDownloadStatus(DownloadStatus.fromString(status));
+ aReport.setDownloadDetails(attributes.getValue("details"));
+ aReport.setSize(Long.parseLong(attributes.getValue("size")));
+ aReport.setDownloadTimeMillis(Long.parseLong(attributes.getValue("time")));
+ if (attributes.getValue("location") != null) {
+ aReport.setLocalFile(new File(attributes.getValue("location")));
+ }
+ if (attributes.getValue("unpackedFile") != null) {
+ aReport.setUnpackedLocalFile(new File(attributes.getValue("unpackedFile")));
+ }
+ revisionArtifacts.add(aReport);
+ } else if ("origin-location".equals(qName)) {
+ if (skip) {
+ return;
+ }
+ ArtifactDownloadReport aReport = revisionArtifacts
+ .get(revisionArtifacts.size() - 1);
+
+ if (ArtifactOrigin.isUnknown(attributes.getValue("location"))) {
+ aReport.setArtifactOrigin(ArtifactOrigin.unkwnown(aReport.getArtifact()));
+ } else {
+ aReport.setArtifactOrigin(new ArtifactOrigin(aReport.getArtifact(),
+ parseBoolean(attributes.getValue("is-local")), attributes
+ .getValue("location")));
+ }
+ } else if ("info".equals(qName)) {
+ String organisation = attributes.getValue("organisation");
+ String name = attributes.getValue("module");
+ String branch = attributes.getValue("branch");
+ String revision = attributes.getValue("revision");
+ Map<String, String> extraAttributes = new HashMap<String, String>();
+ for (int i = 0; i < attributes.getLength(); i++) {
+ String attName = attributes.getQName(i);
+ if (attName.startsWith("extra-")) {
+ String extraAttrName = attName.substring("extra-".length());
+ String extraAttrValue = attributes.getValue(i);
+ extraAttributes.put(extraAttrName, extraAttrValue);
+ }
+ }
+ mRevisionId = ModuleRevisionId.newInstance(organisation, name, branch,
+ revision, extraAttributes);
+ }
+ }
+
+ public void endElement(String uri, String localName, String qname) throws SAXException {
+ if ("dependencies".equals(qname)) {
+ // add the artifacts in the correct order
+ for (List<ArtifactDownloadReport> artifactReports : revisionsMap.values()) {
+ SaxXmlReportParser.this.artifactReports.addAll(artifactReports);
+ for (ArtifactDownloadReport artifactReport : artifactReports) {
+ if (artifactReport.getDownloadStatus() != DownloadStatus.FAILED) {
+ artifacts.add(artifactReport.getArtifact());
+ }
+ }
+
+ }
+ }
+ }
+
+ private int getMaxPos() {
+ return revisionsMap.isEmpty() ? -1
+ : ((Integer) revisionsMap.keySet().toArray()[revisionsMap.size() - 1])
+ .intValue();
+ }
+ }
+
+ private List<ModuleRevisionId> mrids = new ArrayList<ModuleRevisionId>();
+
+ private List<ModuleRevisionId> defaultMrids = new ArrayList<ModuleRevisionId>();
+
+ private List<ModuleRevisionId> realMrids = new ArrayList<ModuleRevisionId>();
+
+ private List<Artifact> artifacts = new ArrayList<Artifact>();
+
+ private List<ArtifactDownloadReport> artifactReports = new ArrayList<ArtifactDownloadReport>();
+
+ private Map<ModuleRevisionId, MetadataArtifactDownloadReport> metadataReports = new HashMap<ModuleRevisionId, MetadataArtifactDownloadReport>();
+
+ private ModuleRevisionId mRevisionId;
+
+ private File report;
+
+ private boolean hasError = false;
+
+ SaxXmlReportParser(File report) {
+ this.report = report;
+ }
+
+ public void parse() throws Exception {
+ SAXParser saxParser = SAXParserFactory.newInstance().newSAXParser();
+ saxParser.parse(report, new XmlReportParserHandler());
+ }
+
+ private static boolean parseBoolean(String str) {
+ return (str != null) && str.equalsIgnoreCase("true");
+ }
+
+ public List<Artifact> getArtifacts() {
+ return artifacts;
+ }
+
+ public List<ArtifactDownloadReport> getArtifactReports() {
+ return artifactReports;
+ }
+
+ public List<ModuleRevisionId> getModuleRevisionIds() {
+ return mrids;
+ }
+
+ public List<ModuleRevisionId> getRealModuleRevisionIds() {
+ return realMrids;
+ }
+
+ public ModuleRevisionId getResolvedModule() {
+ return mRevisionId;
+ }
+
+ public MetadataArtifactDownloadReport getMetadataArtifactReport(ModuleRevisionId id) {
+ return metadataReports.get(id);
+ }
+ }
+
+ private SaxXmlReportParser parser = null;
+
+ public void parse(File report) throws ParseException {
+ if (!report.exists()) {
+ throw new IllegalStateException("Report file '" + report.getAbsolutePath()
+ + "' does not exist.");
+ }
+
+ parser = new SaxXmlReportParser(report);
+ try {
+ parser.parse();
+ } catch (Exception e) {
+ ParseException pe = new ParseException("failed to parse report: " + report + ": "
+ + e.getMessage(), 0);
+ pe.initCause(e);
+ throw pe;
+ }
+ }
+
+ public Artifact[] getArtifacts() {
+ return parser.getArtifacts().toArray(new Artifact[parser.getArtifacts().size()]);
+ }
+
+ public ArtifactDownloadReport[] getArtifactReports() {
+ return parser.getArtifactReports().toArray(
+ new ArtifactDownloadReport[parser.getArtifactReports().size()]);
+ }
+
+ public ModuleRevisionId[] getDependencyRevisionIds() {
+ return parser.getModuleRevisionIds().toArray(
+ new ModuleRevisionId[parser.getModuleRevisionIds().size()]);
+ }
+
+ public ModuleRevisionId[] getRealDependencyRevisionIds() {
+ return parser.getRealModuleRevisionIds().toArray(
+ new ModuleRevisionId[parser.getRealModuleRevisionIds().size()]);
+ }
+
+ public MetadataArtifactDownloadReport getMetadataArtifactReport(ModuleRevisionId id) {
+ return parser.getMetadataArtifactReport(id);
+ }
+
+ /**
+ * Returns the <tt>ModuleRevisionId</tt> of the resolved module.
+ */
+ public ModuleRevisionId getResolvedModule() {
+ return parser.getResolvedModule();
+ }
+
+ public boolean hasError() {
+ return parser.hasError;
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/report/XmlReportWriter.java b/src/java/org/apache/ivy/plugins/report/XmlReportWriter.java
new file mode 100644
index 0000000..080c658
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/report/XmlReportWriter.java
@@ -0,0 +1,319 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.report;
+
+import java.io.BufferedWriter;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.ivy.core.cache.ArtifactOrigin;
+import org.apache.ivy.core.module.descriptor.License;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleId;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.report.ArtifactDownloadReport;
+import org.apache.ivy.core.report.ConfigurationResolveReport;
+import org.apache.ivy.core.report.MetadataArtifactDownloadReport;
+import org.apache.ivy.core.resolve.IvyNode;
+import org.apache.ivy.core.resolve.IvyNodeCallers.Caller;
+import org.apache.ivy.core.resolve.IvyNodeEviction.EvictionData;
+import org.apache.ivy.util.DateUtil;
+import org.apache.ivy.util.StringUtils;
+import org.apache.ivy.util.XMLHelper;
+
+/**
+ * XmlReportWriter allows to write ResolveReport in an xml format.
+ */
+public class XmlReportWriter {
+
+ static final String REPORT_ENCODING = "UTF-8";
+
+ public void output(ConfigurationResolveReport report, OutputStream stream) {
+ output(report, new String[] {report.getConfiguration()}, stream);
+ }
+
+ public void output(ConfigurationResolveReport report, String[] confs, OutputStream stream) {
+ OutputStreamWriter encodedOutStream;
+ try {
+ encodedOutStream = new OutputStreamWriter(stream, REPORT_ENCODING);
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException(REPORT_ENCODING + " is not known on your jvm", e);
+ }
+ PrintWriter out = new PrintWriter(new BufferedWriter(encodedOutStream));
+ ModuleRevisionId mrid = report.getModuleDescriptor().getModuleRevisionId();
+ // out.println("<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>");
+ out.println("<?xml version=\"1.0\" encoding=\"" + REPORT_ENCODING + "\"?>");
+ out.println("<?xml-stylesheet type=\"text/xsl\" href=\"ivy-report.xsl\"?>");
+ out.println("<ivy-report version=\"1.0\">");
+ out.println("\t<info");
+ out.println("\t\torganisation=\"" + XMLHelper.escape(mrid.getOrganisation()) + "\"");
+ out.println("\t\tmodule=\"" + XMLHelper.escape(mrid.getName()) + "\"");
+ out.println("\t\trevision=\"" + XMLHelper.escape(mrid.getRevision()) + "\"");
+ if (mrid.getBranch() != null) {
+ out.println("\t\tbranch=\"" + XMLHelper.escape(mrid.getBranch()) + "\"");
+ }
+ Map extraAttributes = mrid.getExtraAttributes();
+ for (Iterator it = extraAttributes.entrySet().iterator(); it.hasNext();) {
+ Map.Entry entry = (Entry) it.next();
+ out.println("\t\textra-" + entry.getKey() + "=\""
+ + XMLHelper.escape(entry.getValue().toString()) + "\"");
+ }
+ out.println("\t\tconf=\"" + XMLHelper.escape(report.getConfiguration()) + "\"");
+ out.println("\t\tconfs=\"" + XMLHelper.escape(StringUtils.join(confs, ", ")) + "\"");
+ out.println("\t\tdate=\"" + DateUtil.format(report.getDate()) + "\"/>");
+
+ out.println("\t<dependencies>");
+
+ // create a list of ModuleRevisionIds indicating the position for each dependency
+ List dependencies = new ArrayList(report.getModuleRevisionIds());
+
+ for (Iterator iter = report.getModuleIds().iterator(); iter.hasNext();) {
+ ModuleId mid = (ModuleId) iter.next();
+ out.println("\t\t<module organisation=\"" + XMLHelper.escape(mid.getOrganisation())
+ + "\"" + " name=\"" + XMLHelper.escape(mid.getName()) + "\">");
+ for (Iterator it2 = report.getNodes(mid).iterator(); it2.hasNext();) {
+ IvyNode dep = (IvyNode) it2.next();
+ ouputRevision(report, out, dependencies, dep);
+ }
+ out.println("\t\t</module>");
+ }
+ out.println("\t</dependencies>");
+ out.println("</ivy-report>");
+ out.flush();
+ }
+
+ private void ouputRevision(ConfigurationResolveReport report, PrintWriter out,
+ List dependencies, IvyNode dep) {
+ Map extraAttributes;
+ ModuleDescriptor md = null;
+ if (dep.getModuleRevision() != null) {
+ md = dep.getModuleRevision().getDescriptor();
+ }
+ StringBuffer details = new StringBuffer();
+ if (dep.isLoaded()) {
+ details.append(" status=\"");
+ details.append(XMLHelper.escape(dep.getDescriptor().getStatus()));
+ details.append("\" pubdate=\"");
+ details.append(DateUtil.format(new Date(dep.getPublication())));
+ details.append("\" resolver=\"");
+ details.append(XMLHelper.escape(dep.getModuleRevision().getResolver().getName()));
+ details.append("\" artresolver=\"");
+ details.append(XMLHelper
+ .escape(dep.getModuleRevision().getArtifactResolver().getName()));
+ details.append("\"");
+ }
+ if (dep.isEvicted(report.getConfiguration())) {
+ EvictionData ed = dep.getEvictedData(report.getConfiguration());
+ if (ed.getConflictManager() != null) {
+ details.append(" evicted=\"")
+ .append(XMLHelper.escape(ed.getConflictManager().toString())).append("\"");
+ } else {
+ details.append(" evicted=\"transitive\"");
+ }
+ details.append(" evicted-reason=\"")
+ .append(XMLHelper.escape(ed.getDetail() == null ? "" : ed.getDetail()))
+ .append("\"");
+ }
+ if (dep.hasProblem()) {
+ details.append(" error=\"").append(XMLHelper.escape(dep.getProblem().getMessage()))
+ .append("\"");
+ }
+ if (md != null && md.getHomePage() != null) {
+ details.append(" homepage=\"").append(XMLHelper.escape(md.getHomePage())).append("\"");
+ }
+ extraAttributes = md != null ? md.getExtraAttributes() : dep.getResolvedId()
+ .getExtraAttributes();
+ for (Iterator iterator = extraAttributes.keySet().iterator(); iterator.hasNext();) {
+ String attName = (String) iterator.next();
+ details.append(" extra-").append(attName).append("=\"")
+ .append(XMLHelper.escape(extraAttributes.get(attName).toString())).append("\"");
+ }
+ String defaultValue = dep.getDescriptor() != null ? " default=\""
+ + dep.getDescriptor().isDefault() + "\"" : "";
+ int position = dependencies.indexOf(dep.getResolvedId());
+ out.println("\t\t\t<revision name=\""
+ + XMLHelper.escape(dep.getResolvedId().getRevision())
+ + "\""
+ + (dep.getResolvedId().getBranch() == null ? "" : " branch=\""
+ + XMLHelper.escape(dep.getResolvedId().getBranch()) + "\"") + details
+ + " downloaded=\"" + dep.isDownloaded() + "\"" + " searched=\"" + dep.isSearched()
+ + "\"" + defaultValue + " conf=\""
+ + toString(dep.getConfigurations(report.getConfiguration())) + "\""
+ + " position=\"" + position + "\">");
+ if (md != null) {
+ License[] licenses = md.getLicenses();
+ for (int i = 0; i < licenses.length; i++) {
+ String lurl;
+ if (licenses[i].getUrl() != null) {
+ lurl = " url=\"" + XMLHelper.escape(licenses[i].getUrl()) + "\"";
+ } else {
+ lurl = "";
+ }
+ out.println("\t\t\t\t<license name=\"" + XMLHelper.escape(licenses[i].getName())
+ + "\"" + lurl + "/>");
+ }
+ }
+ outputMetadataArtifact(out, dep);
+ outputEvictionInformation(report, out, dep);
+ outputCallers(report, out, dep);
+ outputArtifacts(report, out, dep);
+ out.println("\t\t\t</revision>");
+ }
+
+ private void outputEvictionInformation(ConfigurationResolveReport report, PrintWriter out,
+ IvyNode dep) {
+ if (dep.isEvicted(report.getConfiguration())) {
+ EvictionData ed = dep.getEvictedData(report.getConfiguration());
+ Collection selected = ed.getSelected();
+ if (selected != null) {
+ for (Iterator it3 = selected.iterator(); it3.hasNext();) {
+ IvyNode sel = (IvyNode) it3.next();
+ out.println("\t\t\t\t<evicted-by rev=\""
+ + XMLHelper.escape(sel.getResolvedId().getRevision()) + "\"/>");
+ }
+ }
+ }
+ }
+
+ private void outputMetadataArtifact(PrintWriter out, IvyNode dep) {
+ if (dep.getModuleRevision() != null) {
+ MetadataArtifactDownloadReport madr = dep.getModuleRevision().getReport();
+ out.print("\t\t\t\t<metadata-artifact");
+ out.print(" status=\"" + XMLHelper.escape(madr.getDownloadStatus().toString()) + "\"");
+ out.print(" details=\"" + XMLHelper.escape(madr.getDownloadDetails()) + "\"");
+ out.print(" size=\"" + madr.getSize() + "\"");
+ out.print(" time=\"" + madr.getDownloadTimeMillis() + "\"");
+ if (madr.getLocalFile() != null) {
+ out.print(" location=\"" + XMLHelper.escape(madr.getLocalFile().getAbsolutePath())
+ + "\"");
+ }
+
+ out.print(" searched=\"" + madr.isSearched() + "\"");
+ if (madr.getOriginalLocalFile() != null) {
+ out.print(" original-local-location=\""
+ + XMLHelper.escape(madr.getOriginalLocalFile().getAbsolutePath()) + "\"");
+ }
+
+ ArtifactOrigin origin = madr.getArtifactOrigin();
+ if (origin != null) {
+ out.print(" origin-is-local=\"" + String.valueOf(origin.isLocal()) + "\"");
+ out.print(" origin-location=\"" + XMLHelper.escape(origin.getLocation()) + "\"");
+ }
+ out.println("/>");
+
+ }
+ }
+
+ private void outputCallers(ConfigurationResolveReport report, PrintWriter out, IvyNode dep) {
+ Caller[] callers = dep.getCallers(report.getConfiguration());
+ for (int i = 0; i < callers.length; i++) {
+ StringBuffer callerDetails = new StringBuffer();
+ Map callerExtraAttributes = callers[i].getDependencyDescriptor().getExtraAttributes();
+ for (Iterator iterator = callerExtraAttributes.keySet().iterator(); iterator.hasNext();) {
+ String attName = (String) iterator.next();
+ callerDetails.append(" extra-").append(attName).append("=\"")
+ .append(XMLHelper.escape(callerExtraAttributes.get(attName).toString()))
+ .append("\"");
+ }
+
+ out.println("\t\t\t\t<caller organisation=\""
+ + XMLHelper.escape(callers[i].getModuleRevisionId().getOrganisation())
+ + "\""
+ + " name=\""
+ + XMLHelper.escape(callers[i].getModuleRevisionId().getName())
+ + "\""
+ + " conf=\""
+ + XMLHelper.escape(toString(callers[i].getCallerConfigurations()))
+ + "\""
+ + " rev=\""
+ + XMLHelper.escape(callers[i].getAskedDependencyId(dep.getData()).getRevision())
+ + "\""
+ + " rev-constraint-default=\""
+ + XMLHelper.escape(callers[i].getDependencyDescriptor()
+ .getDependencyRevisionId().getRevision())
+ + "\""
+ + " rev-constraint-dynamic=\""
+ + XMLHelper.escape(callers[i].getDependencyDescriptor()
+ .getDynamicConstraintDependencyRevisionId().getRevision()) + "\""
+ + " callerrev=\""
+ + XMLHelper.escape(callers[i].getModuleRevisionId().getRevision()) + "\""
+ + callerDetails + "/>");
+ }
+ }
+
+ private void outputArtifacts(ConfigurationResolveReport report, PrintWriter out, IvyNode dep) {
+ Map extraAttributes;
+ ArtifactDownloadReport[] adr = report.getDownloadReports(dep.getResolvedId());
+ out.println("\t\t\t\t<artifacts>");
+ for (int i = 0; i < adr.length; i++) {
+ out.print("\t\t\t\t\t<artifact name=\"" + XMLHelper.escape(adr[i].getName())
+ + "\" type=\"" + XMLHelper.escape(adr[i].getType()) + "\" ext=\""
+ + XMLHelper.escape(adr[i].getExt()) + "\"");
+ extraAttributes = adr[i].getArtifact().getExtraAttributes();
+ for (Iterator iterator = extraAttributes.keySet().iterator(); iterator.hasNext();) {
+ String attName = (String) iterator.next();
+ out.print(" extra-" + attName + "=\""
+ + XMLHelper.escape(extraAttributes.get(attName).toString()) + "\"");
+ }
+ out.print(" status=\"" + XMLHelper.escape(adr[i].getDownloadStatus().toString()) + "\"");
+ out.print(" details=\"" + XMLHelper.escape(adr[i].getDownloadDetails()) + "\"");
+ out.print(" size=\"" + adr[i].getSize() + "\"");
+ out.print(" time=\"" + adr[i].getDownloadTimeMillis() + "\"");
+ if (adr[i].getLocalFile() != null) {
+ out.print(" location=\""
+ + XMLHelper.escape(adr[i].getLocalFile().getAbsolutePath()) + "\"");
+ }
+ if (adr[i].getUnpackedLocalFile() != null) {
+ out.print(" unpackedFile=\""
+ + XMLHelper.escape(adr[i].getUnpackedLocalFile().getAbsolutePath()) + "\"");
+ }
+
+ ArtifactOrigin origin = adr[i].getArtifactOrigin();
+ if (origin != null) {
+ out.println(">");
+ out.println("\t\t\t\t\t\t<origin-location is-local=\""
+ + String.valueOf(origin.isLocal()) + "\"" + " location=\""
+ + XMLHelper.escape(origin.getLocation()) + "\"/>");
+ out.println("\t\t\t\t\t</artifact>");
+ } else {
+ out.println("/>");
+ }
+ }
+ out.println("\t\t\t\t</artifacts>");
+ }
+
+ private String toString(String[] strs) {
+ StringBuffer buf = new StringBuffer();
+ for (int i = 0; i < strs.length; i++) {
+ buf.append(strs[i]);
+ if (i + 1 < strs.length) {
+ buf.append(", ");
+ }
+ }
+ return XMLHelper.escape(buf.toString());
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/report/ivy-report-dot-all.xsl b/src/java/org/apache/ivy/plugins/report/ivy-report-dot-all.xsl
new file mode 100644
index 0000000..ef79a32
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/report/ivy-report-dot-all.xsl
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<xsl:stylesheet version="1.0"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+<xsl:output method="text"/>
+<xsl:template match="/ivy-report">
+ <xsl:text>/* * directed graph dot input file. * * generated by ivy report */
+ digraph G {</xsl:text>
+ <xsl:for-each select="dependencies/module">
+ <xsl:text>"</xsl:text><xsl:value-of select="@name"/><xsl:text>"</xsl:text>
+ </xsl:for-each>
+ <xsl:for-each select="dependencies/module/revision[not(@evicted)]/caller[@organisation!='caller']">
+ <xsl:text>"</xsl:text><xsl:value-of select="@name"/><xsl:text>" -> "</xsl:text><xsl:value-of select="../../@name"/><xsl:text>";</xsl:text>
+ </xsl:for-each>
+ <xsl:text>}</xsl:text>
+</xsl:template>
+</xsl:stylesheet>
diff --git a/src/java/org/apache/ivy/plugins/report/ivy-report-dot.xsl b/src/java/org/apache/ivy/plugins/report/ivy-report-dot.xsl
new file mode 100644
index 0000000..58e7932
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/report/ivy-report-dot.xsl
@@ -0,0 +1,49 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<xsl:stylesheet version="1.0"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+<xsl:output method="text"/>
+<xsl:template match="/ivy-report">
+/* * directed graph dot input file. * * generated by ivy report */
+ digraph G {
+ "<xsl:value-of select="info/@organisation"/>-<xsl:value-of select="info/@module"/>" [label="<xsl:value-of select="info/@module"/>"];
+ <xsl:for-each select="dependencies/module">
+ "<xsl:value-of select="@organisation"/>-<xsl:value-of select="@name"/>" [label="<xsl:value-of select="@name"/>
+ <xsl:for-each select="revision">
+ <xsl:text>\n</xsl:text>
+ <xsl:value-of select="@name"/><xsl:if test="@error"> (error)</xsl:if><xsl:if test="@evicted"> (evicted)</xsl:if>
+ </xsl:for-each>
+ <xsl:text>"];
+</xsl:text>
+ </xsl:for-each>
+ <xsl:for-each select="dependencies/module/revision[not(@evicted)]/caller">
+ <xsl:text>"</xsl:text>
+ <xsl:value-of select="@organisation"/>-<xsl:value-of select="@name"/>
+ <xsl:text>" -> "</xsl:text>
+ <xsl:value-of select="../../@organisation"/>-<xsl:value-of select="../../@name"/>
+ <xsl:text>" [label="</xsl:text>
+ <xsl:value-of select="@rev"/>
+ <xsl:text>"]</xsl:text>
+ <xsl:text>;
+</xsl:text>
+ </xsl:for-each>
+ <xsl:text>}</xsl:text>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/src/java/org/apache/ivy/plugins/report/ivy-report-graph-all.xsl b/src/java/org/apache/ivy/plugins/report/ivy-report-graph-all.xsl
new file mode 100644
index 0000000..6cb6fec
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/report/ivy-report-graph-all.xsl
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<xsl:stylesheet version="1.0"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+<xsl:template match="/ivy-report">
+ <graphml xmlns="http://graphml.graphdrawing.org/xmlns/graphml"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns/graphml http://www.yworks.com/xml/schema/graphml/1.0/ygraphml.xsd"
+ xmlns:y="http://www.yworks.com/xml/graphml">
+ <key id="d0" for="node" yfiles.type="nodegraphics"/>
+ <key id="d1" for="edge" yfiles.type="edgegraphics"/>
+ <graph id="G" edgedefault="directed">
+ <xsl:for-each select="dependencies/module">
+ <xsl:element name="node">
+ <xsl:attribute name="id"><xsl:value-of select="@organisation"/>-<xsl:value-of select="@name"/></xsl:attribute>
+ <data key="d0" >
+ <y:ShapeNode>
+ <y:Fill color="#FFFFCC" transparent="false"/>
+ <y:BorderStyle type="line" width="1.0" color="#000000" />
+ <y:NodeLabel visible="true" alignment="center" fontFamily="Dialog" fontSize="12" fontStyle="plain" textColor="#000000" modelName="internal" modelPosition="c" autoSizePolicy="center">
+ <xsl:value-of select="@name"/>
+ </y:NodeLabel>
+ <y:Shape type="roundrectangle"/>
+ </y:ShapeNode>
+ </data>
+ </xsl:element>
+ </xsl:for-each>
+ <xsl:for-each select="dependencies/module/revision[not(@evicted)]/caller[@organisation!='caller']">
+ <xsl:element name="edge">
+ <xsl:attribute name="id"><xsl:value-of select="@organisation"/>-<xsl:value-of select="@name"/>-<xsl:value-of select="../../@organisation"/>-<xsl:value-of select="../../@name"/></xsl:attribute>
+ <xsl:attribute name="source"><xsl:value-of select="@organisation"/>-<xsl:value-of select="@name"/></xsl:attribute>
+ <xsl:attribute name="target"><xsl:value-of select="../../@organisation"/>-<xsl:value-of select="../../@name"/></xsl:attribute>
+ <data key="d1">
+ <y:PolyLineEdge>
+ <y:LineStyle type="line" width="1.0" color="#000000" />
+ <y:Arrows source="none" target="standard"/>
+ <y:BendStyle smoothed="false"/>
+ </y:PolyLineEdge>
+ </data>
+ </xsl:element>
+ </xsl:for-each>
+ </graph>
+ </graphml>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/src/java/org/apache/ivy/plugins/report/ivy-report-graph.xsl b/src/java/org/apache/ivy/plugins/report/ivy-report-graph.xsl
new file mode 100644
index 0000000..88d6bac
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/report/ivy-report-graph.xsl
@@ -0,0 +1,85 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<xsl:stylesheet version="1.0"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+<xsl:template match="/ivy-report">
+ <graphml xmlns="http://graphml.graphdrawing.org/xmlns/graphml"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns/graphml http://www.yworks.com/xml/schema/graphml/1.0/ygraphml.xsd"
+ xmlns:y="http://www.yworks.com/xml/graphml">
+ <key id="d0" for="node" yfiles.type="nodegraphics"/>
+ <key id="d1" for="edge" yfiles.type="edgegraphics"/>
+ <graph id="G" edgedefault="directed">
+ <xsl:element name="node">
+ <xsl:attribute name="id"><xsl:value-of select="info/@organisation"/>-<xsl:value-of select="info/@module"/></xsl:attribute>
+ <data key="d0" >
+ <y:ShapeNode>
+ <y:Fill color="#CCCCFF" transparent="false"/>
+ <y:BorderStyle type="line" width="1.0" color="#000000" />
+ <y:NodeLabel visible="true" alignment="center" fontFamily="Dialog" fontSize="12" fontStyle="plain" textColor="#000000" modelName="internal" modelPosition="c" autoSizePolicy="center">
+ <xsl:value-of select="info/@module"/>
+ </y:NodeLabel>
+ <y:Shape type="roundrectangle"/>
+ </y:ShapeNode>
+ </data>
+ </xsl:element>
+ <xsl:for-each select="dependencies/module">
+ <xsl:element name="node">
+ <xsl:attribute name="id"><xsl:value-of select="@organisation"/>-<xsl:value-of select="@name"/></xsl:attribute>
+ <data key="d0" >
+ <y:ShapeNode>
+ <y:Fill color="#FFFFCC" transparent="false"/>
+ <y:BorderStyle type="line" width="1.0" color="#000000" />
+ <y:NodeLabel visible="true" alignment="center" fontFamily="Dialog" fontSize="12" fontStyle="plain" textColor="#000000" modelName="internal" modelPosition="c" autoSizePolicy="center">
+ <xsl:value-of select="@name"/>
+ <xsl:for-each select="revision">
+ <xsl:text>
+</xsl:text>
+ <xsl:value-of select="@name"/><xsl:if test="@error"> (error)</xsl:if><xsl:if test="@evicted"> (evicted)</xsl:if>
+ </xsl:for-each>
+ </y:NodeLabel>
+ <y:Shape type="roundrectangle"/>
+ </y:ShapeNode>
+ </data>
+ </xsl:element>
+ </xsl:for-each>
+ <xsl:for-each select="dependencies/module/revision[not(@evicted)]/caller">
+ <xsl:element name="edge">
+ <xsl:attribute name="id"><xsl:value-of select="@organisation"/>-<xsl:value-of select="@name"/>-<xsl:value-of select="../../@organisation"/>-<xsl:value-of select="../../@name"/></xsl:attribute>
+ <xsl:attribute name="source"><xsl:value-of select="@organisation"/>-<xsl:value-of select="@name"/></xsl:attribute>
+ <xsl:attribute name="target"><xsl:value-of select="../../@organisation"/>-<xsl:value-of select="../../@name"/></xsl:attribute>
+ <data key="d1">
+ <y:PolyLineEdge>
+ <y:LineStyle type="line" width="1.0" color="#000000" />
+ <y:Arrows source="none" target="standard"/>
+ <y:EdgeLabel visible="true" alignment="center" fontFamily="Dialog" fontSize="12" fontStyle="plain" textColor="#000000" modelName="free" modelPosition="anywhere" preferredPlacement="target" distance="2.0" ratio="0.5">
+ <xsl:value-of select="@rev"/>
+ </y:EdgeLabel>
+ <y:BendStyle smoothed="false"/>
+ </y:PolyLineEdge>
+ </data>
+ </xsl:element>
+ </xsl:for-each>
+ </graph>
+ </graphml>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/src/java/org/apache/ivy/plugins/report/ivy-report.css b/src/java/org/apache/ivy/plugins/report/ivy-report.css
new file mode 100644
index 0000000..135bc26
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/report/ivy-report.css
@@ -0,0 +1,279 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+body {
+ font-family:"Trebuchet MS",Verdana,Geneva,Arial,Helvetica,sans-serif;
+ font-size:small;
+}
+
+div#logo {
+ float: right;
+ padding-left: 10px;
+ padding-bottom: 10px;
+ background: white;
+ text-align: center;
+}
+
+#logo img {
+ border: 0;
+}
+
+div#date {
+ font-style: italic;
+ padding-left: 60px;
+ padding-bottom: 40px;
+}
+
+
+h1 {
+ margin-bottom:2px;
+
+ border-color:#7A9437;
+ border-style:solid;
+ border-width:0 0 3px 0;
+}
+
+span#module {
+ color:#7A9437;
+ text-decoration:none;
+}
+
+span#organisation {
+ color:black;
+ text-decoration:none;
+}
+
+#confmenu {
+ color: #000;
+ border-bottom: 2px solid black;
+ margin: 12px 0px 0px 0px;
+ padding: 0px;
+ z-index: 1;
+ padding-left: 10px
+}
+
+#confmenu li {
+ display: inline;
+ overflow: hidden;
+ list-style-type: none;
+}
+
+#confmenu a, a.active {
+ color: #DEDECF;
+ background: #898B5E;
+ font: bold 1em "Trebuchet MS", Arial, sans-serif;
+ border: 2px solid black;
+ padding: 2px 5px 0px 5px;
+ text-decoration: none;
+}
+
+/*
+background: #ABAD85 #CED4BD
+background: #DEE4CD
+ */
+
+#confmenu a.active {
+ color: #7A9437;
+ background: #DEE4CD;
+ border-bottom: 3px solid #DEE4CD;
+}
+
+#confmenu a:hover {
+ color: #fff;
+ background: #ADC09F;
+}
+
+#confmenu a:visited {
+ color: #DEDECF;
+}
+
+#confmenu a.active:visited {
+ color: #7A9437;
+}
+
+#confmenu a.active:hover {
+ background: #DEE4CD;
+ color: #DEDECF;
+}
+
+#content {
+ background: #DEE4CD;
+ padding: 20px;
+ border: 2px solid black;
+ border-top: none;
+ z-index: 2;
+}
+
+#content a {
+ text-decoration: none;
+ color: #E8E9BE;
+}
+
+#content a:hover {
+ background: #898B5E;
+}
+
+
+h2 {
+ margin-bottom:2px;
+ font-size:medium;
+
+ border-color:#7A9437;
+ border-style:solid;
+ border-width:0 0 2px 0;
+}
+
+h3 {
+ margin-top:30px;
+ margin-bottom:2px;
+ padding: 5 5 5 0;
+ font-size: 24px;
+ border-style:solid;
+ border-width:0 0 2px 0;
+}
+
+h4 {
+ margin-bottom:2px;
+ margin-top:2px;
+ font-size:medium;
+
+ border-color:#7A9437;
+ border-style:dashed;
+ border-width:0 0 1px 0;
+}
+
+h5 {
+ margin-bottom:2px;
+ margin-top:2px;
+ margin-left:20px;
+ font-size:medium;
+}
+
+span.resolved {
+ padding-left: 15px;
+ font-weight: 500;
+ font-size: small;
+}
+
+
+#content table {
+ border-collapse:collapse;
+ width:90%;
+ margin:auto;
+ margin-top: 5px;
+}
+#content thead {
+ background-color:#CED4BD;
+ border:1px solid #7A9437;
+}
+#content tbody {
+ border-collapse:collapse;
+ background-color:#FFFFFF;
+ border:1px solid #7A9437;
+}
+
+#content th {
+ font-family:monospace;
+ border:1px solid #7A9437;
+ padding:5px;
+}
+
+#content td {
+ border:1px dotted #7A9437;
+ padding:0 3 0 3;
+}
+
+#content table a {
+ color:#7A9437;
+ text-decoration:none;
+}
+
+#content table a:hover {
+ background-color:#CED4BD;
+ color:#7A9437;
+}
+
+
+
+table.deps {
+ border-collapse:collapse;
+ width:90%;
+ margin:auto;
+ margin-top: 5px;
+}
+
+table.deps thead {
+ background-color:#CED4BD;
+ border:1px solid #7A9437;
+}
+table.deps tbody {
+ border-collapse:collapse;
+ background-color:#FFFFFF;
+ border:1px solid #7A9437;
+}
+
+table.deps th {
+ font-family:monospace;
+ border:1px solid #7A9437;
+ padding:2;
+}
+
+table.deps td {
+ border:1px dotted #7A9437;
+ padding:0 3 0 3;
+}
+
+
+
+
+
+table.header {
+ border:0;
+ width:90%;
+ margin:auto;
+ margin-top: 5px;
+}
+
+table.header thead {
+ border:0;
+}
+table.header tbody {
+ border:0;
+}
+table.header tr {
+ padding:0px;
+ border:0;
+}
+table.header td {
+ padding:0 3 0 3;
+ border:0;
+}
+
+td.title {
+ width:150px;
+ margin-right:15px;
+
+ font-size:small;
+ font-weight:700;
+}
+
+td.title:first-letter {
+ color:#7A9437;
+ background-color:transparent;
+}
+
diff --git a/src/java/org/apache/ivy/plugins/report/ivy-report.xsl b/src/java/org/apache/ivy/plugins/report/ivy-report.xsl
new file mode 100644
index 0000000..af5f953
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/report/ivy-report.xsl
@@ -0,0 +1,514 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<xsl:stylesheet version="1.0"
+ xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+<xsl:param name="confs" select="/ivy-report/info/@confs"/>
+<xsl:param name="extension" select="'xml'"/>
+
+<xsl:variable name="myorg" select="/ivy-report/info/@organisation"/>
+<xsl:variable name="mymod" select="/ivy-report/info/@module"/>
+<xsl:variable name="myconf" select="/ivy-report/info/@conf"/>
+
+<xsl:variable name="modules" select="/ivy-report/dependencies/module"/>
+<xsl:variable name="conflicts" select="$modules[count(revision) > 1]"/>
+
+<xsl:variable name="revisions" select="$modules/revision"/>
+<xsl:variable name="evicteds" select="$revisions[@evicted]"/>
+<xsl:variable name="downloadeds" select="$revisions[@downloaded='true']"/>
+<xsl:variable name="searcheds" select="$revisions[@searched='true']"/>
+<xsl:variable name="errors" select="$revisions[@error]"/>
+
+<xsl:variable name="artifacts" select="$revisions/artifacts/artifact"/>
+<xsl:variable name="cacheartifacts" select="$artifacts[@status='no']"/>
+<xsl:variable name="dlartifacts" select="$artifacts[@status='successful']"/>
+<xsl:variable name="faileds" select="$artifacts[@status='failed']"/>
+<xsl:variable name="artifactsok" select="$artifacts[@status!='failed']"/>
+
+<xsl:template name="calling">
+ <xsl:param name="org" />
+ <xsl:param name="mod" />
+ <xsl:param name="rev" />
+ <xsl:if test="count($modules/revision/caller[(@organisation=$org and @name=$mod) and @callerrev=$rev]) = 0">
+ <table><tr><td>
+ No dependency
+ </td></tr></table>
+ </xsl:if>
+ <xsl:if test="count($modules/revision/caller[(@organisation=$org and @name=$mod) and @callerrev=$rev]) > 0">
+ <table class="deps">
+ <thead>
+ <tr>
+ <th>Module</th>
+ <th>Revision</th>
+ <th>Status</th>
+ <th>Resolver</th>
+ <th>Default</th>
+ <th>Licenses</th>
+ <th>Size</th>
+ <th></th>
+ </tr>
+ </thead>
+ <tbody>
+ <xsl:for-each select="$modules/revision/caller[(@organisation=$org and @name=$mod) and @callerrev=$rev]">
+ <xsl:call-template name="called">
+ <xsl:with-param name="callstack" select="concat($org, string('/'), $mod)"/>
+ <xsl:with-param name="indent" select="string('')"/>
+ <xsl:with-param name="revision" select=".."/>
+ </xsl:call-template>
+ </xsl:for-each>
+ </tbody>
+ </table>
+ </xsl:if>
+</xsl:template>
+
+<xsl:template name="called">
+ <xsl:param name="callstack"/>
+ <xsl:param name="indent"/>
+ <xsl:param name="revision"/>
+
+ <xsl:param name="organisation" select="$revision/../@organisation"/>
+ <xsl:param name="module" select="$revision/../@name"/>
+ <xsl:param name="rev" select="$revision/@name"/>
+ <xsl:param name="resolver" select="$revision/@resolver"/>
+ <xsl:param name="isdefault" select="$revision/@default"/>
+ <xsl:param name="status" select="$revision/@status"/>
+ <tr>
+ <td>
+ <xsl:element name="a">
+ <xsl:attribute name="href">#<xsl:value-of select="$organisation"/>-<xsl:value-of select="$module"/></xsl:attribute>
+ <xsl:value-of select="concat($indent, ' ')"/>
+ <xsl:value-of select="$module"/>
+ by
+ <xsl:value-of select="$organisation"/>
+ </xsl:element>
+ </td>
+ <td>
+ <xsl:element name="a">
+ <xsl:attribute name="href">#<xsl:value-of select="$organisation"/>-<xsl:value-of select="$module"/>-<xsl:value-of select="$rev"/></xsl:attribute>
+ <xsl:value-of select="$rev"/>
+ </xsl:element>
+ </td>
+ <td align="center">
+ <xsl:value-of select="$status"/>
+ </td>
+ <td align="center">
+ <xsl:value-of select="$resolver"/>
+ </td>
+ <td align="center">
+ <xsl:value-of select="$isdefault"/>
+ </td>
+ <td align="center">
+ <xsl:call-template name="licenses">
+ <xsl:with-param name="revision" select="$revision"/>
+ </xsl:call-template>
+ </td>
+ <td align="center">
+ <xsl:value-of select="round(sum($revision/artifacts/artifact/@size) div 1024)"/> kB
+ </td>
+ <td align="center">
+ <xsl:call-template name="icons">
+ <xsl:with-param name="revision" select="$revision"/>
+ </xsl:call-template>
+ </td>
+ </tr>
+ <xsl:if test="not($revision/@evicted)">
+ <xsl:if test="not(contains($callstack, concat($organisation, string('/'), $module)))">
+ <xsl:for-each select="$modules/revision/caller[(@organisation=$organisation and @name=$module) and @callerrev=$rev]">
+ <xsl:call-template name="called">
+ <xsl:with-param name="callstack" select="concat($callstack, string('#'), $organisation, string('/'), $module)"/>
+ <xsl:with-param name="indent" select="concat($indent, string('---'))"/>
+ <xsl:with-param name="revision" select=".."/>
+ </xsl:call-template>
+ </xsl:for-each>
+ </xsl:if>
+ </xsl:if>
+</xsl:template>
+
+<xsl:template name="licenses">
+ <xsl:param name="revision"/>
+ <xsl:for-each select="$revision/license">
+ <span style="padding-right:3px;">
+ <xsl:if test="@url">
+ <xsl:element name="a">
+ <xsl:attribute name="href"><xsl:value-of select="@url"/></xsl:attribute>
+ <xsl:value-of select="@name"/>
+ </xsl:element>
+ </xsl:if>
+ <xsl:if test="not(@url)">
+ <xsl:value-of select="@name"/>
+ </xsl:if>
+ </span>
+ </xsl:for-each>
+</xsl:template>
+
+<xsl:template name="icons">
+ <xsl:param name="revision"/>
+ <xsl:if test="$revision/@searched = 'true'">
+ <img src="http://ant.apache.org/ivy/images/searched.gif" alt="searched" title="required a search in repository"/>
+ </xsl:if>
+ <xsl:if test="$revision/@downloaded = 'true'">
+ <img src="http://ant.apache.org/ivy/images/downloaded.gif" alt="downloaded" title="downloaded from repository"/>
+ </xsl:if>
+ <xsl:if test="$revision/@evicted">
+ <xsl:element name="img">
+ <xsl:attribute name="src">http://ant.apache.org/ivy/images/evicted.gif</xsl:attribute>
+ <xsl:attribute name="alt">evicted</xsl:attribute>
+ <xsl:attribute name="title">evicted by <xsl:for-each select="$revision/evicted-by"><xsl:value-of select="@rev"/> </xsl:for-each></xsl:attribute>
+ </xsl:element>
+ </xsl:if>
+ <xsl:if test="$revision/@error">
+ <xsl:element name="img">
+ <xsl:attribute name="src">http://ant.apache.org/ivy/images/error.gif</xsl:attribute>
+ <xsl:attribute name="alt">error</xsl:attribute>
+ <xsl:attribute name="title">error: <xsl:value-of select="$revision/@error"/></xsl:attribute>
+ </xsl:element>
+ </xsl:if>
+</xsl:template>
+
+<xsl:template name="error">
+ <xsl:param name="organisation"/>
+ <xsl:param name="module"/>
+ <xsl:param name="revision"/>
+ <xsl:param name="error"/>
+ <tr>
+ <td>
+ <xsl:element name="a">
+ <xsl:attribute name="href">#<xsl:value-of select="$organisation"/>-<xsl:value-of select="$module"/></xsl:attribute>
+ <xsl:value-of select="$module"/>
+ by
+ <xsl:value-of select="$organisation"/>
+ </xsl:element>
+ </td>
+ <td>
+ <xsl:element name="a">
+ <xsl:attribute name="href">#<xsl:value-of select="$organisation"/>-<xsl:value-of select="$module"/>-<xsl:value-of select="$revision"/></xsl:attribute>
+ <xsl:value-of select="$revision"/>
+ </xsl:element>
+ </td>
+ <td>
+ <xsl:value-of select="$error"/>
+ </td>
+ </tr>
+</xsl:template>
+
+<xsl:template name="confs">
+ <xsl:param name="configurations"/>
+
+ <xsl:if test="contains($configurations, ',')">
+ <xsl:call-template name="conf">
+ <xsl:with-param name="conf" select="normalize-space(substring-before($configurations,','))"/>
+ </xsl:call-template>
+ <xsl:call-template name="confs">
+ <xsl:with-param name="configurations" select="substring-after($configurations,',')"/>
+ </xsl:call-template>
+ </xsl:if>
+ <xsl:if test="not(contains($configurations, ','))">
+ <xsl:call-template name="conf">
+ <xsl:with-param name="conf" select="normalize-space($configurations)"/>
+ </xsl:call-template>
+ </xsl:if>
+</xsl:template>
+
+<xsl:template name="conf">
+ <xsl:param name="conf"/>
+
+ <li>
+ <xsl:element name="a">
+ <xsl:if test="$conf = $myconf">
+ <xsl:attribute name="class">active</xsl:attribute>
+ </xsl:if>
+ <xsl:attribute name="href"><xsl:value-of select="$myorg"/>-<xsl:value-of select="$mymod"/>-<xsl:value-of select="$conf"/>.<xsl:value-of select="$extension"/></xsl:attribute>
+ <xsl:value-of select="$conf"/>
+ </xsl:element>
+ </li>
+</xsl:template>
+
+<xsl:template name="date">
+ <xsl:param name="date"/>
+
+ <xsl:value-of select="substring($date,1,4)"/>-<xsl:value-of select="substring($date,5,2)"/>-<xsl:value-of select="substring($date,7,2)"/>
+ <xsl:value-of select="' '"/>
+ <xsl:value-of select="substring($date,9,2)"/>:<xsl:value-of select="substring($date,11,2)"/>:<xsl:value-of select="substring($date,13)"/>
+</xsl:template>
+
+
+<xsl:template match="/ivy-report">
+
+ <html>
+ <head>
+ <title>Ivy report :: <xsl:value-of select="info/@module"/> by <xsl:value-of select="info/@organisation"/> :: <xsl:value-of select="info/@conf"/></title>
+ <meta http-equiv="content-type" content="text/html; charset=ISO-8859-1" />
+ <meta http-equiv="content-language" content="en" />
+ <meta name="robots" content="index,follow" />
+ <link rel="stylesheet" type="text/css" href="ivy-report.css" />
+ </head>
+ <body>
+ <div id="logo"><a href="http://ant.apache.org/ivy/"><img src="http://ant.apache.org/ivy/images/logo.png"/></a></div>
+ <h1>
+ <xsl:element name="a">
+ <xsl:attribute name="name"><xsl:value-of select="info/@organisation"/>-<xsl:value-of select="info/@module"/></xsl:attribute>
+ </xsl:element>
+ <span id="module">
+ <xsl:value-of select="concat(info/@module, ' ', info/@revision)"/>
+ </span>
+ by
+ <span id="organisation">
+ <xsl:value-of select="info/@organisation"/>
+ </span>
+ </h1>
+ <div id="date">
+ resolved on
+ <xsl:call-template name="date">
+ <xsl:with-param name="date" select="info/@date"/>
+ </xsl:call-template>
+ </div>
+ <ul id="confmenu">
+ <xsl:call-template name="confs">
+ <xsl:with-param name="configurations" select="$confs"/>
+ </xsl:call-template>
+ </ul>
+
+ <div id="content">
+ <h2>Dependencies Stats</h2>
+ <table class="header">
+ <tr><td class="title">Modules</td><td class="value"><xsl:value-of select="count($modules)"/></td></tr>
+ <tr><td class="title">Revisions</td><td class="value"><xsl:value-of select="count($revisions)"/>
+ (<xsl:value-of select="count($searcheds)"/> searched <img src="http://ant.apache.org/ivy/images/searched.gif" alt="searched" title="module revisions which required a search with a dependency resolver to be resolved"/>,
+ <xsl:value-of select="count($downloadeds)"/> downloaded <img src="http://ant.apache.org/ivy/images/downloaded.gif" alt="downloaded" title="module revisions for which ivy file was downloaded by dependency resolver"/>,
+ <xsl:value-of select="count($evicteds)"/> evicted <img src="http://ant.apache.org/ivy/images/evicted.gif" alt="evicted" title="module revisions which were evicted by others"/>,
+ <xsl:value-of select="count($errors)"/> errors <img src="http://ant.apache.org/ivy/images/error.gif" alt="error" title="module revisions on which error occurred"/>)</td></tr>
+ <tr><td class="title">Artifacts</td><td class="value"><xsl:value-of select="count($artifacts)"/>
+ (<xsl:value-of select="count($dlartifacts)"/> downloaded,
+ <xsl:value-of select="count($faileds)"/> failed)</td></tr>
+ <tr><td class="title">Artifacts size</td><td class="value"><xsl:value-of select="round(sum($artifacts/@size) div 1024)"/> kB
+ (<xsl:value-of select="round(sum($dlartifacts/@size) div 1024)"/> kB downloaded,
+ <xsl:value-of select="round(sum($cacheartifacts/@size) div 1024)"/> kB in cache)</td></tr>
+ </table>
+
+ <xsl:if test="count($errors) > 0">
+ <h2>Errors</h2>
+ <table class="errors">
+ <thead>
+ <tr>
+ <th>Module</th>
+ <th>Revision</th>
+ <th>Error</th>
+ </tr>
+ </thead>
+ <tbody>
+ <xsl:for-each select="$errors">
+ <xsl:call-template name="error">
+ <xsl:with-param name="organisation" select="../@organisation"/>
+ <xsl:with-param name="module" select="../@name"/>
+ <xsl:with-param name="revision" select="@name"/>
+ <xsl:with-param name="error" select="@error"/>
+ </xsl:call-template>
+ </xsl:for-each>
+ </tbody>
+ </table>
+ </xsl:if>
+
+ <xsl:if test="count($conflicts) > 0">
+ <h2>Conflicts</h2>
+ <table class="conflicts">
+ <thead>
+ <tr>
+ <th>Module</th>
+ <th>Selected</th>
+ <th>Evicted</th>
+ </tr>
+ </thead>
+ <tbody>
+ <xsl:for-each select="$conflicts">
+ <tr>
+ <td>
+ <xsl:element name="a">
+ <xsl:attribute name="href">#<xsl:value-of select="@organisation"/>-<xsl:value-of select="@name"/></xsl:attribute>
+ <xsl:value-of select="@name"/>
+ by
+ <xsl:value-of select="@organisation"/>
+ </xsl:element>
+ </td>
+ <td>
+ <xsl:for-each select="revision[not(@evicted)]">
+ <xsl:element name="a">
+ <xsl:attribute name="href">#<xsl:value-of select="../@organisation"/>-<xsl:value-of select="../@name"/>-<xsl:value-of select="@name"/></xsl:attribute>
+ <xsl:value-of select="@name"/>
+ </xsl:element>
+ <xsl:text> </xsl:text>
+ </xsl:for-each>
+ </td>
+ <td>
+ <xsl:for-each select="revision[@evicted]">
+ <xsl:element name="a">
+ <xsl:attribute name="href">#<xsl:value-of select="../@organisation"/>-<xsl:value-of select="../@name"/>-<xsl:value-of select="@name"/></xsl:attribute>
+ <xsl:value-of select="@name"/>
+ <xsl:text> </xsl:text>
+ <xsl:value-of select="@evicted-reason"/>
+ </xsl:element>
+ <xsl:text> </xsl:text>
+ </xsl:for-each>
+ </td>
+ </tr>
+ </xsl:for-each>
+ </tbody>
+ </table>
+ </xsl:if>
+
+ <h2>Dependencies Overview</h2>
+ <xsl:call-template name="calling">
+ <xsl:with-param name="org" select="info/@organisation"/>
+ <xsl:with-param name="mod" select="info/@module"/>
+ <xsl:with-param name="rev" select="info/@revision"/>
+ </xsl:call-template>
+
+ <h2>Details</h2>
+ <xsl:for-each select="$modules">
+ <h3>
+ <xsl:element name="a">
+ <xsl:attribute name="name"><xsl:value-of select="@organisation"/>-<xsl:value-of select="@name"/></xsl:attribute>
+ </xsl:element>
+ <xsl:value-of select="@name"/> by <xsl:value-of select="@organisation"/>
+ </h3>
+ <xsl:for-each select="revision">
+ <h4>
+ <xsl:element name="a">
+ <xsl:attribute name="name"><xsl:value-of select="../@organisation"/>-<xsl:value-of select="../@name"/>-<xsl:value-of select="@name"/></xsl:attribute>
+ </xsl:element>
+ Revision: <xsl:value-of select="@name"/>
+ <span style="padding-left:15px;">
+ <xsl:call-template name="icons">
+ <xsl:with-param name="revision" select="."/>
+ </xsl:call-template>
+ </span>
+ </h4>
+ <table class="header">
+ <xsl:if test="@homepage">
+ <tr><td class="title">Home Page</td><td class="value">
+ <xsl:element name="a">
+ <xsl:attribute name="href"><xsl:value-of select="@homepage"/></xsl:attribute>
+ <xsl:value-of select="@homepage"/>
+ </xsl:element></td>
+ </tr>
+ </xsl:if>
+ <tr><td class="title">Status</td><td class="value"><xsl:value-of select="@status"/></td></tr>
+ <tr><td class="title">Publication</td><td class="value"><xsl:value-of select="@pubdate"/></td></tr>
+ <tr><td class="title">Resolver</td><td class="value"><xsl:value-of select="@resolver"/></td></tr>
+ <tr><td class="title">Configurations</td><td class="value"><xsl:value-of select="@conf"/></td></tr>
+ <tr><td class="title">Artifacts size</td><td class="value"><xsl:value-of select="round(sum(artifacts/artifact/@size) div 1024)"/> kB
+ (<xsl:value-of select="round(sum(artifacts/artifact[@status='successful']/@size) div 1024)"/> kB downloaded,
+ <xsl:value-of select="round(sum(artifacts/artifact[@status='no']/@size) div 1024)"/> kB in cache)</td></tr>
+ <xsl:if test="count(license) > 0">
+ <tr><td class="title">Licenses</td><td class="value">
+ <xsl:call-template name="licenses">
+ <xsl:with-param name="revision" select="."/>
+ </xsl:call-template>
+ </td></tr>
+ </xsl:if>
+ <xsl:if test="@evicted">
+ <tr><td class="title">Evicted by</td><td class="value">
+ <b>
+ <xsl:for-each select="evicted-by">
+ <xsl:value-of select="@rev"/>
+ <xsl:text> </xsl:text>
+ </xsl:for-each>
+ </b>
+ <xsl:text> </xsl:text>
+ <b><xsl:value-of select="@evicted-reason"/></b>
+ in <b><xsl:value-of select="@evicted"/></b> conflict manager
+ </td></tr>
+ </xsl:if>
+ </table>
+ <h5>Required by</h5>
+ <table>
+ <thead>
+ <tr>
+ <th>Organisation</th>
+ <th>Name</th>
+ <th>Revision</th>
+ <th>In Configurations</th>
+ <th>Asked Revision</th>
+ </tr>
+ </thead>
+ <tbody>
+ <xsl:for-each select="caller">
+ <tr>
+ <td><xsl:value-of select="@organisation"/></td>
+ <td>
+ <xsl:element name="a">
+ <xsl:attribute name="href">#<xsl:value-of select="@organisation"/>-<xsl:value-of select="@name"/></xsl:attribute>
+ <xsl:value-of select="@name"/>
+ </xsl:element>
+ </td>
+ <td><xsl:value-of select="@callerrev"/></td>
+ <td><xsl:value-of select="@conf"/></td>
+ <td><xsl:value-of select="@rev"/></td>
+ </tr>
+ </xsl:for-each>
+ </tbody>
+ </table>
+ <xsl:if test="not(@evicted)">
+
+ <h5>Dependencies</h5>
+ <xsl:call-template name="calling">
+ <xsl:with-param name="org" select="../@organisation"/>
+ <xsl:with-param name="mod" select="../@name"/>
+ <xsl:with-param name="rev" select="@name"/>
+ </xsl:call-template>
+ <h5>Artifacts</h5>
+ <xsl:if test="count(artifacts/artifact) = 0">
+ <table><tr><td>
+ No artifact
+ </td></tr></table>
+ </xsl:if>
+ <xsl:if test="count(artifacts/artifact) > 0">
+ <table>
+ <thead>
+ <tr>
+ <th>Name</th>
+ <th>Type</th>
+ <th>Ext</th>
+ <th>Download</th>
+ <th>Size</th>
+ </tr>
+ </thead>
+ <tbody>
+ <xsl:for-each select="artifacts/artifact">
+ <tr>
+ <td><xsl:value-of select="@name"/></td>
+ <td><xsl:value-of select="@type"/></td>
+ <td><xsl:value-of select="@ext"/></td>
+ <td align="center"><xsl:value-of select="@status"/></td>
+ <td align="center"><xsl:value-of select="round(number(@size) div 1024)"/> kB</td>
+ </tr>
+ </xsl:for-each>
+ </tbody>
+ </table>
+ </xsl:if>
+
+ </xsl:if>
+ </xsl:for-each>
+ </xsl:for-each>
+ </div>
+ </body>
+ </html>
+</xsl:template>
+
+</xsl:stylesheet>
diff --git a/src/java/org/apache/ivy/plugins/repository/AbstractRepository.java b/src/java/org/apache/ivy/plugins/repository/AbstractRepository.java
new file mode 100644
index 0000000..f1f4238
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/repository/AbstractRepository.java
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.repository;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+
+import javax.swing.event.EventListenerList;
+
+import org.apache.ivy.core.module.descriptor.Artifact;
+
+public abstract class AbstractRepository implements Repository {
+ private EventListenerList listeners = new EventListenerList();
+
+ private String name;
+
+ private TransferEvent evt;
+
+ public void addTransferListener(TransferListener listener) {
+ listeners.add(TransferListener.class, listener);
+ }
+
+ public void removeTransferListener(TransferListener listener) {
+ listeners.remove(TransferListener.class, listener);
+ }
+
+ public boolean hasTransferListener(TransferListener listener) {
+ return Arrays.asList(listeners.getListeners(TransferListener.class)).contains(listener);
+ }
+
+ protected void fireTransferInitiated(Resource res, int requestType) {
+ evt = new TransferEvent(this, res, TransferEvent.TRANSFER_INITIATED, requestType);
+ fireTransferEvent(evt);
+ }
+
+ protected void fireTransferStarted() {
+ evt.setEventType(TransferEvent.TRANSFER_STARTED);
+ fireTransferEvent(evt);
+ }
+
+ protected void fireTransferStarted(long totalLength) {
+ evt.setEventType(TransferEvent.TRANSFER_STARTED);
+ evt.setTotalLength(totalLength);
+ evt.setTotalLengthSet(true);
+ fireTransferEvent(evt);
+ }
+
+ protected void fireTransferProgress(long length) {
+ evt.setEventType(TransferEvent.TRANSFER_PROGRESS);
+ evt.setLength(length);
+ if (!evt.isTotalLengthSet()) {
+ evt.setTotalLength(evt.getTotalLength() + length);
+ }
+ fireTransferEvent(evt);
+ }
+
+ protected void fireTransferCompleted() {
+ evt.setEventType(TransferEvent.TRANSFER_COMPLETED);
+ if (evt.getTotalLength() > 0 && !evt.isTotalLengthSet()) {
+ evt.setTotalLengthSet(true);
+ }
+ fireTransferEvent(evt);
+ }
+
+ protected void fireTransferCompleted(long totalLength) {
+ evt.setEventType(TransferEvent.TRANSFER_COMPLETED);
+ evt.setTotalLength(totalLength);
+ evt.setTotalLengthSet(true);
+ fireTransferEvent(evt);
+ }
+
+ protected void fireTransferError() {
+ evt.setEventType(TransferEvent.TRANSFER_ERROR);
+ fireTransferEvent(evt);
+ }
+
+ protected void fireTransferError(Exception ex) {
+ evt.setEventType(TransferEvent.TRANSFER_ERROR);
+ evt.setException(ex);
+ fireTransferEvent(evt);
+ }
+
+ protected void fireTransferEvent(TransferEvent evt) {
+ Object[] listeners = this.listeners.getListenerList();
+ for (int i = listeners.length - 2; i >= 0; i -= 2) {
+ if (listeners[i] == TransferListener.class) {
+ ((TransferListener) listeners[i + 1]).transferProgress(evt);
+ }
+ }
+ }
+
+ public String getFileSeparator() {
+ return "/";
+ }
+
+ public String standardize(String source) {
+ return source.replace('\\', '/');
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String toString() {
+ return getName();
+ }
+
+ public void put(Artifact artifact, File source, String destination, boolean overwrite)
+ throws IOException {
+ put(source, destination, overwrite);
+ }
+
+ protected void put(File source, String destination, boolean overwrite) throws IOException {
+ throw new UnsupportedOperationException("put in not supported by " + getName());
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/repository/ArtifactResourceResolver.java b/src/java/org/apache/ivy/plugins/repository/ArtifactResourceResolver.java
new file mode 100644
index 0000000..6cf719f
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/repository/ArtifactResourceResolver.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.repository;
+
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.plugins.resolver.util.ResolvedResource;
+
+/**
+ * An {@link ArtifactResourceResolver} is responsible for the resolution of an artifact into a
+ * {@link ResolvedResource}.
+ */
+public interface ArtifactResourceResolver {
+ public ResolvedResource resolve(Artifact artifact);
+}
diff --git a/src/java/org/apache/ivy/plugins/repository/BasicResource.java b/src/java/org/apache/ivy/plugins/repository/BasicResource.java
new file mode 100644
index 0000000..9184f29
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/repository/BasicResource.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.repository;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public class BasicResource implements Resource {
+ private boolean local;
+
+ private String name;
+
+ private long lastModified;
+
+ private long contentLength;
+
+ private boolean exists;
+
+ public BasicResource(String name, boolean exists, long contentLength, long lastModified,
+ boolean local) {
+ this.name = name;
+ this.exists = exists;
+ this.contentLength = contentLength;
+ this.lastModified = lastModified;
+ this.local = local;
+ }
+
+ public Resource clone(String cloneName) {
+ throw new UnsupportedOperationException("basic resource do not support the clone method");
+ }
+
+ public boolean exists() {
+ return this.exists;
+ }
+
+ public long getContentLength() {
+ return this.contentLength;
+ }
+
+ public long getLastModified() {
+ return this.lastModified;
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ public boolean isLocal() {
+ return this.local;
+ }
+
+ public InputStream openStream() throws IOException {
+ throw new UnsupportedOperationException(
+ "basic resource do not support the openStream method");
+ }
+
+ public String toString() {
+ return getName();
+ }
+
+}
diff --git a/src/java/org/apache/ivy/plugins/repository/LazyResource.java b/src/java/org/apache/ivy/plugins/repository/LazyResource.java
new file mode 100644
index 0000000..12dbf84
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/repository/LazyResource.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.repository;
+
+public abstract class LazyResource implements Resource {
+ private boolean init = false;
+
+ private boolean local;
+
+ private String name;
+
+ private long lastModified;
+
+ private long contentLength;
+
+ private boolean exists;
+
+ public LazyResource(String name) {
+ this.name = name;
+ }
+
+ protected abstract void init();
+
+ private void checkInit() {
+ if (!init) {
+ init();
+ init = true;
+ }
+ }
+
+ public boolean exists() {
+ checkInit();
+ return exists;
+ }
+
+ public long getContentLength() {
+ checkInit();
+ return contentLength;
+ }
+
+ public long getLastModified() {
+ checkInit();
+ return lastModified;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public boolean isLocal() {
+ checkInit();
+ return local;
+ }
+
+ public String toString() {
+ return getName();
+ }
+
+ protected void setContentLength(long contentLength) {
+ this.contentLength = contentLength;
+ }
+
+ protected void setExists(boolean exists) {
+ this.exists = exists;
+ }
+
+ protected void setLastModified(long lastModified) {
+ this.lastModified = lastModified;
+ }
+
+ protected void setLocal(boolean local) {
+ this.local = local;
+ }
+
+ protected void init(Resource r) {
+ setContentLength(r.getContentLength());
+ setLocal(r.isLocal());
+ setLastModified(r.getLastModified());
+ setExists(r.exists());
+ }
+
+}
diff --git a/src/java/org/apache/ivy/plugins/repository/LocalizableResource.java b/src/java/org/apache/ivy/plugins/repository/LocalizableResource.java
new file mode 100644
index 0000000..18be7ca
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/repository/LocalizableResource.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.repository;
+
+import java.io.File;
+
+/**
+ * Resource which can be located locally
+ * <p>
+ * If local (checked via {@link #isLocal()}), {@link #getFile()} will return its local location,
+ * without any download or copy involved
+ * </p>
+ */
+public interface LocalizableResource extends Resource {
+
+ /**
+ * @return the local file of this resource.
+ * @throws IllegalStateException
+ * when {@link #isLocal()} returns <code>false</code>
+ */
+ public File getFile();
+
+}
diff --git a/src/java/org/apache/ivy/plugins/repository/Repository.java b/src/java/org/apache/ivy/plugins/repository/Repository.java
new file mode 100644
index 0000000..9534c99
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/repository/Repository.java
@@ -0,0 +1,153 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.repository;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+import org.apache.ivy.core.module.descriptor.Artifact;
+
+/**
+ * Represents a collection of resources available to Ivy. Ivy uses one or more repositories as both
+ * a source of resources for Ivy enabled build systems and as a distribution center for resources
+ * generated by Ivy enabled build systems. </p>
+ * <p>
+ * A repository supports the following fundamental operations
+ * <ul>
+ * <li>retrieving a resource from the repository.</li>
+ * <li>transferring a resource to the repository.</li>
+ * <li>retrieving a listing of resources.</li>
+ * </ul>
+ * </p>
+ * <h4>Resource Retrieval</h4> </p>
+ * <p>
+ * {@link #get} retrieves a resource specified by a provided identifier creating a new file .
+ * </p>
+ * </p> <h4>resource Publication</h4> </p>
+ * <p>
+ * {@link #put} transfers a file to the repository.
+ * </p>
+ * </p> <h4>resource Listing</h4> </p>
+ * <p>
+ * {@link #list} returns a listing of file like objects belonging to a specified parent directory.
+ * </p>
+ * </p>
+ */
+public interface Repository {
+
+ /**
+ * Return the resource associated with a specified identifier. If the resource does not exist,
+ * it should return a Resource with exists() returning false. An IOException should only be
+ * thrown when a real IO problem occurs, like the impossibility to connect to a server.
+ *
+ * @param source
+ * A string identifying the resource.
+ * @return The resource associated with the resource identifier.
+ * @throws IOException
+ * On error while trying to get resource.
+ */
+ Resource getResource(String source) throws IOException;
+
+ /**
+ * Fetch a resource from the repository.
+ *
+ * @param source
+ * A string identifying the resource to be fetched.
+ * @param destination
+ * Where to place the fetched resource.
+ * @throws IOException
+ * On retrieval failure.
+ */
+ void get(String source, File destination) throws IOException;
+
+ /**
+ * Transfer a resource to the repository
+ *
+ * @param artifact
+ * The artifact to be transferred.
+ * @param source
+ * The local file to be transferred.
+ * @param destination
+ * Where to transfer the resource.
+ * @param overwrite
+ * Whether the transfer should overwrite an existing resource.
+ * @throws IOException
+ * On publication failure.
+ */
+ void put(Artifact artifact, File source, String destination, boolean overwrite)
+ throws IOException;
+
+ /**
+ * Return a listing of resources names
+ *
+ * @param parent
+ * The parent directory from which to generate the listing.
+ * @return A listing of the parent directory's file content, as a List of String.
+ * @throws IOException
+ * On listing failure.
+ */
+ List list(String parent) throws IOException;
+
+ /**
+ * Add a listener to the repository.
+ *
+ * @param listener
+ * The listener to attach to the repository.
+ */
+ void addTransferListener(TransferListener listener);
+
+ /**
+ * Remove a listener on the repository
+ *
+ * @param listener
+ * The listener to remove
+ */
+ void removeTransferListener(TransferListener listener);
+
+ /**
+ * Determine if a given listener is attached to the repository.
+ *
+ * @param listener
+ * The listener being queried
+ * @return <code>true</code> if the provided listener is attached to the repository,
+ * <code>false</code> if not.
+ */
+ boolean hasTransferListener(TransferListener listener);
+
+ /**
+ * Get the repository's file separator string.
+ *
+ * @return The repository's file separator delimiter
+ */
+ String getFileSeparator();
+
+ /**
+ * Normalize a string.
+ *
+ * @param source
+ * The string to normalize.
+ * @return The normalized string.
+ */
+ String standardize(String source);
+
+ /**
+ * Return the name of the repository
+ */
+ String getName();
+}
diff --git a/src/java/org/apache/ivy/plugins/repository/RepositoryCopyProgressListener.java b/src/java/org/apache/ivy/plugins/repository/RepositoryCopyProgressListener.java
new file mode 100644
index 0000000..2f89630
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/repository/RepositoryCopyProgressListener.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.repository;
+
+import org.apache.ivy.util.CopyProgressEvent;
+import org.apache.ivy.util.CopyProgressListener;
+
+public class RepositoryCopyProgressListener implements CopyProgressListener {
+ private final AbstractRepository repository;
+
+ public RepositoryCopyProgressListener(AbstractRepository repository) {
+ this.repository = repository;
+ }
+
+ private Long totalLength = null;
+
+ public void start(CopyProgressEvent evt) {
+ if (totalLength != null) {
+ repository.fireTransferStarted(totalLength.longValue());
+ } else {
+ repository.fireTransferStarted();
+ }
+ }
+
+ public void progress(CopyProgressEvent evt) {
+ repository.fireTransferProgress(evt.getReadBytes());
+ }
+
+ public void end(CopyProgressEvent evt) {
+ repository.fireTransferProgress(evt.getReadBytes());
+ repository.fireTransferCompleted();
+ }
+
+ public Long getTotalLength() {
+ return totalLength;
+ }
+
+ public void setTotalLength(Long totalLength) {
+ this.totalLength = totalLength;
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/repository/Resource.java b/src/java/org/apache/ivy/plugins/repository/Resource.java
new file mode 100644
index 0000000..ea3ef01
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/repository/Resource.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.repository;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Represents a resource in an Ivy {@link Repository}. The resource interface allows one to obtain
+ * the following information about a resource:
+ * <ul>
+ * <li>resource name/identifier in repository syntax</li>
+ * <li>date the resource was last modified.</li>
+ * <li>size of the resource in bytes.</li>
+ * <li>if the resource is available.</li>
+ * </ul>
+ * </p> <h4>Implementation Notes</h4> In implementing the interface you need to ensure the following
+ * behaviors:
+ * <ul>
+ * <li>All of the methods specified in the interface fail by returning an empty value (
+ * <code>false</code>, <code>0</code>, <code>""</code>). In other words, the specified interface
+ * methods should not throw RuntimeExceptions.</li>
+ * <li>Failure conditions should be logged using the {@link org.apache.ivy.util.Message#verbose}
+ * method.</li>
+ * <li>Failure of one of the interface's specified methods results in all other interface specified
+ * methods returning an empty value (<code>false</code>, <code>0</code>, <code>""</code>).</li>
+ * </ul>
+ * </p>
+ */
+
+public interface Resource {
+ /**
+ * Get the name of the resource.
+ *
+ * @return the repositorie's assigned resource name/identifier.
+ */
+ public String getName();
+
+ /**
+ * Get the date the resource was last modified
+ *
+ * @return A <code>long</code> value representing the time the file was last modified, measured
+ * in milliseconds since the epoch (00:00:00 GMT, January 1, 1970), or <code>0L</code>
+ * if the file does not exist or if an I/O error occurs.
+ */
+ public long getLastModified();
+
+ /**
+ * Get the resource size
+ *
+ * @return a <code>long</code> value representing the size of the resource in bytes.
+ */
+ public long getContentLength();
+
+ /**
+ * Determine if the resource is available. </p> Note that this method only checks for
+ * availability, not for actual existence.
+ *
+ * @return <code>boolean</code> value indicating if the resource is available.
+ */
+ public boolean exists();
+
+ /**
+ * Is this resource local to this host, i.e. is it on the file system?
+ *
+ * @return <code>boolean</code> value indicating if the resource is local.
+ */
+ public boolean isLocal();
+
+ /**
+ * Clones this resource with a new resource with a different name
+ *
+ * @param cloneName
+ * the name of the clone
+ * @return the cloned resource
+ */
+ public Resource clone(String cloneName);
+
+ /**
+ * Opens a stream on this resource
+ *
+ * @return the opened input stream
+ */
+ public InputStream openStream() throws IOException;
+}
diff --git a/src/java/org/apache/ivy/plugins/repository/ResourceDownloader.java b/src/java/org/apache/ivy/plugins/repository/ResourceDownloader.java
new file mode 100644
index 0000000..a2cb829
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/repository/ResourceDownloader.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.repository;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.ivy.core.module.descriptor.Artifact;
+
+/**
+ * A {@link ResourceDownloader} is able to download a Resource to a File.
+ * <p>
+ * Depending on the implementation, the downloader may also choose to download checksums
+ * automatically and check the consistency of the downloaded resource.
+ * </p>
+ * <p>
+ * The implementation is also responsible for using a .part file during download, to ensure the
+ * destination file will exist only if the download is completed successfully.
+ * </p>
+ */
+public interface ResourceDownloader {
+ public void download(Artifact artifact, Resource resource, File dest) throws IOException;
+}
diff --git a/src/java/org/apache/ivy/plugins/repository/ResourceHelper.java b/src/java/org/apache/ivy/plugins/repository/ResourceHelper.java
new file mode 100644
index 0000000..e0837cc
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/repository/ResourceHelper.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.repository;
+
+import java.io.File;
+import java.net.MalformedURLException;
+
+import org.apache.ivy.plugins.repository.file.FileResource;
+import org.apache.ivy.plugins.repository.url.URLResource;
+import org.apache.ivy.util.Message;
+
+public final class ResourceHelper {
+
+ private ResourceHelper() {
+
+ }
+
+ public static boolean equals(Resource res, File f) {
+ if (res == null && f == null) {
+ return true;
+ }
+ if (res == null || f == null) {
+ return false;
+ }
+ if (res instanceof FileResource) {
+ return new File(res.getName()).equals(f);
+ } else if (res instanceof URLResource) {
+ try {
+ return f.toURI().toURL().toExternalForm().equals(res.getName());
+ } catch (MalformedURLException e) {
+ return false;
+ }
+ }
+ return false;
+ }
+
+ public static long getLastModifiedOrDefault(Resource res) {
+ long last = res.getLastModified();
+ if (last > 0) {
+ return last;
+ } else {
+ Message.debug("impossible to get date for " + res + ": using 'now'");
+ return System.currentTimeMillis();
+ }
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/repository/TransferEvent.java b/src/java/org/apache/ivy/plugins/repository/TransferEvent.java
new file mode 100644
index 0000000..d7697e3
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/repository/TransferEvent.java
@@ -0,0 +1,357 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.repository;
+
+import java.io.File;
+
+import org.apache.ivy.core.event.IvyEvent;
+
+/**
+ * TransferEvent is used to notify TransferListeners about progress in transfer of resources form/to
+ * the respository This class is LARGELY inspired by org.apache.maven.wagon.events.TransferEvent
+ * released under the following copyright license:
+ *
+ * <pre>
+ *
+ * Copyright 2001-2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * </pre>
+ *
+ * Orginal class written by Michal Maczka.
+ */
+public class TransferEvent extends IvyEvent {
+ /**
+ * A transfer was attempted, but has not yet commenced.
+ */
+ public static final int TRANSFER_INITIATED = 0;
+
+ /**
+ * A transfer was started.
+ */
+ public static final int TRANSFER_STARTED = 1;
+
+ /**
+ * A transfer is completed.
+ */
+ public static final int TRANSFER_COMPLETED = 2;
+
+ /**
+ * A transfer is in progress.
+ */
+ public static final int TRANSFER_PROGRESS = 3;
+
+ /**
+ * An error occurred during transfer
+ */
+ public static final int TRANSFER_ERROR = 4;
+
+ /**
+ * Used to check event type validity: should always be 0 <= type <= LAST_EVENT_TYPE
+ */
+ private static final int LAST_EVENT_TYPE = TRANSFER_ERROR;
+
+ /**
+ * Indicates GET transfer (from the repository)
+ */
+ public static final int REQUEST_GET = 5;
+
+ /**
+ * Indicates PUT transfer (to the repository)
+ */
+ public static final int REQUEST_PUT = 6;
+
+ public static final String TRANSFER_INITIATED_NAME = "transfer-initiated";
+
+ public static final String TRANSFER_STARTED_NAME = "transfer-started";
+
+ public static final String TRANSFER_PROGRESS_NAME = "transfer-progress";
+
+ public static final String TRANSFER_COMPLETED_NAME = "transfer-completed";
+
+ public static final String TRANSFER_ERROR_NAME = "transfer-error";
+
+ private Resource resource;
+
+ private int eventType;
+
+ private int requestType;
+
+ private Exception exception;
+
+ private File localFile;
+
+ private Repository repository;
+
+ private long length;
+
+ private long totalLength;
+
+ private boolean isTotalLengthSet = false;
+
+ /**
+ * This attribute is used to store the time at which the event enters a type.
+ * <p>
+ * The array should better be seen as a Map from event type (int) to the time at which the event
+ * entered that type, 0 if it never entered this type.
+ * </p>
+ */
+ private long[] timeTracking = new long[LAST_EVENT_TYPE + 1];
+
+ public TransferEvent(final Repository repository, final Resource resource, final int eventType,
+ final int requestType) {
+ super(getName(eventType));
+
+ this.repository = repository;
+ setResource(resource);
+
+ setEventType(eventType);
+
+ setRequestType(requestType);
+ }
+
+ public TransferEvent(final Repository repository, final Resource resource,
+ final Exception exception, final int requestType) {
+ this(repository, resource, TRANSFER_ERROR, requestType);
+
+ this.exception = exception;
+ }
+
+ public TransferEvent(final Repository repository, final Resource resource, long length,
+ final int requestType) {
+ this(repository, resource, TRANSFER_PROGRESS, requestType);
+
+ this.length = length;
+ this.totalLength = length;
+ }
+
+ private static String getName(int eventType) {
+ switch (eventType) {
+ case TRANSFER_INITIATED:
+ return TRANSFER_INITIATED_NAME;
+ case TRANSFER_STARTED:
+ return TRANSFER_STARTED_NAME;
+ case TRANSFER_PROGRESS:
+ return TRANSFER_PROGRESS_NAME;
+ case TRANSFER_COMPLETED:
+ return TRANSFER_COMPLETED_NAME;
+ case TRANSFER_ERROR:
+ return TRANSFER_ERROR_NAME;
+ default:
+ return null;
+ }
+ }
+
+ /**
+ * @return Returns the resource.
+ */
+ public Resource getResource() {
+ return resource;
+ }
+
+ /**
+ * @return Returns the exception.
+ */
+ public Exception getException() {
+ return exception;
+ }
+
+ /**
+ * Returns the request type.
+ *
+ * @return Returns the request type. The Request type is one of
+ * <code>TransferEvent.REQUEST_GET<code> or <code>TransferEvent.REQUEST_PUT<code>
+ */
+ public int getRequestType() {
+ return requestType;
+ }
+
+ /**
+ * Sets the request type
+ *
+ * @param requestType
+ * The requestType to set. The Request type value should be either
+ * <code>TransferEvent.REQUEST_GET<code> or <code>TransferEvent.REQUEST_PUT<code>.
+ * @throws IllegalArgumentException
+ * when
+ */
+ protected void setRequestType(final int requestType) {
+ switch (requestType) {
+
+ case REQUEST_PUT:
+ break;
+ case REQUEST_GET:
+ break;
+
+ default:
+ throw new IllegalArgumentException("Illegal request type: " + requestType);
+ }
+
+ this.requestType = requestType;
+ addAttribute("request-type", requestType == REQUEST_GET ? "get" : "put");
+ }
+
+ /**
+ * @return Returns the eventType.
+ */
+ public int getEventType() {
+ return eventType;
+ }
+
+ /**
+ * @param eventType
+ * The eventType to set.
+ */
+ protected void setEventType(final int eventType) {
+ checkEventType(eventType);
+ if (this.eventType != eventType) {
+ this.eventType = eventType;
+ timeTracking[eventType] = System.currentTimeMillis();
+ if (eventType > TRANSFER_INITIATED) {
+ addAttribute("total-duration",
+ String.valueOf(getElapsedTime(TRANSFER_INITIATED, eventType)));
+ if (eventType > TRANSFER_STARTED) {
+ addAttribute("duration",
+ String.valueOf(getElapsedTime(TRANSFER_STARTED, eventType)));
+ }
+ }
+ }
+ }
+
+ /**
+ * @param resource
+ * The resource to set.
+ */
+ protected void setResource(final Resource resource) {
+ this.resource = resource;
+ addAttribute("resource", this.resource.getName());
+ }
+
+ /**
+ * @return Returns the local file.
+ */
+ public File getLocalFile() {
+ return localFile;
+ }
+
+ /**
+ * @param localFile
+ * The local file to set.
+ */
+ protected void setLocalFile(File localFile) {
+ this.localFile = localFile;
+ }
+
+ public long getLength() {
+ return length;
+ }
+
+ protected void setLength(long length) {
+ this.length = length;
+ }
+
+ public long getTotalLength() {
+ return totalLength;
+ }
+
+ protected void setTotalLength(long totalLength) {
+ this.totalLength = totalLength;
+ }
+
+ public void setException(Exception exception) {
+ this.exception = exception;
+ }
+
+ public boolean isTotalLengthSet() {
+ return isTotalLengthSet;
+ }
+
+ public void setTotalLengthSet(boolean isTotalLengthSet) {
+ this.isTotalLengthSet = isTotalLengthSet;
+ }
+
+ public Repository getRepository() {
+ return repository;
+ }
+
+ /**
+ * Returns the elapsed time (in ms) between when the event entered one type until it entered
+ * another event time.
+ * <p>
+ * This is especially useful to get the elapsed transfer time:
+ *
+ * <pre>
+ * getElapsedTime(TransferEvent.TRANSFER_STARTED, TransferEvent.TRANSFER_COMPLETED);
+ * </pre>
+ *
+ * </p>
+ * <p>
+ * Special cases:
+ * <ul>
+ * <li>returns -1 if the event never entered the fromEventType or the toEventType.</li>
+ * <li>returns 0 if the event entered toEventType before fromEventType</li>
+ * </ul>
+ * </p>
+ *
+ * @param fromEventType
+ * the event type constant from which time should be measured
+ * @param toEventType
+ * the event type constant to which time should be measured
+ * @return the elapsed time (in ms) between when the event entered fromEventType until it
+ * entered toEventType.
+ * @throws IllegalArgumentException
+ * if either type is not a known constant event type.
+ */
+ public long getElapsedTime(int fromEventType, int toEventType) {
+ checkEventType(fromEventType);
+ checkEventType(toEventType);
+ long start = timeTracking[fromEventType];
+ long end = timeTracking[toEventType];
+ if (start == 0 || end == 0) {
+ return -1;
+ } else if (end < start) {
+ return 0;
+ } else {
+ return end - start;
+ }
+ }
+
+ /**
+ * Checks the given event type is a valid event type, throws an {@link IllegalArgumentException}
+ * if it isn't
+ *
+ * @param eventType
+ * the event type to check
+ */
+ private void checkEventType(int eventType) {
+ if (eventType < 0 || eventType > LAST_EVENT_TYPE) {
+ throw new IllegalArgumentException("invalid event type " + eventType);
+ }
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/repository/TransferListener.java b/src/java/org/apache/ivy/plugins/repository/TransferListener.java
new file mode 100644
index 0000000..b3ccb9e
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/repository/TransferListener.java
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.repository;
+
+import java.util.EventListener;
+
+/**
+ * Listen to repository transfer
+ */
+public interface TransferListener extends EventListener {
+ void transferProgress(TransferEvent evt);
+}
diff --git a/src/java/org/apache/ivy/plugins/repository/file/FileRepository.java b/src/java/org/apache/ivy/plugins/repository/file/FileRepository.java
new file mode 100644
index 0000000..0ac4baf
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/repository/file/FileRepository.java
@@ -0,0 +1,150 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.repository.file;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.ivy.plugins.repository.AbstractRepository;
+import org.apache.ivy.plugins.repository.RepositoryCopyProgressListener;
+import org.apache.ivy.plugins.repository.Resource;
+import org.apache.ivy.plugins.repository.TransferEvent;
+import org.apache.ivy.util.Checks;
+import org.apache.ivy.util.FileUtil;
+
+public class FileRepository extends AbstractRepository {
+ private RepositoryCopyProgressListener progress = new RepositoryCopyProgressListener(this);
+
+ private File baseDir;
+
+ private boolean local = true;
+
+ public FileRepository() {
+ baseDir = null;
+ }
+
+ public FileRepository(File basedir) {
+ setBaseDir(basedir);
+ }
+
+ public Resource getResource(String source) throws IOException {
+ return new FileResource(this, getFile(source));
+ }
+
+ public void get(String source, File destination) throws IOException {
+ fireTransferInitiated(getResource(source), TransferEvent.REQUEST_GET);
+ copy(getFile(source), destination, true);
+ }
+
+ public void put(File source, String destination, boolean overwrite) throws IOException {
+ fireTransferInitiated(getResource(destination), TransferEvent.REQUEST_PUT);
+ copy(source, getFile(destination), overwrite);
+ }
+
+ public void move(File src, File dest) throws IOException {
+ if (!src.renameTo(dest)) {
+ throw new IOException("impossible to move '" + src + "' to '" + dest + "'");
+ }
+ }
+
+ public void delete(File f) throws IOException {
+ if (!FileUtil.forceDelete(f)) {
+ throw new IOException("impossible to delete '" + f + "'");
+ }
+ }
+
+ private void copy(File src, File destination, boolean overwrite) throws IOException {
+ try {
+ getProgressListener().setTotalLength(new Long(src.length()));
+ if (!FileUtil.copy(src, destination, getProgressListener(), overwrite)) {
+ if (!overwrite && destination.exists()) {
+ throw new IOException("file copy not done from " + src + " to " + destination
+ + ": destination already exists and overwrite is false");
+ } else {
+ throw new IOException("file copy not done from " + src + " to " + destination);
+ }
+ }
+ } catch (IOException ex) {
+ fireTransferError(ex);
+ throw ex;
+ } catch (RuntimeException ex) {
+ fireTransferError(ex);
+ throw ex;
+ } finally {
+ getProgressListener().setTotalLength(null);
+ }
+ }
+
+ protected RepositoryCopyProgressListener getProgressListener() {
+ return progress;
+ }
+
+ public List list(String parent) throws IOException {
+ File dir = getFile(parent);
+ if (dir.exists() && dir.isDirectory()) {
+ String[] names = dir.list();
+ if (names != null) {
+ List ret = new ArrayList(names.length);
+ for (int i = 0; i < names.length; i++) {
+ ret.add(parent + getFileSeparator() + names[i]);
+ }
+ return ret;
+ }
+ }
+ return null;
+ }
+
+ File getFile(String source) {
+ if (baseDir != null) {
+ return FileUtil.resolveFile(baseDir, source);
+ } else {
+ return Checks.checkAbsolute(source, "source");
+ }
+ }
+
+ public boolean isLocal() {
+ return local;
+ }
+
+ public void setLocal(boolean local) {
+ this.local = local;
+ }
+
+ public File getBaseDir() {
+ return baseDir;
+ }
+
+ public final void setBaseDir(File baseDir) {
+ Checks.checkAbsolute(baseDir, "basedir");
+ this.baseDir = baseDir;
+ }
+
+ public String standardize(String source) {
+ if (baseDir != null) {
+ return FileUtil.resolveFile(baseDir, source).getPath();
+ } else {
+ return FileUtil.normalize(source).getPath();
+ }
+ }
+
+ public String getFileSeparator() {
+ return File.separator;
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/repository/file/FileResource.java b/src/java/org/apache/ivy/plugins/repository/file/FileResource.java
new file mode 100644
index 0000000..322299d
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/repository/file/FileResource.java
@@ -0,0 +1,76 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.repository.file;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.ivy.plugins.repository.Resource;
+
+public class FileResource implements Resource {
+ private File file;
+
+ private FileRepository repository;
+
+ public FileResource(FileRepository repository, File f) {
+ this.repository = repository;
+ this.file = f;
+ }
+
+ public String getName() {
+ return file.getPath();
+ }
+
+ public Resource clone(String cloneName) {
+ return new FileResource(repository, repository.getFile(cloneName));
+ }
+
+ public long getLastModified() {
+ return file.lastModified();
+ }
+
+ public long getContentLength() {
+ return file.length();
+ }
+
+ public boolean exists() {
+ return file.exists();
+ }
+
+ public String toString() {
+ return getName();
+ }
+
+ public File getFile() {
+ return file;
+ }
+
+ public FileRepository getRepository() {
+ return repository;
+ }
+
+ public boolean isLocal() {
+ return repository.isLocal();
+ }
+
+ public InputStream openStream() throws IOException {
+ return new FileInputStream(file);
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/repository/jar/JarRepository.java b/src/java/org/apache/ivy/plugins/repository/jar/JarRepository.java
new file mode 100644
index 0000000..7a1cf28
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/repository/jar/JarRepository.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.repository.jar;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.jar.JarFile;
+import java.util.zip.ZipEntry;
+
+import org.apache.ivy.plugins.repository.AbstractRepository;
+import org.apache.ivy.plugins.repository.RepositoryCopyProgressListener;
+import org.apache.ivy.plugins.repository.Resource;
+import org.apache.ivy.plugins.repository.TransferEvent;
+import org.apache.ivy.util.FileUtil;
+
+public class JarRepository extends AbstractRepository {
+
+ private RepositoryCopyProgressListener progress = new RepositoryCopyProgressListener(this);
+
+ private JarFile jarFile;
+
+ public void setJarFile(JarFile jarFile) {
+ this.jarFile = jarFile;
+ }
+
+ public Resource getResource(String source) throws IOException {
+ return new JarResource(jarFile, source);
+ }
+
+ protected RepositoryCopyProgressListener getProgressListener() {
+ return progress;
+ }
+
+ public void get(String source, File destination) throws IOException {
+ fireTransferInitiated(getResource(source), TransferEvent.REQUEST_GET);
+ try {
+ ZipEntry entry = jarFile.getEntry(source);
+ if (entry == null) {
+ throw new FileNotFoundException();
+ }
+ getProgressListener().setTotalLength(new Long(entry.getSize()));
+ FileUtil.copy(jarFile.getInputStream(entry), destination, getProgressListener());
+ } catch (IOException ex) {
+ fireTransferError(ex);
+ throw ex;
+ } catch (RuntimeException ex) {
+ fireTransferError(ex);
+ throw ex;
+ } finally {
+ getProgressListener().setTotalLength(null);
+ }
+ }
+
+ public List/* <String> */list(String parent) throws IOException {
+ ZipEntry parentEntry = jarFile.getEntry(parent);
+ if (parentEntry == null || !parentEntry.isDirectory()) {
+ return null;
+ }
+ List/* <String> */children = new ArrayList();
+ Enumeration entries = jarFile.entries();
+ while (entries.hasMoreElements()) {
+ ZipEntry entry = (ZipEntry) entries.nextElement();
+ if (entry.getName().startsWith(parent) && entry.getName().equals(parentEntry.getName())) {
+ children.add(entry.getName());
+ }
+ }
+ return children;
+ }
+
+}
diff --git a/src/java/org/apache/ivy/plugins/repository/jar/JarResource.java b/src/java/org/apache/ivy/plugins/repository/jar/JarResource.java
new file mode 100644
index 0000000..3324806
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/repository/jar/JarResource.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.repository.jar;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.jar.JarFile;
+import java.util.zip.ZipEntry;
+
+import org.apache.ivy.plugins.repository.Resource;
+
+public class JarResource implements Resource {
+
+ private final JarFile jarFile;
+
+ private final String path;
+
+ private ZipEntry entry;
+
+ public JarResource(JarFile jarFile, String path) {
+ this.jarFile = jarFile;
+ this.path = path;
+ entry = jarFile.getEntry(path);
+ }
+
+ public String getName() {
+ return path;
+ }
+
+ public long getLastModified() {
+ return entry.getTime();
+ }
+
+ public long getContentLength() {
+ return entry.getSize();
+ }
+
+ public boolean exists() {
+ return entry != null;
+ }
+
+ public boolean isLocal() {
+ // not local as it is not a directly accessible file
+ return false;
+ }
+
+ public Resource clone(String cloneName) {
+ return new JarResource(jarFile, cloneName);
+ }
+
+ public InputStream openStream() throws IOException {
+ return jarFile.getInputStream(entry);
+ }
+
+ public String toString() {
+ return jarFile.getName() + "!" + getName();
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/repository/sftp/SFTPRepository.java b/src/java/org/apache/ivy/plugins/repository/sftp/SFTPRepository.java
new file mode 100644
index 0000000..1f5f93a
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/repository/sftp/SFTPRepository.java
@@ -0,0 +1,289 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.repository.sftp;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.ivy.plugins.repository.BasicResource;
+import org.apache.ivy.plugins.repository.Resource;
+import org.apache.ivy.plugins.repository.TransferEvent;
+import org.apache.ivy.plugins.repository.ssh.AbstractSshBasedRepository;
+import org.apache.ivy.plugins.repository.ssh.SshCache;
+import org.apache.ivy.util.Message;
+
+import com.jcraft.jsch.ChannelSftp;
+import com.jcraft.jsch.ChannelSftp.LsEntry;
+import com.jcraft.jsch.JSchException;
+import com.jcraft.jsch.Session;
+import com.jcraft.jsch.SftpATTRS;
+import com.jcraft.jsch.SftpException;
+import com.jcraft.jsch.SftpProgressMonitor;
+
+/**
+ * SFTP Repository, allow to use a repository accessed by sftp protocol. It supports all operations:
+ * get, put and list. It relies on jsch for sftp handling, and thus is compatible with sftp version
+ * 0, 1, 2 and 3
+ */
+public class SFTPRepository extends AbstractSshBasedRepository {
+ // this must be a long to ensure the multiplication done below uses longs
+ // instead of ints which are not big enough to hold the result
+ private static final long MILLIS_PER_SECOND = 1000;
+
+ private final class MyProgressMonitor implements SftpProgressMonitor {
+ private long totalLength;
+
+ public void init(int op, String src, String dest, long max) {
+ totalLength = max;
+ fireTransferStarted(max);
+ }
+
+ public void end() {
+ fireTransferCompleted(totalLength);
+ }
+
+ public boolean count(long count) {
+ fireTransferProgress(count);
+ return true;
+ }
+ }
+
+ public SFTPRepository() {
+ }
+
+ public Resource getResource(String source) {
+ return new SFTPResource(this, source);
+ }
+
+ /**
+ * This method is similar to getResource, except that the returned resource is fully initialized
+ * (resolved in the sftp repository), and that the given string is a full remote path
+ *
+ * @param path
+ * the full remote path in the repository of the resource
+ * @return a fully initialized resource, able to answer to all its methods without needing any
+ * further connection
+ */
+ public Resource resolveResource(String path) {
+ try {
+ ChannelSftp c = getSftpChannel(path);
+
+ Collection r = c.ls(getPath(path));
+
+ if (r != null) {
+ for (Iterator iter = r.iterator(); iter.hasNext();) {
+ Object obj = iter.next();
+ if (obj instanceof LsEntry) {
+ LsEntry entry = (LsEntry) obj;
+ SftpATTRS attrs = entry.getAttrs();
+ return new BasicResource(path, true, attrs.getSize(), attrs.getMTime()
+ * MILLIS_PER_SECOND, false);
+ }
+ }
+ }
+ } catch (Exception e) {
+ Message.debug("Error while resolving resource " + path, e);
+ // silent fail, return unexisting resource
+ }
+
+ return new BasicResource(path, false, 0, 0, false);
+ }
+
+ public InputStream openStream(SFTPResource resource) throws IOException {
+ ChannelSftp c = getSftpChannel(resource.getName());
+ try {
+ String path = getPath(resource.getName());
+ return c.get(path);
+ } catch (SftpException e) {
+ IOException ex = new IOException("impossible to open stream for " + resource + " on "
+ + getHost() + (e.getMessage() != null ? ": " + e.getMessage() : ""));
+ ex.initCause(e);
+ throw ex;
+ } catch (URISyntaxException e) {
+ IOException ex = new IOException("impossible to open stream for " + resource + " on "
+ + getHost() + (e.getMessage() != null ? ": " + e.getMessage() : ""));
+ ex.initCause(e);
+ throw ex;
+ }
+ }
+
+ public void get(String source, File destination) throws IOException {
+ fireTransferInitiated(getResource(source), TransferEvent.REQUEST_GET);
+ ChannelSftp c = getSftpChannel(source);
+ try {
+ String path = getPath(source);
+ c.get(path, destination.getAbsolutePath(), new MyProgressMonitor());
+ } catch (SftpException e) {
+ IOException ex = new IOException("impossible to get " + source + " on " + getHost()
+ + (e.getMessage() != null ? ": " + e.getMessage() : ""));
+ ex.initCause(e);
+ throw ex;
+ } catch (URISyntaxException e) {
+ IOException ex = new IOException("impossible to get " + source + " on " + getHost()
+ + (e.getMessage() != null ? ": " + e.getMessage() : ""));
+ ex.initCause(e);
+ throw ex;
+ }
+ }
+
+ public void put(File source, String destination, boolean overwrite) throws IOException {
+ fireTransferInitiated(getResource(destination), TransferEvent.REQUEST_PUT);
+ ChannelSftp c = getSftpChannel(destination);
+ try {
+ String path = getPath(destination);
+ if (!overwrite && checkExistence(path, c)) {
+ throw new IOException("destination file exists and overwrite == false");
+ }
+ if (path.indexOf('/') != -1) {
+ mkdirs(path.substring(0, path.lastIndexOf('/')), c);
+ }
+ c.put(source.getAbsolutePath(), path, new MyProgressMonitor());
+ } catch (SftpException e) {
+ IOException ex = new IOException(e.getMessage());
+ ex.initCause(e);
+ throw ex;
+ } catch (URISyntaxException e) {
+ IOException ex = new IOException(e.getMessage());
+ ex.initCause(e);
+ throw ex;
+ }
+ }
+
+ private void mkdirs(String directory, ChannelSftp c) throws IOException, SftpException {
+ try {
+ SftpATTRS att = c.stat(directory);
+ if (att != null) {
+ if (att.isDir()) {
+ return;
+ }
+ }
+ } catch (SftpException ex) {
+ if (directory.indexOf('/') != -1) {
+ mkdirs(directory.substring(0, directory.lastIndexOf('/')), c);
+ }
+ c.mkdir(directory);
+ }
+ }
+
+ private String getPath(String sftpURI) throws URISyntaxException {
+ String result = null;
+ URI uri = new URI(sftpURI);
+ result = uri.getPath();
+
+ if (result == null) {
+ throw new URISyntaxException(sftpURI, "Missing path in URI.");
+ }
+
+ return result;
+ }
+
+ public List list(String parent) throws IOException {
+ try {
+ ChannelSftp c = getSftpChannel(parent);
+ String path = getPath(parent);
+ Collection r = c.ls(path);
+ if (r != null) {
+ if (!path.endsWith("/")) {
+ path = parent + "/";
+ }
+ List result = new ArrayList();
+ for (Iterator iter = r.iterator(); iter.hasNext();) {
+ Object obj = iter.next();
+ if (obj instanceof LsEntry) {
+ LsEntry entry = (LsEntry) obj;
+ if (".".equals(entry.getFilename()) || "..".equals(entry.getFilename())) {
+ continue;
+ }
+ result.add(path + entry.getFilename());
+ }
+ }
+ return result;
+ }
+ } catch (SftpException e) {
+ IOException ex = new IOException("Failed to return a listing for '" + parent + "'");
+ ex.initCause(e);
+ throw ex;
+ } catch (URISyntaxException usex) {
+ IOException ex = new IOException("Failed to return a listing for '" + parent + "'");
+ ex.initCause(usex);
+ throw ex;
+ }
+ return null;
+ }
+
+ /**
+ * Checks the existence for a remote file
+ *
+ * @param file
+ * to check
+ * @param channel
+ * to use
+ * @returns true if file exists, false otherwise
+ * @throws IOException
+ * @throws SftpException
+ */
+ private boolean checkExistence(String file, ChannelSftp channel) throws IOException,
+ SftpException {
+ try {
+ return channel.stat(file) != null;
+ } catch (SftpException ex) {
+ return false;
+ }
+ }
+
+ /**
+ * Establish the connection to the server if not yet connected, and listen to ivy events for
+ * closing connection when resolve is finished. Not meant to be used in multi threaded
+ * environment.
+ *
+ * @return the ChannelSftp with which a connection is established
+ * @throws IOException
+ * if any connection problem occurs
+ */
+ private ChannelSftp getSftpChannel(String pathOrUri) throws IOException {
+ Session session = getSession(pathOrUri);
+ String host = session.getHost();
+ ChannelSftp channel = SshCache.getInstance().getChannelSftp(session);
+ if (channel == null) {
+ try {
+ channel = (ChannelSftp) session.openChannel("sftp");
+ channel.connect();
+ Message.verbose(":: SFTP :: connected to " + host + "!");
+ SshCache.getInstance().attachChannelSftp(session, channel);
+ } catch (JSchException e) {
+ IOException ex = new IOException(e.getMessage());
+ ex.initCause(e);
+ throw ex;
+ }
+ }
+ return channel;
+ }
+
+ protected String getRepositoryScheme() {
+ // use the Resolver type name here?
+ // would be nice if it would be static, so we could use SFTPResolver.getTypeName()
+ return "sftp";
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/repository/sftp/SFTPResource.java b/src/java/org/apache/ivy/plugins/repository/sftp/SFTPResource.java
new file mode 100644
index 0000000..db9447c
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/repository/sftp/SFTPResource.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.repository.sftp;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.ivy.plugins.repository.Resource;
+
+public class SFTPResource implements Resource {
+ private SFTPRepository repository;
+
+ private String path;
+
+ private transient boolean init = false;
+
+ private transient boolean exists;
+
+ private transient long lastModified;
+
+ private transient long contentLength;
+
+ public SFTPResource(SFTPRepository repository, String path) {
+ this.repository = repository;
+ this.path = path;
+ }
+
+ public String getName() {
+ return path;
+ }
+
+ public Resource clone(String cloneName) {
+ return new SFTPResource(repository, cloneName);
+ }
+
+ public long getLastModified() {
+ init();
+ return lastModified;
+ }
+
+ public long getContentLength() {
+ init();
+ return contentLength;
+ }
+
+ public boolean exists() {
+ init();
+ return exists;
+ }
+
+ private void init() {
+ if (!init) {
+ Resource r = repository.resolveResource(path);
+ contentLength = r.getContentLength();
+ lastModified = r.getLastModified();
+ exists = r.exists();
+ init = true;
+ }
+ }
+
+ public String toString() {
+ return getName();
+ }
+
+ public boolean isLocal() {
+ return false;
+ }
+
+ public InputStream openStream() throws IOException {
+ return repository.openStream(this);
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/repository/ssh/AbstractSshBasedRepository.java b/src/java/org/apache/ivy/plugins/repository/ssh/AbstractSshBasedRepository.java
new file mode 100644
index 0000000..94b41df
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/repository/ssh/AbstractSshBasedRepository.java
@@ -0,0 +1,320 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.repository.ssh;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.HashMap;
+import java.util.Locale;
+
+import org.apache.ivy.plugins.repository.AbstractRepository;
+import org.apache.ivy.util.Credentials;
+import org.apache.ivy.util.CredentialsUtil;
+import org.apache.ivy.util.Message;
+
+import com.jcraft.jsch.Session;
+
+public abstract class AbstractSshBasedRepository extends AbstractRepository {
+
+ private File keyFile = null;
+
+ private File passFile = null;
+
+ private String userPassword = null;
+
+ private String keyFilePassword = null;
+
+ private String user = null;
+
+ private String host = null;
+
+ private int port = -1;
+
+ private boolean allowedAgentUse = false;
+
+ public AbstractSshBasedRepository() {
+ super();
+ }
+
+ /**
+ * hashmap of user/hosts with credentials. key is hostname, value is Credentials
+ **/
+ private static HashMap credentialsCache = new HashMap();
+
+ private static final int MAX_CREDENTILAS_CACHE_SIZE = 100;
+
+ /**
+ * get a new session using the default attributes if the given String is a full uri, use the
+ * data from the uri instead
+ *
+ * @param pathOrUri
+ * might be just a path or a full ssh or sftp uri
+ * @return matching Session
+ */
+ protected Session getSession(String pathOrUri) throws IOException {
+ URI uri = parseURI(pathOrUri);
+ String host = getHost();
+ int port = getPort();
+ String user = getUser();
+ String userPassword = getUserPassword();
+ if (uri != null && uri.getScheme() != null) {
+ if (uri.getHost() != null) {
+ host = uri.getHost();
+ }
+ if (uri.getPort() != -1) {
+ port = uri.getPort();
+ }
+ if (uri.getUserInfo() != null) {
+ String userInfo = uri.getUserInfo();
+ if (userInfo.indexOf(":") == -1) {
+ user = userInfo;
+ } else {
+ user = userInfo.substring(0, userInfo.indexOf(":"));
+ userPassword = userInfo.substring(userInfo.indexOf(":") + 1);
+ }
+ }
+ }
+ if (host == null) {
+ throw new IllegalArgumentException(
+ "missing host information. host should be provided either "
+ + "directly on the repository or in the connection URI");
+ }
+ if (user == null) {
+ Credentials c = requestCredentials(host);
+ if (c != null) {
+ user = c.getUserName();
+ userPassword = c.getPasswd();
+ } else {
+ Message.error("username is not set");
+ }
+ }
+ return SshCache.getInstance().getSession(host, port, user, userPassword, getKeyFile(),
+ getKeyFilePassword(), getPassFile(), isAllowedAgentUse());
+ }
+
+ /**
+ * Just check the uri for sanity
+ *
+ * @param source
+ * String of the uri
+ * @return URI object of the String or null
+ */
+ private URI parseURI(String source) {
+ try {
+ URI uri = new URI(source);
+ if (uri.getScheme() != null
+ && !uri.getScheme().toLowerCase(Locale.US)
+ .equals(getRepositoryScheme().toLowerCase(Locale.US))) {
+ throw new URISyntaxException(source, "Wrong scheme in URI. Expected "
+ + getRepositoryScheme() + " as scheme!");
+ }
+ if (uri.getHost() == null && getHost() == null) {
+ throw new URISyntaxException(source, "Missing host in URI or in resolver");
+ }
+ if (uri.getPath() == null) {
+ throw new URISyntaxException(source, "Missing path in URI");
+ }
+ // if (uri.getUserInfo() == null && getUser() == null) {
+ // throw new URISyntaxException(source, "Missing username in URI or in resolver");
+ // }
+ return uri;
+ } catch (URISyntaxException e) {
+ Message.error(e.getMessage());
+ Message.error("The uri '" + source + "' is in the wrong format.");
+ Message.error("Please use " + getRepositoryScheme()
+ + "://user:pass at hostname/path/to/repository");
+ return null;
+ }
+ }
+
+ /**
+ * Called, when user was not found in URL. Maintain static hashe of credentials and retrieve or
+ * ask credentials for host.
+ *
+ * @param host
+ * host for which we want to get credentials.
+ * @return credentials for given host
+ **/
+ private Credentials requestCredentials(String host) {
+ Object o = credentialsCache.get(host);
+ if (o == null) {
+ Credentials c = CredentialsUtil.promptCredentials(new Credentials(null, host, user,
+ userPassword), getPassFile());
+ if (c != null) {
+ if (credentialsCache.size() > MAX_CREDENTILAS_CACHE_SIZE) {
+ credentialsCache.clear();
+ }
+ credentialsCache.put(host, c);
+ }
+ return c;
+ } else {
+ return (Credentials) o;
+ }
+ }
+
+ /**
+ * closes the session and remove it from the cache (eg. on case of errors)
+ *
+ * @param session
+ * key for the cache
+ * @param pathOrUri
+ * to release
+ */
+ protected void releaseSession(Session session, String pathOrUri) {
+ session.disconnect();
+ SshCache.getInstance().clearSession(session);
+ }
+
+ /**
+ * set the default user to use for the connection if no user is given or a PEM file is used
+ *
+ * @param user
+ * to use
+ */
+ public void setUser(String user) {
+ this.user = user;
+ }
+
+ /**
+ * @return the user to use for the connection if no user is given or a PEM file is used
+ */
+ public String getUser() {
+ return user;
+ }
+
+ /**
+ * Sets the full file path to use for accessing a PEM key file
+ *
+ * @param filePath
+ * fully qualified name
+ */
+ public void setKeyFile(File filePath) {
+ this.keyFile = filePath;
+ if (!keyFile.exists()) {
+ Message.warn("Pemfile " + keyFile.getAbsolutePath() + " doesn't exist.");
+ keyFile = null;
+ } else if (!keyFile.canRead()) {
+ Message.warn("Pemfile " + keyFile.getAbsolutePath() + " not readable.");
+ keyFile = null;
+ } else {
+ Message.debug("Using " + keyFile.getAbsolutePath() + " as keyfile.");
+ }
+ }
+
+ /**
+ * @return the keyFile
+ */
+ public File getKeyFile() {
+ return keyFile;
+ }
+
+ /**
+ * @param password
+ * password to use for user/password authentication
+ */
+ public void setUserPassword(String password) {
+ this.userPassword = password;
+ }
+
+ /**
+ * @return the keyFile password for public key based authentication
+ */
+ public String getKeyFilePassword() {
+ return keyFilePassword;
+ }
+
+ /**
+ * @param keyFilePassword
+ * sets password for public key based authentication
+ */
+ public void setKeyFilePassword(String keyFilePassword) {
+ this.keyFilePassword = keyFilePassword;
+ }
+
+ /**
+ * @return the user password
+ */
+ public String getUserPassword() {
+ return userPassword;
+ }
+
+ /**
+ * @return the host
+ */
+ public String getHost() {
+ return host;
+ }
+
+ /**
+ * @param host
+ * the host to set
+ */
+ public void setHost(String host) {
+ this.host = host;
+ }
+
+ /**
+ * @return the port
+ */
+ public int getPort() {
+ return port;
+ }
+
+ /**
+ * @param port
+ * the port to set
+ */
+ public void setPort(int port) {
+ this.port = port;
+ }
+
+ /**
+ * @param passFile
+ * the passfile to set
+ */
+ public void setPassFile(File passFile) {
+ this.passFile = passFile;
+ }
+
+ /**
+ * @return the passFile
+ */
+ public File getPassFile() {
+ return passFile;
+ }
+
+ /**
+ * @return allowedAgentUse Whether use of a local SSH agent for authentication is allowed
+ */
+ public boolean isAllowedAgentUse() {
+ return allowedAgentUse;
+ }
+
+ /**
+ * @param allowedAgentUse
+ * Whether use of a local SSH agent for authentication is allowed
+ */
+ public void setAllowedAgentUse(boolean allowedAgentUse) {
+ this.allowedAgentUse = allowedAgentUse;
+ }
+
+ protected abstract String getRepositoryScheme();
+
+}
diff --git a/src/java/org/apache/ivy/plugins/repository/ssh/RemoteScpException.java b/src/java/org/apache/ivy/plugins/repository/ssh/RemoteScpException.java
new file mode 100644
index 0000000..bf09516
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/repository/ssh/RemoteScpException.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.repository.ssh;
+
+/**
+ * This exception will be used for Remote SCP Exceptions (failures on the target system, no
+ * connetion probs)
+ */
+public class RemoteScpException extends Exception {
+
+ private static final long serialVersionUID = 3107198655563736600L;
+
+ public RemoteScpException() {
+ }
+
+ /**
+ * @param message
+ */
+ public RemoteScpException(String message) {
+ super(message);
+ }
+
+ /**
+ * @param cause
+ */
+ public RemoteScpException(Throwable cause) {
+ super(cause);
+ }
+
+ /**
+ * @param message
+ * @param cause
+ */
+ public RemoteScpException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+}
diff --git a/src/java/org/apache/ivy/plugins/repository/ssh/Scp.java b/src/java/org/apache/ivy/plugins/repository/ssh/Scp.java
new file mode 100644
index 0000000..ab53876
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/repository/ssh/Scp.java
@@ -0,0 +1,611 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.repository.ssh;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import com.jcraft.jsch.Channel;
+import com.jcraft.jsch.ChannelExec;
+import com.jcraft.jsch.JSchException;
+import com.jcraft.jsch.Session;
+
+/**
+ * This class is using the scp client to transfer data and information for the repository.
+ * <p>
+ * It is based on the SCPClient from the ganymed ssh library from Christian Plattner, released under
+ * a BSD style license.
+ * <p>
+ * To minimize the dependency to the ssh library and because we needed some additional
+ * functionality, we decided to copy'n'paste the single class rather than to inherit or delegate it
+ * somehow.
+ * <p>
+ * Nevertheless credit should go to the original author.
+ */
+public class Scp {
+ private static final int MODE_LENGTH = 4;
+
+ private static final int SEND_FILE_BUFFER_LENGTH = 40000;
+
+ private static final int SEND_BYTES_BUFFER_LENGTH = 512;
+
+ private static final int MIN_TLINE_LENGTH = 8;
+
+ private static final int CLINE_SPACE_INDEX2 = 5;
+
+ private static final int CLINE_SPACE_INDEX1 = 4;
+
+ private static final int MIN_C_LINE_LENGTH = 8;
+
+ private static final int DEFAULT_LINE_BUFFER_LENGTH = 30;
+
+ private static final int BUFFER_SIZE = 64 * 1024;
+
+ /*
+ * Maximum length authorized for scp lines. This is a random limit - if your path names are
+ * longer, then adjust it.
+ */
+ private static final int MAX_SCP_LINE_LENGTH = 8192;
+
+ private Session session;
+
+ public class FileInfo {
+ private String filename;
+
+ private long length;
+
+ private long lastModified;
+
+ /**
+ * @param filename
+ * The filename to set.
+ */
+ public void setFilename(String filename) {
+ this.filename = filename;
+ }
+
+ /**
+ * @return Returns the filename.
+ */
+ public String getFilename() {
+ return filename;
+ }
+
+ /**
+ * @param length
+ * The length to set.
+ */
+ public void setLength(long length) {
+ this.length = length;
+ }
+
+ /**
+ * @return Returns the length.
+ */
+ public long getLength() {
+ return length;
+ }
+
+ /**
+ * @param lastModified
+ * The lastModified to set.
+ */
+ public void setLastModified(long lastModified) {
+ this.lastModified = lastModified;
+ }
+
+ /**
+ * @return Returns the lastModified.
+ */
+ public long getLastModified() {
+ return lastModified;
+ }
+ }
+
+ public Scp(Session session) {
+ if (session == null) {
+ throw new IllegalArgumentException("Cannot accept null argument!");
+ }
+ this.session = session;
+ }
+
+ private void readResponse(InputStream is) throws IOException, RemoteScpException {
+ int c = is.read();
+
+ if (c == 0) {
+ return;
+ }
+
+ if (c == -1) {
+ throw new RemoteScpException("Remote scp terminated unexpectedly.");
+ }
+
+ if ((c != 1) && (c != 2)) {
+ throw new RemoteScpException("Remote scp sent illegal error code.");
+ }
+
+ if (c == 2) {
+ throw new RemoteScpException("Remote scp terminated with error.");
+ }
+
+ String err = receiveLine(is);
+ throw new RemoteScpException("Remote scp terminated with error (" + err + ").");
+ }
+
+ private String receiveLine(InputStream is) throws IOException, RemoteScpException {
+ StringBuffer sb = new StringBuffer(DEFAULT_LINE_BUFFER_LENGTH);
+
+ while (true) {
+
+ if (sb.length() > MAX_SCP_LINE_LENGTH) {
+ throw new RemoteScpException("Remote scp sent a too long line");
+ }
+
+ int c = is.read();
+
+ if (c < 0) {
+ throw new RemoteScpException("Remote scp terminated unexpectedly.");
+ }
+
+ if (c == '\n') {
+ break;
+ }
+
+ sb.append((char) c);
+
+ }
+ return sb.toString();
+ }
+
+ private void parseCLine(String line, FileInfo fileInfo) throws RemoteScpException {
+ /* Minimum line: "xxxx y z" ---> 8 chars */
+
+ long len;
+
+ if (line.length() < MIN_C_LINE_LENGTH) {
+ throw new RemoteScpException(
+ "Malformed C line sent by remote SCP binary, line too short.");
+ }
+
+ if ((line.charAt(CLINE_SPACE_INDEX1) != ' ') || (line.charAt(CLINE_SPACE_INDEX2) == ' ')) {
+ throw new RemoteScpException("Malformed C line sent by remote SCP binary.");
+ }
+
+ int lengthNameSep = line.indexOf(' ', CLINE_SPACE_INDEX2);
+
+ if (lengthNameSep == -1) {
+ throw new RemoteScpException("Malformed C line sent by remote SCP binary.");
+ }
+
+ String lengthSubstring = line.substring(CLINE_SPACE_INDEX2, lengthNameSep);
+ String nameSubstring = line.substring(lengthNameSep + 1);
+
+ if ((lengthSubstring.length() <= 0) || (nameSubstring.length() <= 0)) {
+ throw new RemoteScpException("Malformed C line sent by remote SCP binary.");
+ }
+
+ if ((CLINE_SPACE_INDEX2 + 1 + lengthSubstring.length() + nameSubstring.length()) != line
+ .length()) {
+ throw new RemoteScpException("Malformed C line sent by remote SCP binary.");
+ }
+
+ try {
+ len = Long.parseLong(lengthSubstring);
+ } catch (NumberFormatException e) {
+ throw new RemoteScpException(
+ "Malformed C line sent by remote SCP binary, cannot parse file length.");
+ }
+
+ if (len < 0) {
+ throw new RemoteScpException(
+ "Malformed C line sent by remote SCP binary, illegal file length.");
+ }
+
+ fileInfo.setLength(len);
+ fileInfo.setFilename(nameSubstring);
+ }
+
+ private void parseTLine(String line, FileInfo fileInfo) throws RemoteScpException {
+ /* Minimum line: "0 0 0 0" ---> 8 chars */
+
+ long modtime;
+ long firstMsec;
+ long atime;
+ long secondMsec;
+
+ if (line.length() < MIN_TLINE_LENGTH) {
+ throw new RemoteScpException(
+ "Malformed T line sent by remote SCP binary, line too short.");
+ }
+
+ int firstMsecBegin = line.indexOf(" ") + 1;
+ if (firstMsecBegin == 0 || firstMsecBegin >= line.length()) {
+ throw new RemoteScpException(
+ "Malformed T line sent by remote SCP binary, line not enough data.");
+ }
+
+ int atimeBegin = line.indexOf(" ", firstMsecBegin + 1) + 1;
+ if (atimeBegin == 0 || atimeBegin >= line.length()) {
+ throw new RemoteScpException(
+ "Malformed T line sent by remote SCP binary, line not enough data.");
+ }
+
+ int secondMsecBegin = line.indexOf(" ", atimeBegin + 1) + 1;
+ if (secondMsecBegin == 0 || secondMsecBegin >= line.length()) {
+ throw new RemoteScpException(
+ "Malformed T line sent by remote SCP binary, line not enough data.");
+ }
+
+ try {
+ modtime = Long.parseLong(line.substring(0, firstMsecBegin - 1));
+ firstMsec = Long.parseLong(line.substring(firstMsecBegin, atimeBegin - 1));
+ atime = Long.parseLong(line.substring(atimeBegin, secondMsecBegin - 1));
+ secondMsec = Long.parseLong(line.substring(secondMsecBegin));
+ } catch (NumberFormatException e) {
+ throw new RemoteScpException(
+ "Malformed C line sent by remote SCP binary, cannot parse file length.");
+ }
+
+ if (modtime < 0 || firstMsec < 0 || atime < 0 || secondMsec < 0) {
+ throw new RemoteScpException(
+ "Malformed C line sent by remote SCP binary, illegal file length.");
+ }
+
+ fileInfo.setLastModified(modtime);
+ }
+
+ private void sendFile(Channel channel, String localFile, String remoteName, String mode)
+ throws IOException, RemoteScpException {
+ byte[] buffer = new byte[BUFFER_SIZE];
+
+ OutputStream os = new BufferedOutputStream(channel.getOutputStream(),
+ SEND_FILE_BUFFER_LENGTH);
+ InputStream is = new BufferedInputStream(channel.getInputStream(), SEND_BYTES_BUFFER_LENGTH);
+
+ try {
+ if (channel.isConnected()) {
+ channel.start();
+ } else {
+ channel.connect();
+ }
+ } catch (JSchException e1) {
+ throw (IOException) new IOException("Channel connection problems").initCause(e1);
+ }
+
+ readResponse(is);
+
+ File f = new File(localFile);
+ long remain = f.length();
+
+ String cMode = mode;
+ if (cMode == null) {
+ cMode = "0600";
+ }
+ String cline = "C" + cMode + " " + remain + " " + remoteName + "\n";
+
+ os.write(cline.getBytes());
+ os.flush();
+
+ readResponse(is);
+
+ FileInputStream fis = null;
+
+ try {
+ fis = new FileInputStream(f);
+
+ while (remain > 0) {
+ int trans;
+ if (remain > buffer.length) {
+ trans = buffer.length;
+ } else {
+ trans = (int) remain;
+ }
+ if (fis.read(buffer, 0, trans) != trans) {
+ throw new IOException("Cannot read enough from local file " + localFile);
+ }
+
+ os.write(buffer, 0, trans);
+
+ remain -= trans;
+ }
+
+ fis.close();
+ } catch (IOException e) {
+ if (fis != null) {
+ fis.close();
+ }
+ throw (e);
+ }
+
+ os.write(0);
+ os.flush();
+
+ readResponse(is);
+
+ os.write("E\n".getBytes());
+ os.flush();
+ }
+
+ /**
+ * Receive a file via scp and store it in a stream
+ *
+ * @param channel
+ * ssh channel to use
+ * @param file
+ * to receive from remote
+ * @param target
+ * to store file into (if null, get only file info)
+ * @return file information of the file we received
+ * @throws IOException
+ * in case of network or protocol trouble
+ * @throws RemoteScpException
+ * in case of problems on the target system (connection is fine)
+ */
+ private FileInfo receiveStream(Channel channel, String file, OutputStream targetStream)
+ throws IOException, RemoteScpException {
+ byte[] buffer = new byte[BUFFER_SIZE];
+
+ OutputStream os = channel.getOutputStream();
+ InputStream is = channel.getInputStream();
+ try {
+ if (channel.isConnected()) {
+ channel.start();
+ } else {
+ channel.connect();
+ }
+ } catch (JSchException e1) {
+ throw (IOException) new IOException("Channel connection problems").initCause(e1);
+ }
+ os.write(0x0);
+ os.flush();
+
+ FileInfo fileInfo = new FileInfo();
+
+ while (true) {
+ int c = is.read();
+ if (c < 0) {
+ throw new RemoteScpException("Remote scp terminated unexpectedly.");
+ }
+
+ String line = receiveLine(is);
+
+ if (c == 'T') {
+ parseTLine(line, fileInfo);
+ os.write(0x0);
+ os.flush();
+ continue;
+ }
+ if ((c == 1) || (c == 2)) {
+ throw new RemoteScpException("Remote SCP error: " + line);
+ }
+
+ if (c == 'C') {
+ parseCLine(line, fileInfo);
+ break;
+ }
+ throw new RemoteScpException("Remote SCP error: " + ((char) c) + line);
+ }
+ if (targetStream != null) {
+
+ os.write(0x0);
+ os.flush();
+
+ try {
+ long remain = fileInfo.getLength();
+
+ while (remain > 0) {
+ int trans;
+ if (remain > buffer.length) {
+ trans = buffer.length;
+ } else {
+ trans = (int) remain;
+ }
+
+ int thisTimeReceived = is.read(buffer, 0, trans);
+
+ if (thisTimeReceived < 0) {
+ throw new IOException("Remote scp terminated connection unexpectedly");
+ }
+
+ targetStream.write(buffer, 0, thisTimeReceived);
+
+ remain -= thisTimeReceived;
+ }
+
+ targetStream.close();
+ } catch (IOException e) {
+ if (targetStream != null) {
+ targetStream.close();
+ }
+ throw (e);
+ }
+
+ readResponse(is);
+
+ os.write(0x0);
+ os.flush();
+ }
+ return fileInfo;
+ }
+
+ /**
+ * @return
+ * @throws JSchException
+ */
+ private ChannelExec getExecChannel() throws JSchException {
+ ChannelExec channel;
+ channel = (ChannelExec) session.openChannel("exec");
+ return channel;
+ }
+
+ /**
+ * Copy a local file to a remote site, uses the specified mode when creating the file on the
+ * remote side.
+ *
+ * @param localFile
+ * Path and name of local file. Must be absolute.
+ * @param remoteTargetDir
+ * Remote target directory where the file has to end up (optional)
+ * @param remoteTargetName
+ * file name to use on the target system
+ * @param mode
+ * a four digit string (e.g., 0644, see "man chmod", "man open")
+ * @throws IOException
+ * in case of network problems
+ * @throws RemoteScpException
+ * in case of problems on the target system (connection ok)
+ */
+ public void put(String localFile, String remoteTargetDir, String remoteTargetName, String mode)
+ throws IOException, RemoteScpException {
+ ChannelExec channel = null;
+
+ if ((localFile == null) || (remoteTargetName == null)) {
+ throw new IllegalArgumentException("Null argument.");
+ }
+
+ if (mode != null) {
+ if (mode.length() != MODE_LENGTH) {
+ throw new IllegalArgumentException("Invalid mode.");
+ }
+
+ for (int i = 0; i < mode.length(); i++) {
+ if (!Character.isDigit(mode.charAt(i))) {
+ throw new IllegalArgumentException("Invalid mode.");
+ }
+ }
+ }
+
+ String cmd = "scp -t ";
+ if (mode != null) {
+ cmd = cmd + "-p ";
+ }
+ if (remoteTargetDir != null && remoteTargetDir.length() > 0) {
+ cmd = cmd + "-d " + remoteTargetDir;
+ }
+
+ try {
+ channel = getExecChannel();
+ channel.setCommand(cmd);
+ sendFile(channel, localFile, remoteTargetName, mode);
+ channel.disconnect();
+ } catch (JSchException e) {
+ if (channel != null) {
+ channel.disconnect();
+ }
+ throw (IOException) new IOException("Error during SCP transfer." + e.getMessage())
+ .initCause(e);
+ }
+ }
+
+ /**
+ * Download a file from the remote server to a local file.
+ *
+ * @param remoteFile
+ * Path and name of the remote file.
+ * @param localTarget
+ * Local file where to store the data. Must be absolute.
+ * @throws IOException
+ * in case of network problems
+ * @throws RemoteScpException
+ * in case of problems on the target system (connection ok)
+ */
+ public void get(String remoteFile, String localTarget) throws IOException, RemoteScpException {
+ File f = new File(localTarget);
+ FileOutputStream fop = new FileOutputStream(f);
+ get(remoteFile, fop);
+ }
+
+ /**
+ * Download a file from the remote server into an OutputStream
+ *
+ * @param remoteFile
+ * Path and name of the remote file.
+ * @param localTarget
+ * OutputStream to store the data.
+ * @throws IOException
+ * in case of network problems
+ * @throws RemoteScpException
+ * in case of problems on the target system (connection ok)
+ */
+ public void get(String remoteFile, OutputStream localTarget) throws IOException,
+ RemoteScpException {
+ ChannelExec channel = null;
+
+ if ((remoteFile == null) || (localTarget == null)) {
+ throw new IllegalArgumentException("Null argument.");
+ }
+
+ String cmd = "scp -p -f " + remoteFile;
+
+ try {
+ channel = getExecChannel();
+ channel.setCommand(cmd);
+ receiveStream(channel, remoteFile, localTarget);
+ channel.disconnect();
+ } catch (JSchException e) {
+ if (channel != null) {
+ channel.disconnect();
+ }
+ throw (IOException) new IOException("Error during SCP transfer." + e.getMessage())
+ .initCause(e);
+ }
+ }
+
+ /**
+ * Initiates an SCP sequence but stops after getting fileinformation header
+ *
+ * @param remoteFile
+ * to get information for
+ * @return the file information got
+ * @throws IOException
+ * in case of network problems
+ * @throws RemoteScpException
+ * in case of problems on the target system (connection ok)
+ */
+ public FileInfo getFileinfo(String remoteFile) throws IOException, RemoteScpException {
+ ChannelExec channel = null;
+ FileInfo fileInfo = null;
+
+ if (remoteFile == null) {
+ throw new IllegalArgumentException("Null argument.");
+ }
+
+ String cmd = "scp -p -f \"" + remoteFile + "\"";
+
+ try {
+ channel = getExecChannel();
+ channel.setCommand(cmd);
+ fileInfo = receiveStream(channel, remoteFile, null);
+ channel.disconnect();
+ } catch (JSchException e) {
+ throw (IOException) new IOException("Error during SCP transfer." + e.getMessage())
+ .initCause(e);
+ } finally {
+ if (channel != null) {
+ channel.disconnect();
+ }
+ }
+ return fileInfo;
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/repository/ssh/SshCache.java b/src/java/org/apache/ivy/plugins/repository/ssh/SshCache.java
new file mode 100644
index 0000000..095ec61
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/repository/ssh/SshCache.java
@@ -0,0 +1,453 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.repository.ssh;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.ivy.core.IvyContext;
+import org.apache.ivy.core.event.IvyEvent;
+import org.apache.ivy.core.event.IvyListener;
+import org.apache.ivy.core.event.resolve.EndResolveEvent;
+import org.apache.ivy.util.Checks;
+import org.apache.ivy.util.Credentials;
+import org.apache.ivy.util.CredentialsUtil;
+import org.apache.ivy.util.Message;
+
+import com.jcraft.jsch.ChannelSftp;
+import com.jcraft.jsch.JSch;
+import com.jcraft.jsch.JSchException;
+import com.jcraft.jsch.Session;
+import com.jcraft.jsch.UIKeyboardInteractive;
+import com.jcraft.jsch.UserInfo;
+import com.jcraft.jsch.agentproxy.Connector;
+import com.jcraft.jsch.agentproxy.ConnectorFactory;
+import com.jcraft.jsch.agentproxy.RemoteIdentityRepository;
+
+/**
+ * a class to cache SSH Connections and Channel for the SSH Repository each session is defined by
+ * connecting user / host / port two maps are used to find cache entries one map is using the above
+ * keys, the other uses the session itself
+ */
+public final class SshCache {
+
+ private static final int SSH_DEFAULT_PORT = 22;
+
+ private SshCache() {
+ };
+
+ private static SshCache instance = new SshCache();
+
+ public static SshCache getInstance() {
+ return instance;
+ }
+
+ private class Entry {
+ private Session session = null;
+
+ private ChannelSftp channelSftp = null;
+
+ private String host = null;
+
+ private String user = null;
+
+ private int port = SSH_DEFAULT_PORT;
+
+ /**
+ * @return the host
+ */
+ public String getHost() {
+ return host;
+ }
+
+ /**
+ * @return the port
+ */
+ public int getPort() {
+ return port;
+ }
+
+ /**
+ * @return the user
+ */
+ public String getUser() {
+ return user;
+ }
+
+ public Entry(Session newSession, String newUser, String newHost, int newPort) {
+ session = newSession;
+ host = newHost;
+ user = newUser;
+ port = newPort;
+ IvyContext.getContext().getEventManager().addIvyListener(new IvyListener() {
+ public void progress(IvyEvent event) {
+ event.getSource().removeIvyListener(this);
+ clearSession(session);
+ }
+ }, EndResolveEvent.NAME);
+ }
+
+ /**
+ * attach an sftp channel to this cache entry
+ *
+ * @param newChannel
+ * to attach
+ */
+ public void setChannelSftp(ChannelSftp newChannel) {
+ if (channelSftp != null && newChannel != null) {
+ throw new IllegalStateException("Only one sftp channelSftp per session allowed");
+ }
+ this.channelSftp = newChannel;
+ }
+
+ /**
+ * @return the attached sftp channel
+ */
+ public ChannelSftp getChannelSftp() {
+ return channelSftp;
+ }
+
+ /**
+ * @return the session
+ */
+ private Session getSession() {
+ return session;
+ }
+
+ /**
+ * remove channelSftp and disconnect if necessary
+ */
+ public void releaseChannelSftp() {
+ if (channelSftp != null) {
+ if (channelSftp.isConnected()) {
+ Message.verbose(":: SFTP :: closing sftp connection from " + host + "...");
+ channelSftp.disconnect();
+ channelSftp = null;
+ Message.verbose(":: SFTP :: sftp connection closed from " + host);
+ }
+ }
+ }
+ }
+
+ /**
+ * key is username / host / port
+ *
+ * @see SshCache.createCacheKey() for details
+ */
+ private Map uriCacheMap = new HashMap();
+
+ /**
+ * key is the session itself
+ */
+ private Map sessionCacheMap = new HashMap();
+
+ /**
+ * retrieves a session entry for a given hostname from the cache
+ *
+ * @param hostname
+ * to retrieve session for
+ * @return null or the existing entry
+ */
+ private Entry getCacheEntry(String user, String host, int port) {
+ return (Entry) uriCacheMap.get(createCacheKey(user, host, port));
+ }
+
+ /**
+ * Creates a cobined cache key from the given key parts
+ *
+ * @param user
+ * name of the user
+ * @param host
+ * of the connection
+ * @param port
+ * of the connection
+ * @return key for the cache
+ */
+ private static String createCacheKey(String user, String host, int port) {
+ String portToUse = "22";
+ if (port != -1 && port != SSH_DEFAULT_PORT) {
+ portToUse = Integer.toString(port);
+ }
+ return user.toLowerCase(Locale.US).trim() + "@" + host.toLowerCase(Locale.US).trim() + ":"
+ + portToUse;
+ }
+
+ /**
+ * retrieves a session entry for a given session from the cache
+ *
+ * @param session
+ * to retrieve cache entry for
+ * @return null or the existing entry
+ */
+ private Entry getCacheEntry(Session session) {
+ return (Entry) sessionCacheMap.get(session);
+ }
+
+ /**
+ * Sets a session to a given combined key into the cache If an old session object already
+ * exists, close and remove it
+ *
+ * @param user
+ * of the session
+ * @param host
+ * of the session
+ * @param port
+ * of the session
+ * @param newSession
+ * Session to save
+ */
+ private void setSession(String user, String host, int port, Session newSession) {
+ Entry entry = (Entry) uriCacheMap.get(createCacheKey(user, host, port));
+ Session oldSession = null;
+ if (entry != null) {
+ oldSession = entry.getSession();
+ }
+ if (oldSession != null && !oldSession.equals(newSession) && oldSession.isConnected()) {
+ entry.releaseChannelSftp();
+ String oldhost = oldSession.getHost();
+ Message.verbose(":: SSH :: closing ssh connection from " + oldhost + "...");
+ oldSession.disconnect();
+ Message.verbose(":: SSH :: ssh connection closed from " + oldhost);
+ }
+ if ((newSession == null) && (entry != null)) {
+ uriCacheMap.remove(createCacheKey(user, host, port));
+ if (entry.getSession() != null) {
+ sessionCacheMap.remove(entry.getSession());
+ }
+ } else {
+ Entry newEntry = new Entry(newSession, user, host, port);
+ uriCacheMap.put(createCacheKey(user, host, port), newEntry);
+ sessionCacheMap.put(newSession, newEntry);
+ }
+ }
+
+ /**
+ * discardes session entries from the cache
+ *
+ * @param session
+ * to clear
+ */
+ public void clearSession(Session session) {
+ Entry entry = (Entry) sessionCacheMap.get(session);
+ if (entry != null) {
+ setSession(entry.getUser(), entry.getHost(), entry.getPort(), null);
+ }
+ }
+
+ /**
+ * retrieves an sftp channel from the cache
+ *
+ * @param session
+ * to connect to
+ * @return channelSftp or null if not successful (channel not existent or dead)
+ */
+ public ChannelSftp getChannelSftp(Session session) throws IOException {
+ ChannelSftp channel = null;
+ Entry entry = getCacheEntry(session);
+ if (entry != null) {
+ channel = entry.getChannelSftp();
+ if (channel != null && !channel.isConnected()) {
+ entry.releaseChannelSftp();
+ channel = null;
+ }
+ }
+ return channel;
+ }
+
+ /**
+ * attaches a channelSftp to an existing session cache entry
+ *
+ * @param session
+ * to attach the channel to
+ * @param channel
+ * channel to attach
+ */
+ public void attachChannelSftp(Session session, ChannelSftp channel) {
+ Entry entry = getCacheEntry(session);
+ if (entry == null) {
+ throw new IllegalArgumentException("No entry for " + session + " in the cache");
+ }
+ entry.setChannelSftp(channel);
+ }
+
+ /**
+ * Attempts to connect to a local SSH agent (using either UNIX sockets or PuTTY's Pageant)
+ *
+ * @param jsch
+ * Connection to be attached to an available local agent
+ * @return true if connected to agent, false otherwise
+ */
+ private boolean attemptAgentUse(JSch jsch) {
+ try {
+ Connector con = ConnectorFactory.getDefault().createConnector();
+ jsch.setIdentityRepository(new RemoteIdentityRepository(con));
+ return true;
+ } catch (Exception e) {
+ Message.verbose(":: SSH :: Failure connecting to agent :: " + e.toString());
+ return false;
+ }
+ }
+
+ /**
+ * Gets a session from the cache or establishes a new session if necessary
+ *
+ * @param host
+ * to connect to
+ * @param port
+ * to use for session (-1 == use standard port)
+ * @param username
+ * for the session to use
+ * @param userPassword
+ * to use for authentication (optional)
+ * @param pemFile
+ * File to use for public key authentication
+ * @param pemPassword
+ * to use for accessing the pemFile (optional)
+ * @param passFile
+ * to store credentials
+ * @param allowedAgentUse
+ * Whether to communicate with an agent for authentication
+ * @return session or null if not successful
+ */
+ public Session getSession(String host, int port, String username, String userPassword,
+ File pemFile, String pemPassword, File passFile, boolean allowedAgentUse)
+ throws IOException {
+ Checks.checkNotNull(host, "host");
+ Checks.checkNotNull(username, "user");
+ Entry entry = getCacheEntry(username, host, port);
+ Session session = null;
+ if (entry != null) {
+ session = entry.getSession();
+ }
+ if (session == null || !session.isConnected()) {
+ Message.verbose(":: SSH :: connecting to " + host + "...");
+ try {
+ JSch jsch = new JSch();
+ if (port != -1) {
+ session = jsch.getSession(username, host, port);
+ } else {
+ session = jsch.getSession(username, host);
+ }
+ if (allowedAgentUse) {
+ attemptAgentUse(jsch);
+ }
+ if (pemFile != null) {
+ jsch.addIdentity(pemFile.getAbsolutePath(), pemPassword);
+ }
+ session.setUserInfo(new CfUserInfo(host, username, userPassword, pemFile,
+ pemPassword, passFile));
+ session.setDaemonThread(true);
+
+ Properties config = new Properties();
+ config.setProperty("PreferredAuthentications",
+ "publickey,keyboard-interactive,password");
+ session.setConfig(config);
+
+ session.connect();
+ Message.verbose(":: SSH :: connected to " + host + "!");
+ setSession(username, host, port, session);
+ } catch (JSchException e) {
+ if (passFile != null && passFile.exists()) {
+ passFile.delete();
+ }
+ IOException ex = new IOException(e.getMessage());
+ ex.initCause(e);
+ throw ex;
+ }
+ }
+ return session;
+ }
+
+ /**
+ * feeds in password silently into JSch
+ */
+ private static class CfUserInfo implements UserInfo, UIKeyboardInteractive {
+
+ private String userPassword;
+
+ private String pemPassword;
+
+ private String userName;
+
+ private final File pemFile;
+
+ private final String host;
+
+ private final File passfile;
+
+ public CfUserInfo(String host, String userName, String userPassword, File pemFile,
+ String pemPassword, File passfile) {
+ this.userPassword = userPassword;
+ this.pemPassword = pemPassword;
+ this.host = host;
+ this.passfile = passfile;
+ this.userName = userName;
+ this.pemFile = pemFile;
+ }
+
+ public void showMessage(String message) {
+ Message.info(message);
+ }
+
+ public boolean promptYesNo(String message) {
+ return true;
+ }
+
+ public boolean promptPassword(String message) {
+ return true;
+ }
+
+ public boolean promptPassphrase(String message) {
+ return true;
+ }
+
+ public String getPassword() {
+ if (userPassword == null) {
+ Credentials c = CredentialsUtil.promptCredentials(new Credentials(null, host,
+ userName, userPassword), passfile);
+ if (c != null) {
+ userName = c.getUserName();
+ userPassword = c.getPasswd();
+ }
+ }
+ return userPassword;
+ }
+
+ public String getPassphrase() {
+ if (pemPassword == null && pemFile != null) {
+ Credentials c = CredentialsUtil.promptCredentials(
+ new Credentials(null, pemFile.getAbsolutePath(), userName, pemPassword),
+ passfile);
+ if (c != null) {
+ userName = c.getUserName();
+ pemPassword = c.getPasswd();
+ }
+ }
+ return pemPassword;
+ }
+
+ public String[] promptKeyboardInteractive(String destination, String name,
+ String instruction, String[] prompt, boolean[] echo) {
+ return new String[] {getPassword()};
+ }
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/repository/ssh/SshRepository.java b/src/java/org/apache/ivy/plugins/repository/ssh/SshRepository.java
new file mode 100644
index 0000000..ec5240c
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/repository/ssh/SshRepository.java
@@ -0,0 +1,473 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.repository.ssh;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringReader;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.ivy.plugins.repository.Resource;
+import org.apache.ivy.util.Message;
+
+import com.jcraft.jsch.ChannelExec;
+import com.jcraft.jsch.JSchException;
+import com.jcraft.jsch.Session;
+
+/**
+ * Ivy Repository based on SSH
+ */
+public class SshRepository extends AbstractSshBasedRepository {
+
+ private static final int BUFFER_SIZE = 64 * 1024;
+
+ private static final String ARGUMENT_PLACEHOLDER = "%arg";
+
+ private static final int POLL_SLEEP_TIME = 500;
+
+ private char fileSeparator = '/';
+
+ private String listCommand = "ls -1";
+
+ private String existCommand = "ls";
+
+ private String createDirCommand = "mkdir";
+
+ private String publishPermissions = null;
+
+ /**
+ * create a new resource with lazy initializing
+ */
+ public Resource getResource(String source) {
+ Message.debug("SShRepository:getResource called: " + source);
+ return new SshResource(this, source);
+ }
+
+ /**
+ * Fetch the needed file information for a given file (size, last modification time) and report
+ * it back in a SshResource
+ *
+ * @param source
+ * ssh uri for the file to get info for
+ * @return SshResource filled with the needed informations
+ * @see org.apache.ivy.plugins.repository.Repository#getResource(java.lang.String)
+ */
+ public SshResource resolveResource(String source) {
+ Message.debug("SShRepository:resolveResource called: " + source);
+ SshResource result = null;
+ Session session = null;
+ try {
+ session = getSession(source);
+ Scp myCopy = new Scp(session);
+ Scp.FileInfo fileInfo = myCopy.getFileinfo(new URI(source).getPath());
+ result = new SshResource(this, source, true, fileInfo.getLength(),
+ fileInfo.getLastModified());
+ } catch (IOException e) {
+ if (session != null) {
+ releaseSession(session, source);
+ }
+ result = new SshResource();
+ } catch (URISyntaxException e) {
+ if (session != null) {
+ releaseSession(session, source);
+ }
+ result = new SshResource();
+ } catch (RemoteScpException e) {
+ result = new SshResource();
+ }
+ Message.debug("SShRepository:resolveResource end.");
+ return result;
+ }
+
+ /**
+ * Reads out the output of a ssh session exec
+ *
+ * @param channel
+ * Channel to read from
+ * @param strStdout
+ * StringBuffer that receives Session Stdout output
+ * @param strStderr
+ * StringBuffer that receives Session Stderr output
+ * @throws IOException
+ * in case of trouble with the network
+ */
+ private void readSessionOutput(ChannelExec channel, StringBuffer strStdout,
+ StringBuffer strStderr) throws IOException {
+ InputStream stdout = channel.getInputStream();
+ InputStream stderr = channel.getErrStream();
+
+ try {
+ channel.connect();
+ } catch (JSchException e1) {
+ throw (IOException) new IOException("Channel connection problems").initCause(e1);
+ }
+
+ byte[] buffer = new byte[BUFFER_SIZE];
+ while (true) {
+ int avail = 0;
+ while ((avail = stdout.available()) > 0) {
+ int len = stdout.read(buffer, 0, (avail > BUFFER_SIZE - 1 ? BUFFER_SIZE : avail));
+ strStdout.append(new String(buffer, 0, len));
+ }
+ while ((avail = stderr.available()) > 0) {
+ int len = stderr.read(buffer, 0, (avail > BUFFER_SIZE - 1 ? BUFFER_SIZE : avail));
+ strStderr.append(new String(buffer, 0, len));
+ }
+ if (channel.isClosed()) {
+ break;
+ }
+ try {
+ Thread.sleep(POLL_SLEEP_TIME);
+ } catch (Exception ee) {
+ // ignored
+ }
+ }
+ int avail = 0;
+ while ((avail = stdout.available()) > 0) {
+ int len = stdout.read(buffer, 0, (avail > BUFFER_SIZE - 1 ? BUFFER_SIZE : avail));
+ strStdout.append(new String(buffer, 0, len));
+ }
+ while ((avail = stderr.available()) > 0) {
+ int len = stderr.read(buffer, 0, (avail > BUFFER_SIZE - 1 ? BUFFER_SIZE : avail));
+ strStderr.append(new String(buffer, 0, len));
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.ivy.repository.Repository#list(java.lang.String)
+ */
+ public List list(String parent) throws IOException {
+ Message.debug("SShRepository:list called: " + parent);
+ ArrayList result = new ArrayList();
+ Session session = null;
+ ChannelExec channel = null;
+ session = getSession(parent);
+ channel = getExecChannel(session);
+ URI parentUri = null;
+ try {
+ parentUri = new URI(parent);
+ } catch (URISyntaxException e) {
+ IOException ioe = new IOException("The uri '" + parent + "' is not valid!");
+ ioe.initCause(e);
+ throw ioe;
+ }
+ String fullCmd = replaceArgument(listCommand, parentUri.getPath());
+ channel.setCommand(fullCmd);
+ StringBuffer stdOut = new StringBuffer();
+ StringBuffer stdErr = new StringBuffer();
+ readSessionOutput(channel, stdOut, stdErr);
+ if (channel.getExitStatus() != 0) {
+ Message.error("Ssh ListCommand exited with status != 0");
+ Message.error(stdErr.toString());
+ return null;
+ } else {
+ BufferedReader br = new BufferedReader(new StringReader(stdOut.toString()));
+ String line = null;
+ while ((line = br.readLine()) != null) {
+ result.add(line);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * @param session
+ * @return
+ * @throws JSchException
+ */
+ private ChannelExec getExecChannel(Session session) throws IOException {
+ ChannelExec channel;
+ try {
+ channel = (ChannelExec) session.openChannel("exec");
+ } catch (JSchException e) {
+ throw new IOException();
+ }
+ return channel;
+ }
+
+ /**
+ * Replace the argument placeholder with argument or append the argument if no placeholder is
+ * present
+ *
+ * @param command
+ * with argument placeholder or not
+ * @param argument
+ * @return replaced full command
+ */
+ private String replaceArgument(String command, String argument) {
+ String fullCmd;
+ if (command.indexOf(ARGUMENT_PLACEHOLDER) == -1) {
+ fullCmd = command + " " + argument;
+ } else {
+ fullCmd = command.replaceAll(ARGUMENT_PLACEHOLDER, argument);
+ }
+ return fullCmd;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.ivy.repository.Repository#put(java.io.File, java.lang.String, boolean)
+ */
+ public void put(File source, String destination, boolean overwrite) throws IOException {
+ Message.debug("SShRepository:put called: " + destination);
+ Session session = getSession(destination);
+
+ URI destinationUri = null;
+ try {
+ destinationUri = new URI(destination);
+ } catch (URISyntaxException e) {
+ IOException ioe = new IOException("The uri '" + destination + "' is not valid!");
+ ioe.initCause(e);
+ throw ioe;
+ }
+
+ try {
+ String filePath = destinationUri.getPath();
+ int lastSep = filePath.lastIndexOf(fileSeparator);
+ String path;
+ String name;
+ if (lastSep == -1) {
+ name = filePath;
+ path = null;
+ } else {
+ name = filePath.substring(lastSep + 1);
+ path = filePath.substring(0, lastSep);
+ }
+ if (!overwrite) {
+ if (checkExistence(filePath, session)) {
+ throw new IOException("destination file exists and overwrite == false");
+ }
+ }
+ if (path != null) {
+ makePath(path, session);
+ }
+ Scp myCopy = new Scp(session);
+ myCopy.put(source.getCanonicalPath(), path, name, publishPermissions);
+ } catch (IOException e) {
+ if (session != null) {
+ releaseSession(session, destination);
+ }
+ throw e;
+ } catch (RemoteScpException e) {
+ throw new IOException(e.getMessage());
+ }
+ }
+
+ /**
+ * Tries to create a directory path on the target system
+ *
+ * @param path
+ * to create
+ * @param connnection
+ * to use
+ */
+ private void makePath(String path, Session session) throws IOException {
+ ChannelExec channel = null;
+ String trimmed = path;
+ try {
+ while (trimmed.length() > 0 && trimmed.charAt(trimmed.length() - 1) == fileSeparator) {
+ trimmed = trimmed.substring(0, trimmed.length() - 1);
+ }
+ if (trimmed.length() == 0 || checkExistence(trimmed, session)) {
+ return;
+ }
+ int nextSlash = trimmed.lastIndexOf(fileSeparator);
+ if (nextSlash > 0) {
+ String parent = trimmed.substring(0, nextSlash);
+ makePath(parent, session);
+ }
+ channel = getExecChannel(session);
+ String mkdir = replaceArgument(createDirCommand, trimmed);
+ Message.debug("SShRepository: trying to create path: " + mkdir);
+ channel.setCommand(mkdir);
+ StringBuffer stdOut = new StringBuffer();
+ StringBuffer stdErr = new StringBuffer();
+ readSessionOutput(channel, stdOut, stdErr);
+ } finally {
+ if (channel != null) {
+ channel.disconnect();
+ }
+ }
+ }
+
+ /**
+ * check for existence of file or dir on target system
+ *
+ * @param filePath
+ * to the object to check
+ * @param session
+ * to use
+ * @return true: object exists, false otherwise
+ */
+ private boolean checkExistence(String filePath, Session session) throws IOException {
+ Message.debug("SShRepository: checkExistence called: " + filePath);
+ ChannelExec channel = null;
+ channel = getExecChannel(session);
+ String fullCmd = replaceArgument(existCommand, filePath);
+ channel.setCommand(fullCmd);
+ StringBuffer stdOut = new StringBuffer();
+ StringBuffer stdErr = new StringBuffer();
+ readSessionOutput(channel, stdOut, stdErr);
+ return channel.getExitStatus() == 0;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.ivy.repository.Repository#get(java.lang.String, java.io.File)
+ */
+ public void get(String source, File destination) throws IOException {
+ Message.debug("SShRepository:get called: " + source + " to "
+ + destination.getCanonicalPath());
+ if (destination.getParentFile() != null) {
+ destination.getParentFile().mkdirs();
+ }
+ Session session = getSession(source);
+
+ URI sourceUri = null;
+ try {
+ sourceUri = new URI(source);
+ } catch (URISyntaxException e) {
+ IOException ioe = new IOException("The uri '" + source + "' is not valid!");
+ ioe.initCause(e);
+ throw ioe;
+ }
+
+ try {
+ Scp myCopy = new Scp(session);
+ myCopy.get(sourceUri.getPath(), destination.getCanonicalPath());
+ } catch (IOException e) {
+ if (session != null) {
+ releaseSession(session, source);
+ }
+ throw e;
+ } catch (RemoteScpException e) {
+ throw new IOException(e.getMessage());
+ }
+ }
+
+ /**
+ * sets the list command to use for a directory listing listing must be only the filename and
+ * each filename on a separate line
+ *
+ * @param cmd
+ * to use. default is "ls -1"
+ */
+ public void setListCommand(String cmd) {
+ this.listCommand = cmd.trim();
+ }
+
+ /**
+ * @return the list command to use
+ */
+ public String getListCommand() {
+ return listCommand;
+ }
+
+ /**
+ * @return the createDirCommand
+ */
+ public String getCreateDirCommand() {
+ return createDirCommand;
+ }
+
+ /**
+ * @param createDirCommand
+ * the createDirCommand to set
+ */
+ public void setCreateDirCommand(String createDirCommand) {
+ this.createDirCommand = createDirCommand;
+ }
+
+ /**
+ * @return the existCommand
+ */
+ public String getExistCommand() {
+ return existCommand;
+ }
+
+ /**
+ * @param existCommand
+ * the existCommand to set
+ */
+ public void setExistCommand(String existCommand) {
+ this.existCommand = existCommand;
+ }
+
+ /**
+ * The file separator is the separator to use on the target system On a unix system it is '/',
+ * but I don't know, how this is solved on different ssh implementations. Using the default
+ * might be fine
+ *
+ * @param fileSeparator
+ * The fileSeparator to use. default '/'
+ */
+ public void setFileSeparator(char fileSeparator) {
+ this.fileSeparator = fileSeparator;
+ }
+
+ /**
+ * A four digit string (e.g., 0644, see "man chmod", "man open") specifying the permissions of
+ * the published files.
+ */
+ public void setPublishPermissions(String permissions) {
+ this.publishPermissions = permissions;
+ }
+
+ /**
+ * return ssh as scheme use the Resolver type name here? would be nice if it would be static, so
+ * we could use SshResolver.getTypeName()
+ */
+ protected String getRepositoryScheme() {
+ return "ssh";
+ }
+
+ /**
+ * Not really streaming...need to implement a proper streaming approach?
+ *
+ * @param resource
+ * to stream
+ * @return InputStream of the resource data
+ */
+ public InputStream openStream(SshResource resource) throws IOException {
+ Session session = getSession(resource.getName());
+ Scp scp = new Scp(session);
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ try {
+ scp.get(resource.getName(), os);
+ } catch (IOException e) {
+ if (session != null) {
+ releaseSession(session, resource.getName());
+ }
+ throw e;
+ } catch (RemoteScpException e) {
+ throw new IOException(e.getMessage());
+ }
+ return new ByteArrayInputStream(os.toByteArray());
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/repository/ssh/SshResource.java b/src/java/org/apache/ivy/plugins/repository/ssh/SshResource.java
new file mode 100644
index 0000000..7fc797e
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/repository/ssh/SshResource.java
@@ -0,0 +1,139 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.repository.ssh;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.ivy.plugins.repository.Resource;
+import org.apache.ivy.util.Message;
+
+/**
+ * Resource for SSH Ivy Repository
+ */
+public class SshResource implements Resource {
+
+ private boolean resolved = false;
+
+ private String uri = null;
+
+ private boolean bExists = false;
+
+ private long len = 0;
+
+ private long lastModified = 0;
+
+ private SshRepository repository = null;
+
+ public SshResource() {
+ resolved = true;
+ }
+
+ public SshResource(SshRepository repository, String uri) {
+ this.uri = uri;
+ this.repository = repository;
+ resolved = false;
+ }
+
+ public SshResource(SshRepository repository, String uri, boolean bExists, long len,
+ long lastModified) {
+ this.uri = uri;
+ this.bExists = bExists;
+ this.len = len;
+ this.lastModified = lastModified;
+ this.repository = repository;
+ resolved = true;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.ivy.repository.Resource#exists()
+ */
+ public boolean exists() {
+ if (!resolved) {
+ resolve();
+ }
+ return bExists;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.ivy.repository.Resource#getContentLength()
+ */
+ public long getContentLength() {
+ if (!resolved) {
+ resolve();
+ }
+ return len;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.ivy.repository.Resource#getLastModified()
+ */
+ public long getLastModified() {
+ if (!resolved) {
+ resolve();
+ }
+ return lastModified;
+ }
+
+ private void resolve() {
+ Message.debug("SShResource: resolving " + uri);
+ SshResource res = repository.resolveResource(uri);
+ len = res.getContentLength();
+ lastModified = res.getLastModified();
+ bExists = res.exists();
+ resolved = true;
+ Message.debug("SShResource: resolved " + this);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.ivy.repository.Resource#getName()
+ */
+ public String getName() {
+ return uri;
+ }
+
+ public String toString() {
+ StringBuffer buffer = new StringBuffer();
+ buffer.append("SshResource:");
+ buffer.append(uri);
+ buffer.append(" (");
+ buffer.append(len);
+ buffer.append(")]");
+ return buffer.toString();
+ }
+
+ public boolean isLocal() {
+ return false;
+ }
+
+ public Resource clone(String cloneName) {
+ return new SshResource(repository, cloneName);
+ }
+
+ public InputStream openStream() throws IOException {
+ return repository.openStream(this);
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/repository/url/ChainedRepository.java b/src/java/org/apache/ivy/plugins/repository/url/ChainedRepository.java
new file mode 100644
index 0000000..417b9a5
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/repository/url/ChainedRepository.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.repository.url;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.ivy.plugins.repository.AbstractRepository;
+import org.apache.ivy.plugins.repository.BasicResource;
+import org.apache.ivy.plugins.repository.Repository;
+import org.apache.ivy.plugins.repository.Resource;
+import org.apache.ivy.util.Message;
+
+public class ChainedRepository extends AbstractRepository {
+
+ private List/* Repository */repositories;
+
+ public void setRepositories(List/* Repository */repositories) {
+ this.repositories = repositories;
+ }
+
+ public Resource getResource(String source) throws IOException {
+ Iterator it = repositories.iterator();
+ while (it.hasNext()) {
+ Repository repository = (Repository) it.next();
+ logTry(repository);
+ try {
+ Resource r = repository.getResource(source);
+ if (r != null && r.exists()) {
+ logSuccess(repository);
+ return r;
+ }
+ } catch (Exception e) {
+ logFailed(repository, e);
+ }
+ }
+ // resource that basically doesn't exists
+ return new BasicResource(source, false, 0, 0, true);
+ }
+
+ public void get(String source, File destination) throws IOException {
+ Iterator it = repositories.iterator();
+ while (it.hasNext()) {
+ Repository repository = (Repository) it.next();
+ logTry(repository);
+ boolean ok = false;
+ try {
+ repository.get(source, destination);
+ ok = true;
+ } catch (Exception e) {
+ logFailed(repository, e);
+ }
+ if (ok) {
+ logSuccess(repository);
+ return;
+ }
+ }
+ throw newIOEFail("copy " + source + " into " + destination);
+ }
+
+ public List list(String parent) throws IOException {
+ Iterator it = repositories.iterator();
+ while (it.hasNext()) {
+ Repository repository = (Repository) it.next();
+ logTry(repository);
+ try {
+ List list = repository.list(parent);
+ if (list != null) {
+ logSuccess(repository);
+ return list;
+ }
+ } catch (Exception e) {
+ logFailed(repository, e);
+ }
+ }
+ throw newIOEFail("list contents in " + parent);
+ }
+
+ private void logTry(Repository repository) {
+ Message.debug("Mirrored repository " + getName() + ": trying " + repository.getName());
+ }
+
+ private void logFailed(Repository repository, Exception e) {
+ Message.warn("Mirrored repository " + getName() + ": " + repository.getName()
+ + " is not available", e);
+ Message.warn("Trying the next one in the mirror list...");
+ }
+
+ private void logSuccess(Repository repository) {
+ Message.debug("Mirrored repository " + getName() + ": success with " + repository.getName());
+ }
+
+ private IOException newIOEFail(String action) {
+ return new IOException("Mirrored repository " + getName() + ": fail to " + action
+ + " with every listed mirror");
+ }
+
+}
diff --git a/src/java/org/apache/ivy/plugins/repository/url/URLRepository.java b/src/java/org/apache/ivy/plugins/repository/url/URLRepository.java
new file mode 100644
index 0000000..098ccc3
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/repository/url/URLRepository.java
@@ -0,0 +1,141 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.repository.url;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+
+import org.apache.ivy.plugins.repository.AbstractRepository;
+import org.apache.ivy.plugins.repository.RepositoryCopyProgressListener;
+import org.apache.ivy.plugins.repository.Resource;
+import org.apache.ivy.plugins.repository.TransferEvent;
+import org.apache.ivy.util.FileUtil;
+import org.apache.ivy.util.url.ApacheURLLister;
+
+public class URLRepository extends AbstractRepository {
+ private RepositoryCopyProgressListener progress = new RepositoryCopyProgressListener(this);
+
+ private Map resourcesCache = new HashMap();
+
+ public Resource getResource(String source) throws IOException {
+ Resource res = (Resource) resourcesCache.get(source);
+ if (res == null) {
+ res = new URLResource(new URL(source));
+ resourcesCache.put(source, res);
+ }
+ return res;
+ }
+
+ public void get(String source, File destination) throws IOException {
+ fireTransferInitiated(getResource(source), TransferEvent.REQUEST_GET);
+ try {
+ Resource res = getResource(source);
+ long totalLength = res.getContentLength();
+ if (totalLength > 0) {
+ progress.setTotalLength(new Long(totalLength));
+ }
+ FileUtil.copy(new URL(source), destination, progress);
+ } catch (IOException ex) {
+ fireTransferError(ex);
+ throw ex;
+ } catch (RuntimeException ex) {
+ fireTransferError(ex);
+ throw ex;
+ } finally {
+ progress.setTotalLength(null);
+ }
+ }
+
+ public void put(File source, String destination, boolean overwrite) throws IOException {
+ if (!overwrite && getResource(destination).exists()) {
+ throw new IOException("destination file exists and overwrite == false");
+ }
+
+ fireTransferInitiated(getResource(destination), TransferEvent.REQUEST_PUT);
+ try {
+ long totalLength = source.length();
+ if (totalLength > 0) {
+ progress.setTotalLength(new Long(totalLength));
+ }
+ FileUtil.copy(source, new URL(destination), progress);
+ } catch (IOException ex) {
+ fireTransferError(ex);
+ throw ex;
+ } catch (RuntimeException ex) {
+ fireTransferError(ex);
+ throw ex;
+ } finally {
+ progress.setTotalLength(null);
+ }
+ }
+
+ private ApacheURLLister lister = new ApacheURLLister();
+
+ public List list(String parent) throws IOException {
+ if (parent.startsWith("http")) {
+ List urls = lister.listAll(new URL(parent));
+ if (urls != null) {
+ List ret = new ArrayList(urls.size());
+ for (ListIterator iter = urls.listIterator(); iter.hasNext();) {
+ URL url = (URL) iter.next();
+ ret.add(url.toExternalForm());
+ }
+ return ret;
+ }
+ } else if (parent.startsWith("file")) {
+ String path;
+ try {
+ URI uri = new URI(parent);
+ if (uri.isOpaque()) {
+ path = uri.getSchemeSpecificPart();
+ } else {
+ path = uri.getPath();
+ }
+ } catch (URISyntaxException e) {
+ IOException ioe = new IOException("Couldn't list content of '" + parent + "'");
+ ioe.initCause(e);
+ throw ioe;
+ }
+
+ File file = new File(path);
+ if (file.exists() && file.isDirectory()) {
+ String[] files = file.list();
+ List ret = new ArrayList(files.length);
+ URL context = path.endsWith("/") ? new URL(parent) : new URL(parent + "/");
+ for (int i = 0; i < files.length; i++) {
+ ret.add(new URL(context, files[i]).toExternalForm());
+ }
+ return ret;
+ } else {
+ return Collections.EMPTY_LIST;
+ }
+
+ }
+ return null;
+ }
+
+}
diff --git a/src/java/org/apache/ivy/plugins/repository/url/URLResource.java b/src/java/org/apache/ivy/plugins/repository/url/URLResource.java
new file mode 100644
index 0000000..c687308
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/repository/url/URLResource.java
@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.repository.url;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URISyntaxException;
+import java.net.URL;
+
+import org.apache.ivy.plugins.repository.LocalizableResource;
+import org.apache.ivy.plugins.repository.Resource;
+import org.apache.ivy.util.url.URLHandler.URLInfo;
+import org.apache.ivy.util.url.URLHandlerRegistry;
+
+public class URLResource implements LocalizableResource {
+ private URL url;
+
+ private boolean init = false;
+
+ private long lastModified;
+
+ private long contentLength;
+
+ private boolean exists;
+
+ public URLResource(URL url) {
+ this.url = url;
+ }
+
+ public String getName() {
+ return url.toExternalForm();
+ }
+
+ public Resource clone(String cloneName) {
+ try {
+ return new URLResource(new URL(cloneName));
+ } catch (MalformedURLException e) {
+ throw new IllegalArgumentException(
+ "bad clone name provided: not suitable for an URLResource: " + cloneName);
+ }
+ }
+
+ public long getLastModified() {
+ if (!init) {
+ init();
+ }
+ return lastModified;
+ }
+
+ private void init() {
+ URLInfo info = URLHandlerRegistry.getDefault().getURLInfo(url);
+ contentLength = info.getContentLength();
+ lastModified = info.getLastModified();
+ exists = info.isReachable();
+ init = true;
+ }
+
+ public long getContentLength() {
+ if (!init) {
+ init();
+ }
+ return contentLength;
+ }
+
+ public boolean exists() {
+ if (!init) {
+ init();
+ }
+ return exists;
+ }
+
+ public URL getURL() {
+ return url;
+ }
+
+ public String toString() {
+ return getName();
+ }
+
+ public boolean isLocal() {
+ return url.getProtocol().equals("file");
+ }
+
+ public InputStream openStream() throws IOException {
+ return URLHandlerRegistry.getDefault().openStream(url);
+ }
+
+ public File getFile() {
+ if (!isLocal()) {
+ throw new IllegalStateException("Cannot get the local file for the not local resource "
+ + url);
+ }
+ try {
+ return new File(url.toURI());
+ } catch (URISyntaxException e) {
+ return new File(url.getPath());
+ }
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/repository/vfs/VfsRepository.java b/src/java/org/apache/ivy/plugins/repository/vfs/VfsRepository.java
new file mode 100644
index 0000000..aead7cf
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/repository/vfs/VfsRepository.java
@@ -0,0 +1,216 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.ivy.plugins.repository.vfs;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.commons.vfs.FileContent;
+import org.apache.commons.vfs.FileObject;
+import org.apache.commons.vfs.FileSystemException;
+import org.apache.commons.vfs.FileSystemManager;
+import org.apache.commons.vfs.FileType;
+import org.apache.commons.vfs.impl.StandardFileSystemManager;
+import org.apache.ivy.plugins.repository.AbstractRepository;
+import org.apache.ivy.plugins.repository.RepositoryCopyProgressListener;
+import org.apache.ivy.plugins.repository.Resource;
+import org.apache.ivy.plugins.repository.TransferEvent;
+import org.apache.ivy.util.CopyProgressListener;
+import org.apache.ivy.util.FileUtil;
+import org.apache.ivy.util.Message;
+
+/**
+ * Implementation of a VFS repository
+ */
+public class VfsRepository extends AbstractRepository {
+ /**
+ * Name of the resource defining the Ivy VFS Repo configuration.
+ */
+ private static final String IVY_VFS_CONFIG = "ivy_vfs.xml";
+
+ private StandardFileSystemManager manager = null;
+
+ private final CopyProgressListener progress = new RepositoryCopyProgressListener(this);
+
+ /**
+ * Create a new Ivy VFS Repository Instance
+ */
+ public VfsRepository() {
+ }
+
+ private FileSystemManager getVFSManager() throws IOException {
+ synchronized (this) {
+ if (manager == null) {
+ manager = createVFSManager();
+ }
+ }
+ return manager;
+ }
+
+ private StandardFileSystemManager createVFSManager() throws IOException {
+ StandardFileSystemManager result = null;
+ try {
+ /*
+ * The DefaultFileSystemManager gets its configuration from the jakarta-vfs-common
+ * implementation which includes the res and tmp schemes which are of no use to use
+ * here. Using StandardFileSystemManager lets us specify which schemes to support as
+ * well as providing a mechanism to change this support without recompilation.
+ */
+ result = new StandardFileSystemManager() {
+ protected void configurePlugins() throws FileSystemException {
+ // disable automatic loading potential unsupported extensions
+ }
+ };
+ result.setConfiguration(getClass().getResource(IVY_VFS_CONFIG));
+ result.init();
+
+ // Generate and print a list of available schemes
+ Message.verbose("Available VFS schemes...");
+ String[] schemes = result.getSchemes();
+ Arrays.sort(schemes);
+ for (int i = 0; i < schemes.length; i++) {
+ Message.verbose("VFS Supported Scheme: " + schemes[i]);
+ }
+ } catch (FileSystemException e) {
+ /*
+ * If our attempt to initialize a VFS Repository fails we log the failure but continue
+ * on. Given that an Ivy instance may involve numerous different repository types, it
+ * seems overly cautious to throw a runtime exception on the initialization failure of
+ * just one repository type.
+ */
+ Message.error("Unable to initialize VFS repository manager!");
+ Message.error(e.getLocalizedMessage());
+ IOException error = new IOException(e.getLocalizedMessage());
+ error.initCause(e);
+ throw error;
+ }
+
+ return result;
+ }
+
+ protected void finalize() {
+ if (manager != null) {
+ manager.close();
+ manager = null;
+ }
+ }
+
+ /**
+ * Get a VfsResource
+ *
+ * @param source
+ * a <code>String</code> identifying a VFS Resource
+ * @throws <code>IOException</code> on failure
+ * @see "Supported File Systems in the jakarta-commons-vfs documentation"
+ */
+ public Resource getResource(String vfsURI) throws IOException {
+ return new VfsResource(vfsURI, getVFSManager());
+ }
+
+ /**
+ * Transfer a VFS Resource from the repository to the local file system.
+ *
+ * @param srcVfsURI
+ * a <code>String</code> identifying the VFS resource to be fetched
+ * @param destination
+ * a <code>File</code> identifying the destination file
+ * @throws <code>IOException</code> on failure
+ * @see "Supported File Systems in the jakarta-commons-vfs documentation"
+ */
+ public void get(String srcVfsURI, File destination) throws IOException {
+ VfsResource src = new VfsResource(srcVfsURI, getVFSManager());
+ fireTransferInitiated(src, TransferEvent.REQUEST_GET);
+ try {
+ FileContent content = src.getContent();
+ if (content == null) {
+ throw new IllegalArgumentException("invalid vfs uri " + srcVfsURI
+ + ": no content found");
+ }
+ FileUtil.copy(content.getInputStream(), destination, progress);
+ } catch (IOException ex) {
+ fireTransferError(ex);
+ throw ex;
+ } catch (RuntimeException ex) {
+ fireTransferError(ex);
+ throw ex;
+ }
+ }
+
+ /**
+ * Return a listing of the contents of a parent directory. Listing is a set of strings
+ * representing VFS URIs.
+ *
+ * @param vfsURI
+ * providing identifying a VFS provided resource
+ * @throws IOException
+ * on failure.
+ * @see "Supported File Systems in the jakarta-commons-vfs documentation"
+ */
+ public List list(String vfsURI) throws IOException {
+ ArrayList list = new ArrayList();
+ Message.debug("list called for URI" + vfsURI);
+ FileObject resourceImpl = getVFSManager().resolveFile(vfsURI);
+ Message.debug("resourceImpl=" + resourceImpl.toString());
+ Message.debug("resourceImpl.exists()" + resourceImpl.exists());
+ Message.debug("resourceImpl.getType()" + resourceImpl.getType());
+ Message.debug("FileType.FOLDER" + FileType.FOLDER);
+ if ((resourceImpl != null) && resourceImpl.exists()
+ && (resourceImpl.getType() == FileType.FOLDER)) {
+ FileObject[] children = resourceImpl.getChildren();
+ for (int i = 0; i < children.length; i++) {
+ FileObject child = children[i];
+ Message.debug("child " + i + child.getName().getURI());
+ list.add(VfsResource.normalize(child.getName().getURI()));
+ }
+ }
+ return list;
+ }
+
+ /**
+ * Transfer an Ivy resource to a VFS repository
+ *
+ * @param source
+ * a <code>File</code> indentifying the local file to transfer to the repository
+ * @param vfsURI
+ * a <code>String</code> identifying the destination VFS Resource.
+ * @param overwrite
+ * whether to overwrite an existing resource.
+ * @throws <code>IOException</code> on failure.
+ * @see "Supported File Systems in the jakarta-commons-vfs documentation"
+ */
+ public void put(File source, String vfsURI, boolean overwrite) throws IOException {
+ VfsResource dest = new VfsResource(vfsURI, getVFSManager());
+ fireTransferInitiated(dest, TransferEvent.REQUEST_PUT);
+ if (dest.physicallyExists() && !overwrite) {
+ throw new IOException("Cannot copy. Destination file: " + dest.getName()
+ + " exists and overwrite not set.");
+ }
+ if (dest.getContent() == null) {
+ throw new IllegalArgumentException("invalid vfs uri " + vfsURI
+ + " to put data to: resource has no content");
+ }
+
+ FileUtil.copy(new FileInputStream(source), dest.getContent().getOutputStream(), progress);
+ }
+
+}
diff --git a/src/java/org/apache/ivy/plugins/repository/vfs/VfsResource.java b/src/java/org/apache/ivy/plugins/repository/vfs/VfsResource.java
new file mode 100644
index 0000000..5cd7cc2
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/repository/vfs/VfsResource.java
@@ -0,0 +1,206 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.repository.vfs;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.commons.vfs.FileContent;
+import org.apache.commons.vfs.FileObject;
+import org.apache.commons.vfs.FileSystemException;
+import org.apache.commons.vfs.FileSystemManager;
+import org.apache.commons.vfs.FileType;
+import org.apache.ivy.plugins.repository.Resource;
+import org.apache.ivy.plugins.resolver.VfsResolver;
+import org.apache.ivy.util.Message;
+
+/**
+ * VFS implementation of the Resource interface
+ */
+public class VfsResource implements Resource {
+ private String vfsURI;
+
+ private FileSystemManager fsManager;
+
+ private transient boolean init = false;
+
+ private transient boolean exists;
+
+ private transient long lastModified;
+
+ private transient long contentLength;
+
+ private transient FileContent content = null;
+
+ private transient FileObject resourceImpl;
+
+ // Constructor
+ public VfsResource(String vfsURI, FileSystemManager fsManager) {
+ this.vfsURI = vfsURI;
+ this.fsManager = fsManager;
+ this.init = false;
+ }
+
+ private void init() {
+ if (!init) {
+ try {
+ resourceImpl = fsManager.resolveFile(vfsURI);
+ content = resourceImpl.getContent();
+
+ exists = resourceImpl.exists();
+ lastModified = content.getLastModifiedTime();
+ contentLength = content.getSize();
+ } catch (FileSystemException e) {
+ Message.debug(e);
+ Message.verbose(e.getLocalizedMessage());
+ exists = false;
+ lastModified = 0;
+ contentLength = 0;
+ }
+
+ init = true;
+ }
+ }
+
+ /**
+ * Get a list of direct descendents of the given resource. Note that attempts to get a list of
+ * children does <emphasize>not</emphasize> result in an error. Instead an error message is
+ * logged and an empty ArrayList returned.
+ *
+ * @return A <code>ArrayList</code> of VFSResources
+ */
+ public List getChildren() {
+ init();
+ ArrayList list = new ArrayList();
+ try {
+ if ((resourceImpl != null) && resourceImpl.exists()
+ && (resourceImpl.getType() == FileType.FOLDER)) {
+ FileObject[] children = resourceImpl.getChildren();
+ for (int i = 0; i < children.length; i++) {
+ FileObject child = children[i];
+ list.add(normalize(child.getName().getURI()));
+ }
+ }
+ } catch (IOException e) {
+ Message.debug(e);
+ Message.verbose(e.getLocalizedMessage());
+ }
+ return list;
+ }
+
+ public FileContent getContent() {
+ init();
+ return content;
+ }
+
+ /**
+ * Get the name of the resource.
+ *
+ * @return a <code>String</code> representing the Resource URL.
+ */
+ public String getName() {
+ return normalize(vfsURI);
+ }
+
+ public Resource clone(String cloneName) {
+ return new VfsResource(cloneName, fsManager);
+ }
+
+ /**
+ * The VFS FileName getURI method seems to have a bug in it where file: URIs will have 4 forward
+ * slashes instead of 3.
+ *
+ * @param vfsURI
+ * @return a normalized <class>String</class> representing the VFS URI
+ */
+ public static String normalize(String vfsURI) {
+ if (vfsURI == null) {
+ return "";
+ }
+
+ if (vfsURI.startsWith("file:////")) {
+ vfsURI = vfsURI.replaceFirst("////", "///");
+ }
+ return vfsURI;
+ }
+
+ /**
+ * Get the last modification time of the resource.
+ *
+ * @return a <code>long</code> indicating last modified time.
+ */
+ public long getLastModified() {
+ init();
+ return lastModified;
+ }
+
+ /**
+ * Get the size of the resource
+ *
+ * @return a <code>long</code> representing the size of the resource (in bytes).
+ */
+ public long getContentLength() {
+ init();
+ return contentLength;
+ }
+
+ /**
+ * Flag indicating whether a resource is available for querying
+ *
+ * @return <code>true</code> if the resource is available for querying, <code>false</code>
+ * otherwise.
+ */
+ public boolean exists() {
+ init();
+ return exists;
+ }
+
+ /**
+ * Return a flag indicating whether a provided VFS resource physically exists
+ *
+ * @return <code>true</code> if the resource physically exists, <code>false</code> otherwise.
+ */
+ public boolean physicallyExists() {
+ // TODO: there is no need for this method anymore, replace it by calling exists();
+ init();
+
+ try {
+ return resourceImpl.exists();
+ // originally I only checked for a FileSystemException. I expanded it to
+ // include all exceptions when I found it would throw a NPE exception when the query was
+ // run on non-wellformed VFS URI.
+ } catch (Exception e) {
+ Message.verbose("Fail to check the existance of the resource " + getName(), e);
+ return false;
+ }
+ }
+
+ public String toString() {
+ return VfsResolver.prepareForDisplay(getName());
+ }
+
+ public boolean isLocal() {
+ return getName().startsWith("file:");
+ }
+
+ public InputStream openStream() throws IOException {
+ return getContent().getInputStream();
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/repository/vfs/ivy_vfs.xml b/src/java/org/apache/ivy/plugins/repository/vfs/ivy_vfs.xml
new file mode 100644
index 0000000..347d73e
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/repository/vfs/ivy_vfs.xml
@@ -0,0 +1,50 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<!-- For all intents and purposes this is the providers.xml file included
+ in the jakarta-vfs-common distribution with the res and tmp schemas removed.
+ -->
+ <providers>
+ <default-provider class-name="org.apache.commons.vfs.provider.url.UrlFileProvider">
+ </default-provider>
+ <provider class-name="org.apache.commons.vfs.provider.local.DefaultLocalFileProvider">
+ <scheme name="file"/>
+ </provider>
+ <provider class-name="org.apache.commons.vfs.provider.ftp.FtpFileProvider">
+ <scheme name="ftp"/>
+ <if-available class-name="org.apache.commons.net.ftp.FTPFile"/>
+ </provider>
+ <!-- FIXME: not available anymore in commons-vfs but in commons-vfs-sandbox
+ <provider class-name="org.apache.commons.vfs.provider.smb.SmbFileProvider">
+ <scheme name="smb"/>
+ <if-available class-name="jcifs.smb.SmbFile"/>
+ </provider>
+ -->
+ <!--
+ <provider class-name="org.apache.commons.vfs.provider.http.HttpFileProvider">
+ <scheme name="http"/>
+ <if-available class-name="org.apache.commons.httpclient.HttpClient"/>
+ </provider>
+ -->
+
+ <provider class-name="org.apache.commons.vfs.provider.sftp.SftpFileProvider">
+ <scheme name="sftp"/>
+ <if-available class-name="javax.crypto.Cipher"/>
+ <if-available class-name="com.jcraft.jsch.JSch"/>
+ </provider>
+</providers>
diff --git a/src/java/org/apache/ivy/plugins/repository/vsftp/VsftpRepository.java b/src/java/org/apache/ivy/plugins/repository/vsftp/VsftpRepository.java
new file mode 100644
index 0000000..13f773f
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/repository/vsftp/VsftpRepository.java
@@ -0,0 +1,745 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.repository.vsftp;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.io.Reader;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.regex.Pattern;
+
+import org.apache.ivy.Ivy;
+import org.apache.ivy.core.IvyContext;
+import org.apache.ivy.core.IvyThread;
+import org.apache.ivy.core.event.IvyEvent;
+import org.apache.ivy.core.event.IvyListener;
+import org.apache.ivy.core.event.resolve.EndResolveEvent;
+import org.apache.ivy.plugins.repository.AbstractRepository;
+import org.apache.ivy.plugins.repository.BasicResource;
+import org.apache.ivy.plugins.repository.Resource;
+import org.apache.ivy.plugins.repository.TransferEvent;
+import org.apache.ivy.util.Checks;
+import org.apache.ivy.util.Message;
+
+/**
+ * Repository using SecureCRT vsftp command line program to access an sftp repository This is
+ * especially useful to leverage the gssapi authentication supported by SecureCRT. In caseswhere
+ * usual sftp is enough, prefer the 100% java solution of sftp repository. This requires SecureCRT
+ * to be in the PATH. Tested with SecureCRT 5.0.5
+ */
+public class VsftpRepository extends AbstractRepository {
+ private static final int LS_DATE_INDEX4 = 7;
+
+ private static final int LS_DATE_INDEX3 = 6;
+
+ private static final int LS_DATE_INDEX2 = 5;
+
+ private static final int LS_DATE_INDEX1 = 4;
+
+ private static final int LS_SIZE_INDEX = 3;
+
+ private static final int LS_PARTS_NUMBER = 9;
+
+ private static final int DISCONNECT_COMMAND_TIMEOUT = 300;
+
+ private static final int REUSE_CONNECTION_SLEEP_TIME = 10;
+
+ private static final int READER_ALIVE_SLEEP_TIME = 100;
+
+ private static final int MAX_READER_ALIVE_ATTEMPT = 5;
+
+ private static final int ERROR_SLEEP_TIME = 30;
+
+ private static final int PROMPT_SLEEP_TIME = 50;
+
+ private static final int MAX_READ_PROMPT_ATTEMPT = 5;
+
+ private static final int GET_JOIN_MAX_TIME = 100;
+
+ private static final int DEFAULT_REUSE_CONNECTION_TIME = 300000;
+
+ // reuse connection during 5 minutes by default
+
+ private static final int DEFAULT_READ_TIMEOUT = 30000;
+
+ private static final String PROMPT = "vsftp> ";
+
+ private static final SimpleDateFormat FORMAT = new SimpleDateFormat("MMM dd, yyyy HH:mm",
+ Locale.US);
+
+ private String host;
+
+ private String username;
+
+ private String authentication = "gssapi";
+
+ private Reader in;
+
+ private Reader err;
+
+ private PrintWriter out;
+
+ private volatile StringBuffer errors = new StringBuffer();
+
+ private long readTimeout = DEFAULT_READ_TIMEOUT;
+
+ private long reuseConnection = DEFAULT_REUSE_CONNECTION_TIME;
+
+ private volatile long lastCommand;
+
+ private volatile boolean inCommand;
+
+ private Process process;
+
+ private Thread connectionCleaner;
+
+ private Thread errorsReader;
+
+ private volatile long errorsLastUpdateTime;
+
+ private Ivy ivy = null;
+
+ public Resource getResource(String source) throws IOException {
+ initIvy();
+ return new VsftpResource(this, source);
+ }
+
+ private void initIvy() {
+ ivy = IvyContext.getContext().getIvy();
+ }
+
+ protected Resource getInitResource(String source) throws IOException {
+ try {
+ return lslToResource(source, sendCommand("ls -l " + source, true, true));
+ } catch (IOException ex) {
+ cleanup(ex);
+ throw ex;
+ } finally {
+ cleanup();
+ }
+ }
+
+ public void get(final String source, File destination) throws IOException {
+ initIvy();
+ try {
+ fireTransferInitiated(getResource(source), TransferEvent.REQUEST_GET);
+ File destDir = destination.getParentFile();
+ if (destDir != null) {
+ sendCommand("lcd " + destDir.getAbsolutePath());
+ }
+ if (destination.exists()) {
+ destination.delete();
+ }
+
+ int index = source.lastIndexOf('/');
+ String srcName = index == -1 ? source : source.substring(index + 1);
+ final File to = destDir == null ? Checks.checkAbsolute(srcName, "source") : new File(
+ destDir, srcName);
+
+ final IOException[] ex = new IOException[1];
+ Thread get = new IvyThread() {
+ public void run() {
+ initContext();
+ try {
+ sendCommand("get " + source, getExpectedDownloadMessage(source, to), 0);
+ } catch (IOException e) {
+ ex[0] = e;
+ }
+ }
+ };
+ get.start();
+
+ long prevLength = 0;
+ long lastUpdate = System.currentTimeMillis();
+ long timeout = readTimeout;
+ while (get.isAlive()) {
+ checkInterrupted();
+ long length = to.exists() ? to.length() : 0;
+ if (length > prevLength) {
+ fireTransferProgress(length - prevLength);
+ lastUpdate = System.currentTimeMillis();
+ prevLength = length;
+ } else {
+ if (System.currentTimeMillis() - lastUpdate > timeout) {
+ Message.verbose("download hang for more than " + timeout
+ + "ms. Interrupting.");
+ get.interrupt();
+ if (to.exists()) {
+ to.delete();
+ }
+ throw new IOException(source + " download timeout from " + getHost());
+ }
+ }
+ try {
+ get.join(GET_JOIN_MAX_TIME);
+ } catch (InterruptedException e) {
+ if (to.exists()) {
+ to.delete();
+ }
+ return;
+ }
+ }
+ if (ex[0] != null) {
+ if (to.exists()) {
+ to.delete();
+ }
+ throw ex[0];
+ }
+
+ to.renameTo(destination);
+ fireTransferCompleted(destination.length());
+ } catch (IOException ex) {
+ fireTransferError(ex);
+ cleanup(ex);
+ throw ex;
+ } finally {
+ cleanup();
+ }
+ }
+
+ public List list(String parent) throws IOException {
+ initIvy();
+ try {
+ if (!parent.endsWith("/")) {
+ parent = parent + "/";
+ }
+ String response = sendCommand("ls -l " + parent, true, true);
+ if (response.startsWith("ls")) {
+ return null;
+ }
+ String[] lines = response.split("\n");
+ List ret = new ArrayList(lines.length);
+ for (int i = 0; i < lines.length; i++) {
+ while (lines[i].endsWith("\r") || lines[i].endsWith("\n")) {
+ lines[i] = lines[i].substring(0, lines[i].length() - 1);
+ }
+ if (lines[i].trim().length() != 0) {
+ ret.add(parent + lines[i].substring(lines[i].lastIndexOf(' ') + 1));
+ }
+ }
+ return ret;
+ } catch (IOException ex) {
+ cleanup(ex);
+ throw ex;
+ } finally {
+ cleanup();
+ }
+ }
+
+ public void put(File source, String destination, boolean overwrite) throws IOException {
+ initIvy();
+ try {
+ if (getResource(destination).exists()) {
+ if (overwrite) {
+ sendCommand("rm " + destination, getExpectedRemoveMessage(destination));
+ } else {
+ return;
+ }
+ }
+ int index = destination.lastIndexOf('/');
+ String destDir = null;
+ if (index != -1) {
+ destDir = destination.substring(0, index);
+ mkdirs(destDir);
+ sendCommand("cd " + destDir);
+ }
+ String to = destDir != null ? destDir + "/" + source.getName() : source.getName();
+ sendCommand("put " + source.getAbsolutePath(), getExpectedUploadMessage(source, to), 0);
+ sendCommand("mv " + to + " " + destination);
+ } catch (IOException ex) {
+ cleanup(ex);
+ throw ex;
+ } finally {
+ cleanup();
+ }
+ }
+
+ private void mkdirs(String destDir) throws IOException {
+ if (dirExists(destDir)) {
+ return;
+ }
+ if (destDir.endsWith("/")) {
+ destDir = destDir.substring(0, destDir.length() - 1);
+ }
+ int index = destDir.lastIndexOf('/');
+ if (index != -1) {
+ mkdirs(destDir.substring(0, index));
+ }
+ sendCommand("mkdir " + destDir);
+ }
+
+ private boolean dirExists(String dir) throws IOException {
+ return !sendCommand("ls " + dir, true).startsWith("ls: ");
+ }
+
+ protected String sendCommand(String command) throws IOException {
+ return sendCommand(command, false, readTimeout);
+ }
+
+ protected void sendCommand(String command, Pattern expectedResponse) throws IOException {
+ sendCommand(command, expectedResponse, readTimeout);
+ }
+
+ /**
+ * The behaviour of vsftp with some commands is to log the resulting message on the error
+ * stream, even if everything is ok. So it's quite difficult if there was an error or not. Hence
+ * we compare the response with the expected message and deal with it. The problem is that this
+ * is very specific to the version of vsftp used for the test, That's why expected messages are
+ * obtained using overridable protected methods.
+ */
+ protected void sendCommand(String command, Pattern expectedResponse, long timeout)
+ throws IOException {
+ String response = sendCommand(command, true, timeout);
+ if (!expectedResponse.matcher(response).matches()) {
+ Message.debug("invalid response from server:");
+ Message.debug("expected: '" + expectedResponse + "'");
+ Message.debug("was: '" + response + "'");
+ throw new IOException(response);
+ }
+ }
+
+ protected String sendCommand(String command, boolean sendErrorAsResponse) throws IOException {
+ return sendCommand(command, sendErrorAsResponse, readTimeout);
+ }
+
+ protected String sendCommand(String command, boolean sendErrorAsResponse, boolean single)
+ throws IOException {
+ return sendCommand(command, sendErrorAsResponse, single, readTimeout);
+ }
+
+ protected String sendCommand(String command, boolean sendErrorAsResponse, long timeout)
+ throws IOException {
+ return sendCommand(command, sendErrorAsResponse, false, timeout);
+ }
+
+ protected String sendCommand(String command, boolean sendErrorAsResponse, boolean single,
+ long timeout) throws IOException {
+ single = false; // use of alone commands does not work properly due to a long delay between
+ // end of process and end of stream...
+
+ checkInterrupted();
+ inCommand = true;
+ errorsLastUpdateTime = 0;
+ synchronized (this) {
+ if (!single || in != null) {
+ ensureConnectionOpened();
+ Message.debug("sending command '" + command + "' to " + getHost());
+ updateLastCommandTime();
+ out.println(command);
+ out.flush();
+ } else {
+ sendSingleCommand(command);
+ }
+ }
+
+ try {
+ return readResponse(sendErrorAsResponse, timeout);
+ } finally {
+ inCommand = false;
+ if (single) {
+ closeConnection();
+ }
+ }
+ }
+
+ protected String readResponse(boolean sendErrorAsResponse) throws IOException {
+ return readResponse(sendErrorAsResponse, readTimeout);
+ }
+
+ protected synchronized String readResponse(final boolean sendErrorAsResponse, long timeout)
+ throws IOException {
+ final StringBuffer response = new StringBuffer();
+ final IOException[] exc = new IOException[1];
+ final boolean[] done = new boolean[1];
+ Runnable r = new Runnable() {
+ public void run() {
+ synchronized (VsftpRepository.this) {
+ try {
+ int c;
+ boolean getPrompt = false;
+ // the reading is done in a for loop making five attempts to read the stream
+ // if we do not reach the next prompt
+ for (int attempts = 0; !getPrompt && attempts < MAX_READ_PROMPT_ATTEMPT; attempts++) {
+ while ((c = in.read()) != -1) {
+ attempts = 0; // we manage to read something, reset numer of
+ // attempts
+ response.append((char) c);
+ if (response.length() >= PROMPT.length()
+ && response.substring(response.length() - PROMPT.length(),
+ response.length()).equals(PROMPT)) {
+ response.setLength(response.length() - PROMPT.length());
+ getPrompt = true;
+ break;
+ }
+ }
+ if (!getPrompt) {
+ try {
+ Thread.sleep(PROMPT_SLEEP_TIME);
+ } catch (InterruptedException e) {
+ break;
+ }
+ }
+ }
+ if (getPrompt) {
+ // wait enough for error stream to be fully read
+ if (errorsLastUpdateTime == 0) {
+ // no error written yet, but it may be pending...
+ errorsLastUpdateTime = lastCommand;
+ }
+
+ while ((System.currentTimeMillis() - errorsLastUpdateTime) < PROMPT_SLEEP_TIME) {
+ try {
+ Thread.sleep(ERROR_SLEEP_TIME);
+ } catch (InterruptedException e) {
+ break;
+ }
+ }
+ }
+ if (errors.length() > 0) {
+ if (sendErrorAsResponse) {
+ response.append(errors);
+ errors.setLength(0);
+ } else {
+ throw new IOException(chomp(errors).toString());
+ }
+ }
+ chomp(response);
+ done[0] = true;
+ } catch (IOException e) {
+ exc[0] = e;
+ } finally {
+ VsftpRepository.this.notify();
+ }
+ }
+ }
+ };
+ Thread reader = null;
+ if (timeout == 0) {
+ r.run();
+ } else {
+ reader = new IvyThread(r);
+ reader.start();
+ try {
+ wait(timeout);
+ } catch (InterruptedException e) {
+ // nothing to do
+ }
+ }
+ updateLastCommandTime();
+ if (exc[0] != null) {
+ throw exc[0];
+ } else if (!done[0]) {
+ if (reader != null && reader.isAlive()) {
+ reader.interrupt();
+ for (int i = 0; i < MAX_READER_ALIVE_ATTEMPT && reader.isAlive(); i++) {
+ try {
+ Thread.sleep(READER_ALIVE_SLEEP_TIME);
+ } catch (InterruptedException e) {
+ break;
+ }
+ }
+ if (reader.isAlive()) {
+ reader.stop(); // no way to interrupt it non abruptly
+ }
+ }
+ throw new IOException("connection timeout to " + getHost());
+ } else {
+ if ("Not connected.".equals(response.toString())) {
+ Message.info("vsftp connection to " + getHost() + " reset");
+ closeConnection();
+ throw new IOException("not connected to " + getHost());
+ }
+ Message.debug("received response '" + response + "' from " + getHost());
+ return response.toString();
+ }
+ }
+
+ private synchronized void sendSingleCommand(String command) throws IOException {
+ exec(getSingleCommand(command));
+ }
+
+ protected synchronized void ensureConnectionOpened() throws IOException {
+ if (in == null) {
+ Message.verbose("connecting to " + getUsername() + "@" + getHost() + "... ");
+ String connectionCommand = getConnectionCommand();
+ exec(connectionCommand);
+
+ try {
+ readResponse(false); // waits for first prompt
+
+ if (reuseConnection > 0) {
+ connectionCleaner = new IvyThread() {
+ public void run() {
+ initContext();
+ try {
+ long sleep = REUSE_CONNECTION_SLEEP_TIME;
+ while (in != null && sleep > 0) {
+ sleep(sleep);
+ sleep = reuseConnection
+ - (System.currentTimeMillis() - lastCommand);
+ if (inCommand) {
+ sleep = sleep <= 0 ? reuseConnection : sleep;
+ }
+ }
+ } catch (InterruptedException e) {
+ // nothing to do
+ }
+ disconnect();
+ }
+ };
+ connectionCleaner.start();
+ }
+
+ if (ivy != null) {
+ ivy.getEventManager().addIvyListener(new IvyListener() {
+ public void progress(IvyEvent event) {
+ disconnect();
+ event.getSource().removeIvyListener(this);
+ }
+ }, EndResolveEvent.NAME);
+ }
+
+ } catch (IOException ex) {
+ closeConnection();
+ throw new IOException("impossible to connect to " + getUsername() + "@" + getHost()
+ + " using " + getAuthentication() + ": " + ex.getMessage());
+ }
+ Message.verbose("connected to " + getHost());
+ }
+ }
+
+ private void updateLastCommandTime() {
+ lastCommand = System.currentTimeMillis();
+ }
+
+ private void exec(String command) throws IOException {
+ Message.debug("launching '" + command + "'");
+ process = Runtime.getRuntime().exec(command);
+ in = new InputStreamReader(process.getInputStream());
+ err = new InputStreamReader(process.getErrorStream());
+ out = new PrintWriter(process.getOutputStream());
+
+ errorsReader = new IvyThread() {
+ public void run() {
+ initContext();
+ int c;
+ try {
+ // CheckStyle:InnerAssignment OFF
+ while (err != null && (c = err.read()) != -1) {
+ errors.append((char) c);
+ errorsLastUpdateTime = System.currentTimeMillis();
+ }
+ // CheckStyle:InnerAssignment ON
+ } catch (IOException e) {
+ // nothing to do
+ }
+ }
+ };
+ errorsReader.start();
+ }
+
+ private void checkInterrupted() {
+ if (ivy != null) {
+ ivy.checkInterrupted();
+ }
+ }
+
+ /**
+ * Called whenever an api level method end
+ */
+ private void cleanup(Exception ex) {
+ if (ex.getMessage().equals("connection timeout to " + getHost())) {
+ closeConnection();
+ } else {
+ disconnect();
+ }
+ }
+
+ /**
+ * Called whenever an api level method end
+ */
+ private void cleanup() {
+ if (reuseConnection == 0) {
+ disconnect();
+ }
+ }
+
+ public synchronized void disconnect() {
+ if (in != null) {
+ Message.verbose("disconnecting from " + getHost() + "... ");
+ try {
+ sendCommand("exit", false, DISCONNECT_COMMAND_TIMEOUT);
+ } catch (IOException e) {
+ // nothing I can do
+ } finally {
+ closeConnection();
+ Message.verbose("disconnected of " + getHost());
+ }
+ }
+ }
+
+ private synchronized void closeConnection() {
+ if (connectionCleaner != null) {
+ connectionCleaner.interrupt();
+ }
+ if (errorsReader != null) {
+ errorsReader.interrupt();
+ }
+ try {
+ process.destroy();
+ } catch (Exception ex) {
+ // nothing I can do
+ }
+ try {
+ in.close();
+ } catch (Exception e) {
+ // nothing I can do
+ }
+ try {
+ err.close();
+ } catch (Exception e) {
+ // nothing I can do
+ }
+ try {
+ out.close();
+ } catch (Exception e) {
+ // nothing I can do
+ }
+
+ connectionCleaner = null;
+ errorsReader = null;
+ process = null;
+ in = null;
+ out = null;
+ err = null;
+ Message.debug("connection to " + getHost() + " closed");
+ }
+
+ /**
+ * Parses a ls -l line and transforms it in a resource
+ *
+ * @param file
+ * @param responseLine
+ * @return
+ */
+ protected Resource lslToResource(String file, String responseLine) {
+ if (responseLine == null || responseLine.startsWith("ls")) {
+ return new BasicResource(file, false, 0, 0, false);
+ } else {
+ String[] parts = responseLine.split("\\s+");
+ if (parts.length != LS_PARTS_NUMBER) {
+ Message.debug("unrecognized ls format: " + responseLine);
+ return new BasicResource(file, false, 0, 0, false);
+ } else {
+ try {
+ long contentLength = Long.parseLong(parts[LS_SIZE_INDEX]);
+ String date = parts[LS_DATE_INDEX1] + " " + parts[LS_DATE_INDEX2] + " "
+ + parts[LS_DATE_INDEX3] + " " + parts[LS_DATE_INDEX4];
+ return new BasicResource(file, true, contentLength, FORMAT.parse(date)
+ .getTime(), false);
+ } catch (Exception ex) {
+ Message.warn("impossible to parse server response: " + responseLine, ex);
+ return new BasicResource(file, false, 0, 0, false);
+ }
+ }
+ }
+ }
+
+ protected String getSingleCommand(String command) {
+ return "vsh -noprompt -auth " + authentication + " " + username + "@" + host + " "
+ + command;
+ }
+
+ protected String getConnectionCommand() {
+ return "vsftp -noprompt -auth " + authentication + " " + username + "@" + host;
+ }
+
+ protected Pattern getExpectedDownloadMessage(String source, File to) {
+ return Pattern.compile("Downloading " + to.getName() + " from [^\\s]+");
+ }
+
+ protected Pattern getExpectedRemoveMessage(String destination) {
+ return Pattern.compile("Removing [^\\s]+");
+ }
+
+ protected Pattern getExpectedUploadMessage(File source, String to) {
+ return Pattern.compile("Uploading " + source.getName() + " to [^\\s]+");
+ }
+
+ public String getAuthentication() {
+ return authentication;
+ }
+
+ public void setAuthentication(String authentication) {
+ this.authentication = authentication;
+ }
+
+ public String getHost() {
+ return host;
+ }
+
+ public void setHost(String host) {
+ this.host = host;
+ }
+
+ public String getUsername() {
+ return username;
+ }
+
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ private static StringBuffer chomp(StringBuffer str) {
+ if (str == null || str.length() == 0) {
+ return str;
+ }
+ while ("\n".equals(str.substring(str.length() - 1))
+ || "\r".equals(str.substring(str.length() - 1))) {
+ str.setLength(str.length() - 1);
+ }
+ return str;
+ }
+
+ public String toString() {
+ return getName() + " " + getUsername() + "@" + getHost() + " (" + getAuthentication() + ")";
+ }
+
+ /**
+ * Sets the reuse connection time. The same connection will be reused if the time here does not
+ * last between two commands. O indicates that the connection should never be reused
+ *
+ * @param time
+ */
+ public void setReuseConnection(long time) {
+ this.reuseConnection = time;
+ }
+
+ public long getReadTimeout() {
+ return readTimeout;
+ }
+
+ public void setReadTimeout(long readTimeout) {
+ this.readTimeout = readTimeout;
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/repository/vsftp/VsftpResource.java b/src/java/org/apache/ivy/plugins/repository/vsftp/VsftpResource.java
new file mode 100644
index 0000000..d28adb1
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/repository/vsftp/VsftpResource.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.repository.vsftp;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.ivy.plugins.repository.LazyResource;
+import org.apache.ivy.plugins.repository.Resource;
+import org.apache.ivy.util.Message;
+
+public class VsftpResource extends LazyResource {
+ private VsftpRepository repository;
+
+ public VsftpResource(VsftpRepository repository, String file) {
+ super(file);
+ this.repository = repository;
+ }
+
+ protected void init() {
+ try {
+ init(repository.getInitResource(getName()));
+ } catch (IOException e) {
+ Message.debug(e);
+ Message.verbose(e.toString());
+ }
+ }
+
+ public InputStream openStream() throws IOException {
+ throw new UnsupportedOperationException(
+ "vsftp resource does not support openStream operation");
+ }
+
+ public Resource clone(String cloneName) {
+ try {
+ return repository.getResource(cloneName);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+}
diff --git a/src/java/org/apache/ivy/plugins/resolver/AbstractPatternsBasedResolver.java b/src/java/org/apache/ivy/plugins/resolver/AbstractPatternsBasedResolver.java
new file mode 100644
index 0000000..a0f205b
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/resolver/AbstractPatternsBasedResolver.java
@@ -0,0 +1,366 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.resolver;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.apache.ivy.core.IvyPatternHelper;
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.DefaultArtifact;
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.resolve.ResolveData;
+import org.apache.ivy.core.settings.IvyPattern;
+import org.apache.ivy.plugins.matcher.Matcher;
+import org.apache.ivy.plugins.resolver.util.ResolvedResource;
+import org.apache.ivy.plugins.resolver.util.ResourceMDParser;
+import org.apache.ivy.util.Message;
+
+/**
+ *
+ */
+public abstract class AbstractPatternsBasedResolver extends BasicResolver {
+
+ private List ivyPatterns = new ArrayList(); // List (String pattern)
+
+ private List artifactPatterns = new ArrayList(); // List (String pattern)
+
+ private boolean m2compatible = false;
+
+ public AbstractPatternsBasedResolver() {
+ }
+
+ public ResolvedResource findIvyFileRef(DependencyDescriptor dd, ResolveData data) {
+ ModuleRevisionId mrid = dd.getDependencyRevisionId();
+ if (isM2compatible()) {
+ mrid = convertM2IdForResourceSearch(mrid);
+ }
+ return findResourceUsingPatterns(mrid, ivyPatterns,
+ DefaultArtifact.newIvyArtifact(mrid, data.getDate()), getRMDParser(dd, data),
+ data.getDate());
+ }
+
+ public ResolvedResource findArtifactRef(Artifact artifact, Date date) {
+ ModuleRevisionId mrid = artifact.getModuleRevisionId();
+ if (isM2compatible()) {
+ mrid = convertM2IdForResourceSearch(mrid);
+ }
+ return findResourceUsingPatterns(mrid, artifactPatterns, artifact,
+ getDefaultRMDParser(artifact.getModuleRevisionId().getModuleId()), date);
+ }
+
+ public ResolvedResource findResource(ResolvedResource[] rress, ResourceMDParser rmdparser,
+ ModuleRevisionId mrid, Date date) {
+ if (isM2compatible()) {
+ // convert 'M2'-organisation back to 'Ivy'-organisation
+ mrid = convertM2ResourceSearchIdToNormal(mrid);
+ }
+ return super.findResource(rress, rmdparser, mrid, date);
+ }
+
+ protected ResolvedResource findResourceUsingPatterns(ModuleRevisionId moduleRevision,
+ List patternList, Artifact artifact, ResourceMDParser rmdparser, Date date) {
+ List resolvedResources = new ArrayList();
+ Set foundRevisions = new HashSet();
+ boolean dynamic = getSettings().getVersionMatcher().isDynamic(moduleRevision);
+ boolean stop = false;
+ for (Iterator iter = patternList.iterator(); iter.hasNext() && !stop;) {
+ String pattern = (String) iter.next();
+ ResolvedResource rres = findResourceUsingPattern(moduleRevision, pattern, artifact,
+ rmdparser, date);
+ if ((rres != null) && !foundRevisions.contains(rres.getRevision())) {
+ // only add the first found ResolvedResource for each revision
+ foundRevisions.add(rres.getRevision());
+ resolvedResources.add(rres);
+ stop = !dynamic; // stop iterating if we are not searching a dynamic revision
+ }
+ }
+
+ if (resolvedResources.size() > 1) {
+ ResolvedResource[] rress = (ResolvedResource[]) resolvedResources
+ .toArray(new ResolvedResource[resolvedResources.size()]);
+ return findResource(rress, rmdparser, moduleRevision, date);
+ } else if (resolvedResources.size() == 1) {
+ return (ResolvedResource) resolvedResources.get(0);
+ } else {
+ return null;
+ }
+ }
+
+ protected abstract ResolvedResource findResourceUsingPattern(ModuleRevisionId mrid,
+ String pattern, Artifact artifact, ResourceMDParser rmdparser, Date date);
+
+ protected Collection findNames(Map tokenValues, String token) {
+ Collection names = new HashSet();
+ names.addAll(findIvyNames(tokenValues, token));
+ if (isAllownomd()) {
+ names.addAll(findArtifactNames(tokenValues, token));
+ }
+ return names;
+ }
+
+ protected Collection findIvyNames(Map tokenValues, String token) {
+ Collection names = new HashSet();
+ tokenValues = new HashMap(tokenValues);
+ tokenValues.put(IvyPatternHelper.ARTIFACT_KEY, "ivy");
+ tokenValues.put(IvyPatternHelper.TYPE_KEY, "ivy");
+ tokenValues.put(IvyPatternHelper.EXT_KEY, "xml");
+ if (isM2compatible()) {
+ convertM2TokenValuesForResourceSearch(tokenValues);
+ }
+ findTokenValues(names, getIvyPatterns(), tokenValues, token);
+ filterNames(names);
+ return names;
+ }
+
+ protected Collection findArtifactNames(Map tokenValues, String token) {
+ Collection names = new HashSet();
+ tokenValues = new HashMap(tokenValues);
+ tokenValues
+ .put(IvyPatternHelper.ARTIFACT_KEY, tokenValues.get(IvyPatternHelper.MODULE_KEY));
+ tokenValues.put(IvyPatternHelper.TYPE_KEY, "jar");
+ tokenValues.put(IvyPatternHelper.EXT_KEY, "jar");
+ if (isM2compatible()) {
+ convertM2TokenValuesForResourceSearch(tokenValues);
+ }
+ findTokenValues(names, getArtifactPatterns(), tokenValues, token);
+ filterNames(names);
+ return names;
+ }
+
+ public Map[] listTokenValues(String[] tokens, Map criteria) {
+ Set result = new LinkedHashSet();
+
+ // use ivy patterns
+ List ivyPatterns = getIvyPatterns();
+ Map tokenValues = new HashMap(criteria);
+ tokenValues.put(IvyPatternHelper.TYPE_KEY, "ivy");
+ tokenValues.put(IvyPatternHelper.EXT_KEY, getModuleDescriptorExtension());
+ if (isM2compatible()) {
+ convertM2TokenValuesForResourceSearch(tokenValues);
+ }
+ for (Iterator it = ivyPatterns.iterator(); it.hasNext();) {
+ String ivyPattern = (String) it.next();
+ result.addAll(resolveTokenValues(tokens, ivyPattern, tokenValues, false));
+ }
+
+ if (isAllownomd()) {
+ List artifactPatterns = getArtifactPatterns();
+ tokenValues = new HashMap(criteria);
+ tokenValues.put(IvyPatternHelper.TYPE_KEY, "jar");
+ tokenValues.put(IvyPatternHelper.EXT_KEY, "jar");
+ if (isM2compatible()) {
+ convertM2TokenValuesForResourceSearch(tokenValues);
+ }
+ for (Iterator it = artifactPatterns.iterator(); it.hasNext();) {
+ String artifactPattern = (String) it.next();
+ result.addAll(resolveTokenValues(tokens, artifactPattern, tokenValues, true));
+ }
+ }
+
+ return (Map[]) result.toArray(new Map[result.size()]);
+ }
+
+ protected String getModuleDescriptorExtension() {
+ return "xml";
+ }
+
+ private Set resolveTokenValues(String[] tokens, String pattern, Map criteria, boolean noMd) {
+ Set result = new LinkedHashSet();
+ Set tokenSet = new HashSet(Arrays.asList(tokens));
+
+ Map tokenValues = new HashMap();
+ for (Iterator it = criteria.entrySet().iterator(); it.hasNext();) {
+ Map.Entry entry = (Entry) it.next();
+ Object key = entry.getKey();
+ Object value = entry.getValue();
+ if (value instanceof String) {
+ tokenValues.put(key, value);
+ }
+ }
+
+ if (tokenSet.isEmpty()) {
+ // no more tokens to resolve
+ result.add(tokenValues);
+ return result;
+ }
+
+ String partiallyResolvedPattern = IvyPatternHelper.substituteTokens(pattern, tokenValues);
+ String token = IvyPatternHelper.getFirstToken(partiallyResolvedPattern);
+ if ((token == null) && exist(partiallyResolvedPattern)) {
+ // no more tokens to resolve
+ result.add(tokenValues);
+ return result;
+ }
+
+ tokenSet.remove(token);
+
+ Matcher matcher = null;
+ Object criteriaForToken = criteria.get(token);
+ if (criteriaForToken instanceof Matcher) {
+ matcher = (Matcher) criteriaForToken;
+ }
+
+ String[] values = listTokenValues(partiallyResolvedPattern, token);
+ if (values == null) {
+ return result;
+ }
+
+ List vals = new ArrayList(Arrays.asList(values));
+ filterNames(vals);
+
+ for (Iterator it = vals.iterator(); it.hasNext();) {
+ String value = (String) it.next();
+ if ((matcher != null) && !matcher.matches(value)) {
+ continue;
+ }
+
+ tokenValues.put(token, value);
+ String moreResolvedPattern = IvyPatternHelper.substituteTokens(
+ partiallyResolvedPattern, tokenValues);
+
+ Map newCriteria = new HashMap(criteria);
+ newCriteria.put(token, value);
+ if (noMd && "artifact".equals(token)) {
+ newCriteria.put("module", value);
+ } else if (noMd && "module".equals(token)) {
+ newCriteria.put("artifact", value);
+ }
+ result.addAll(resolveTokenValues(
+ (String[]) tokenSet.toArray(new String[tokenSet.size()]), moreResolvedPattern,
+ newCriteria, noMd));
+ }
+
+ return result;
+ }
+
+ protected abstract String[] listTokenValues(String pattern, String token);
+
+ protected abstract boolean exist(String path);
+
+ protected void findTokenValues(Collection names, List patterns, Map tokenValues, String token) {
+ // to be overridden by subclasses wanting to have listing features
+ }
+
+ /**
+ * example of pattern : ~/Workspace/[module]/[module].ivy.xml
+ *
+ * @param pattern
+ */
+ public void addIvyPattern(String pattern) {
+ ivyPatterns.add(pattern);
+ }
+
+ public void addArtifactPattern(String pattern) {
+ artifactPatterns.add(pattern);
+ }
+
+ public List getIvyPatterns() {
+ return Collections.unmodifiableList(ivyPatterns);
+ }
+
+ public List getArtifactPatterns() {
+ return Collections.unmodifiableList(artifactPatterns);
+ }
+
+ protected void setIvyPatterns(List patterns) {
+ ivyPatterns = patterns;
+ }
+
+ protected void setArtifactPatterns(List patterns) {
+ artifactPatterns = patterns;
+ }
+
+ /*
+ * Methods respecting ivy conf method specifications
+ */
+ public void addConfiguredIvy(IvyPattern p) {
+ ivyPatterns.add(p.getPattern());
+ }
+
+ public void addConfiguredArtifact(IvyPattern p) {
+ artifactPatterns.add(p.getPattern());
+ }
+
+ public void dumpSettings() {
+ super.dumpSettings();
+ Message.debug("\t\tm2compatible: " + isM2compatible());
+ Message.debug("\t\tivy patterns:");
+ for (ListIterator iter = getIvyPatterns().listIterator(); iter.hasNext();) {
+ String p = (String) iter.next();
+ Message.debug("\t\t\t" + p);
+ }
+ Message.debug("\t\tartifact patterns:");
+ for (ListIterator iter = getArtifactPatterns().listIterator(); iter.hasNext();) {
+ String p = (String) iter.next();
+ Message.debug("\t\t\t" + p);
+ }
+ }
+
+ public boolean isM2compatible() {
+ return m2compatible;
+ }
+
+ public void setM2compatible(boolean compatible) {
+ m2compatible = compatible;
+ }
+
+ protected ModuleRevisionId convertM2ResourceSearchIdToNormal(ModuleRevisionId mrid) {
+ if (mrid.getOrganisation() == null || mrid.getOrganisation().indexOf('/') == -1) {
+ return mrid;
+ }
+ return ModuleRevisionId.newInstance(mrid.getOrganisation().replace('/', '.'),
+ mrid.getName(), mrid.getBranch(), mrid.getRevision(),
+ mrid.getQualifiedExtraAttributes());
+ }
+
+ protected ModuleRevisionId convertM2IdForResourceSearch(ModuleRevisionId mrid) {
+ if (mrid.getOrganisation() == null || mrid.getOrganisation().indexOf('.') == -1) {
+ return mrid;
+ }
+ return ModuleRevisionId.newInstance(mrid.getOrganisation().replace('.', '/'),
+ mrid.getName(), mrid.getBranch(), mrid.getRevision(),
+ mrid.getQualifiedExtraAttributes());
+ }
+
+ protected String convertM2OrganizationForResourceSearch(String org) {
+ return org.replace('.', '/');
+ }
+
+ protected void convertM2TokenValuesForResourceSearch(Map tokenValues) {
+ if (tokenValues.get(IvyPatternHelper.ORGANISATION_KEY) instanceof String) {
+ tokenValues.put(IvyPatternHelper.ORGANISATION_KEY,
+ convertM2OrganizationForResourceSearch((String) tokenValues
+ .get(IvyPatternHelper.ORGANISATION_KEY)));
+ }
+ }
+
+}
diff --git a/src/java/org/apache/ivy/plugins/resolver/AbstractResolver.java b/src/java/org/apache/ivy/plugins/resolver/AbstractResolver.java
new file mode 100644
index 0000000..7f0d75f
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/resolver/AbstractResolver.java
@@ -0,0 +1,606 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.resolver;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.Map;
+
+import org.apache.ivy.core.IvyContext;
+import org.apache.ivy.core.LogOptions;
+import org.apache.ivy.core.RelativeUrlResolver;
+import org.apache.ivy.core.cache.ArtifactOrigin;
+import org.apache.ivy.core.cache.CacheDownloadOptions;
+import org.apache.ivy.core.cache.CacheMetadataOptions;
+import org.apache.ivy.core.cache.DownloadListener;
+import org.apache.ivy.core.cache.RepositoryCacheManager;
+import org.apache.ivy.core.cache.ResolutionCacheManager;
+import org.apache.ivy.core.event.EventManager;
+import org.apache.ivy.core.event.download.EndArtifactDownloadEvent;
+import org.apache.ivy.core.event.download.NeedArtifactEvent;
+import org.apache.ivy.core.event.download.StartArtifactDownloadEvent;
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleId;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.module.status.StatusManager;
+import org.apache.ivy.core.report.ArtifactDownloadReport;
+import org.apache.ivy.core.report.DownloadReport;
+import org.apache.ivy.core.report.DownloadStatus;
+import org.apache.ivy.core.report.MetadataArtifactDownloadReport;
+import org.apache.ivy.core.resolve.DownloadOptions;
+import org.apache.ivy.core.resolve.IvyNode;
+import org.apache.ivy.core.resolve.ResolveData;
+import org.apache.ivy.core.resolve.ResolveOptions;
+import org.apache.ivy.core.resolve.ResolvedModuleRevision;
+import org.apache.ivy.core.search.ModuleEntry;
+import org.apache.ivy.core.search.OrganisationEntry;
+import org.apache.ivy.core.search.RevisionEntry;
+import org.apache.ivy.core.settings.Validatable;
+import org.apache.ivy.plugins.conflict.ConflictManager;
+import org.apache.ivy.plugins.latest.ArtifactInfo;
+import org.apache.ivy.plugins.latest.LatestStrategy;
+import org.apache.ivy.plugins.matcher.PatternMatcher;
+import org.apache.ivy.plugins.namespace.NameSpaceHelper;
+import org.apache.ivy.plugins.namespace.Namespace;
+import org.apache.ivy.plugins.parser.ParserSettings;
+import org.apache.ivy.plugins.resolver.ChainResolver.ResolvedModuleRevisionArtifactInfo;
+import org.apache.ivy.plugins.resolver.util.HasLatestStrategy;
+import org.apache.ivy.plugins.resolver.util.ResolvedResource;
+import org.apache.ivy.util.Checks;
+import org.apache.ivy.util.Message;
+
+/**
+ * This abstract resolver only provides handling for resolver name
+ */
+public abstract class AbstractResolver implements DependencyResolver, HasLatestStrategy,
+ Validatable {
+
+ /**
+ * True if parsed ivy files should be validated against xsd, false if they should not, null if
+ * default behavior should be used
+ */
+ private Boolean validate = null;
+
+ private String name;
+
+ private ResolverSettings settings;
+
+ private EventManager eventManager = null; // may remain null
+
+ /**
+ * The latest strategy to use to find latest among several artifacts
+ */
+ private LatestStrategy latestStrategy;
+
+ private String latestStrategyName;
+
+ /**
+ * The namespace to which this resolver belongs
+ */
+ private Namespace namespace;
+
+ private String namespaceName;
+
+ private String cacheManagerName;
+
+ private RepositoryCacheManager repositoryCacheManager;
+
+ // used to store default values for nested cache
+ private String changingMatcherName;
+
+ private String changingPattern;
+
+ private Boolean checkmodified;
+
+ public ResolverSettings getSettings() {
+ return settings;
+ }
+
+ public ParserSettings getParserSettings() {
+ return new ResolverParserSettings();
+ }
+
+ public void setSettings(ResolverSettings ivy) {
+ settings = ivy;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ /**
+ * this method should remove sensitive information from a location to be displayed in a log
+ *
+ * @param name
+ * location
+ * @return location with sensitive data replaced by stars
+ */
+ public String hidePassword(String name) {
+ return name;
+ }
+
+ protected boolean doValidate(ResolveData data) {
+ if (validate != null) {
+ return validate.booleanValue();
+ } else {
+ return data.isValidate();
+ }
+ }
+
+ public boolean isValidate() {
+ return validate == null ? true : validate.booleanValue();
+ }
+
+ public void setValidate(boolean validate) {
+ this.validate = Boolean.valueOf(validate);
+ }
+
+ protected void checkInterrupted() {
+ IvyContext.getContext().getIvy().checkInterrupted();
+ }
+
+ public void reportFailure() {
+ Message.verbose("no failure report implemented by " + getName());
+ }
+
+ public void reportFailure(Artifact art) {
+ Message.verbose("no failure report implemented by " + getName());
+ }
+
+ public String[] listTokenValues(String token, Map otherTokenValues) {
+ return new String[0];
+ }
+
+ public Map[] listTokenValues(String[] tokens, Map criteria) {
+ return new Map[0];
+ }
+
+ public OrganisationEntry[] listOrganisations() {
+ return new OrganisationEntry[0];
+ }
+
+ public ModuleEntry[] listModules(OrganisationEntry org) {
+ return new ModuleEntry[0];
+ }
+
+ public RevisionEntry[] listRevisions(ModuleEntry module) {
+ return new RevisionEntry[0];
+ }
+
+ public String toString() {
+ return getName();
+ }
+
+ public void dumpSettings() {
+ Message.verbose("\t" + getName() + " [" + getTypeName() + "]");
+ Message.debug("\t\tcache: " + cacheManagerName);
+ }
+
+ public String getTypeName() {
+ return getClass().getName();
+ }
+
+ /**
+ * Default implementation downloads the artifact without taking advantage of its location
+ */
+ public ArtifactDownloadReport download(ArtifactOrigin artifact, DownloadOptions options) {
+ DownloadReport r = download(new Artifact[] {artifact.getArtifact()}, options);
+ return r.getArtifactReport(artifact.getArtifact());
+ }
+
+ public boolean exists(Artifact artifact) {
+ return locate(artifact) != null;
+ }
+
+ /**
+ * Default implementation actually download the artifact Subclasses should overwrite this to
+ * avoid the download
+ */
+ public ArtifactOrigin locate(Artifact artifact) {
+ DownloadReport dr = download(new Artifact[] {artifact}, new DownloadOptions());
+ if (dr == null) {
+ /*
+ * according to IVY-831, it seems that this actually happen sometime, while the contract
+ * of DependencyResolver says that it should never return null
+ */
+ throw new IllegalStateException("null download report returned by " + getName() + " ("
+ + getClass().getName() + ")" + " when trying to download " + artifact);
+ }
+ ArtifactDownloadReport adr = dr.getArtifactReport(artifact);
+ return adr.getDownloadStatus() == DownloadStatus.FAILED ? null : adr.getArtifactOrigin();
+ }
+
+ public LatestStrategy getLatestStrategy() {
+ if (latestStrategy == null) {
+ initLatestStrategyFromSettings();
+ }
+ return latestStrategy;
+ }
+
+ private void initLatestStrategyFromSettings() {
+ if (getSettings() != null) {
+ if (latestStrategyName != null && !"default".equals(latestStrategyName)) {
+ latestStrategy = getSettings().getLatestStrategy(latestStrategyName);
+ if (latestStrategy == null) {
+ throw new IllegalStateException("unknown latest strategy '"
+ + latestStrategyName + "'");
+ }
+ } else {
+ latestStrategy = getSettings().getDefaultLatestStrategy();
+ Message.debug(getName() + ": no latest strategy defined: using default");
+ }
+ } else {
+ throw new IllegalStateException("no ivy instance found: "
+ + "impossible to get a latest strategy without ivy instance");
+ }
+ }
+
+ public void setLatestStrategy(LatestStrategy latestStrategy) {
+ this.latestStrategy = latestStrategy;
+ }
+
+ public void setLatest(String strategyName) {
+ latestStrategyName = strategyName;
+ }
+
+ public String getLatest() {
+ if (latestStrategyName == null) {
+ latestStrategyName = "default";
+ }
+ return latestStrategyName;
+ }
+
+ public Namespace getNamespace() {
+ if (namespace == null) {
+ initNamespaceFromSettings();
+ }
+ return namespace;
+ }
+
+ private void initNamespaceFromSettings() {
+ if (getSettings() != null) {
+ if (namespaceName != null) {
+ namespace = getSettings().getNamespace(namespaceName);
+ if (namespace == null) {
+ throw new IllegalStateException("unknown namespace '" + namespaceName + "'");
+ }
+ } else {
+ namespace = getSettings().getSystemNamespace();
+ Message.debug(getName() + ": no namespace defined: using system");
+ }
+ } else {
+ Message.verbose(getName()
+ + ": no namespace defined nor ivy instance: using system namespace");
+ namespace = Namespace.SYSTEM_NAMESPACE;
+ }
+ }
+
+ public void setNamespace(String namespaceName) {
+ this.namespaceName = namespaceName;
+ }
+
+ // Namespace conversion methods
+ protected ModuleDescriptor toSystem(ModuleDescriptor md) {
+ return NameSpaceHelper.toSystem(md, getNamespace());
+ }
+
+ protected Artifact fromSystem(Artifact artifact) {
+ return NameSpaceHelper.transform(artifact, getNamespace().getFromSystemTransformer());
+ }
+
+ protected Artifact toSystem(Artifact artifact) {
+ return NameSpaceHelper.transform(artifact, getNamespace().getToSystemTransformer());
+ }
+
+ protected MetadataArtifactDownloadReport toSystem(MetadataArtifactDownloadReport report) {
+ return NameSpaceHelper.transform(report, getNamespace().getToSystemTransformer());
+ }
+
+ protected ResolvedModuleRevision toSystem(ResolvedModuleRevision rmr) {
+ return NameSpaceHelper.toSystem(rmr, getNamespace());
+ }
+
+ protected ModuleRevisionId toSystem(ModuleRevisionId resolvedMrid) {
+ return getNamespace().getToSystemTransformer().transform(resolvedMrid);
+ }
+
+ protected DependencyDescriptor fromSystem(DependencyDescriptor dd) {
+ return NameSpaceHelper.transform(dd, getNamespace().getFromSystemTransformer(), true);
+ }
+
+ protected DependencyDescriptor toSystem(DependencyDescriptor dd) {
+ return NameSpaceHelper.transform(dd, getNamespace().getToSystemTransformer(), true);
+ }
+
+ protected IvyNode getSystemNode(ResolveData data, ModuleRevisionId resolvedMrid) {
+ return data.getNode(toSystem(resolvedMrid));
+ }
+
+ protected ResolvedModuleRevision findModuleInCache(DependencyDescriptor dd, ResolveData data) {
+ return findModuleInCache(dd, data, false);
+ }
+
+ protected ResolvedModuleRevision findModuleInCache(DependencyDescriptor dd, ResolveData data,
+ boolean anyResolver) {
+ ResolvedModuleRevision rmr = getRepositoryCacheManager().findModuleInCache(dd,
+ dd.getDependencyRevisionId(), getCacheOptions(data), anyResolver ? null : getName());
+ if (rmr == null) {
+ return null;
+ }
+ if (data.getReport() != null
+ && data.isBlacklisted(data.getReport().getConfiguration(), rmr.getId())) {
+ Message.verbose("\t" + getName() + ": found revision in cache: " + rmr.getId()
+ + " for " + dd + ", but it is blacklisted");
+ return null;
+ }
+ return rmr;
+ }
+
+ public void setChangingMatcher(String changingMatcherName) {
+ this.changingMatcherName = changingMatcherName;
+ }
+
+ protected String getChangingMatcherName() {
+ return changingMatcherName;
+ }
+
+ public void setChangingPattern(String changingPattern) {
+ this.changingPattern = changingPattern;
+ }
+
+ protected String getChangingPattern() {
+ return changingPattern;
+ }
+
+ public void setCheckmodified(boolean check) {
+ checkmodified = Boolean.valueOf(check);
+ }
+
+ public RepositoryCacheManager getRepositoryCacheManager() {
+ if (repositoryCacheManager == null) {
+ initRepositoryCacheManagerFromSettings();
+ }
+ return repositoryCacheManager;
+ }
+
+ private void initRepositoryCacheManagerFromSettings() {
+ if (cacheManagerName == null) {
+ repositoryCacheManager = settings.getDefaultRepositoryCacheManager();
+ if (repositoryCacheManager == null) {
+ throw new IllegalStateException(
+ "no default cache manager defined with current settings");
+ }
+ } else {
+ repositoryCacheManager = settings.getRepositoryCacheManager(cacheManagerName);
+ if (repositoryCacheManager == null) {
+ throw new IllegalStateException("unknown cache manager '" + cacheManagerName
+ + "'. Available caches are "
+ + Arrays.asList(settings.getRepositoryCacheManagers()));
+ }
+ }
+ }
+
+ public void setRepositoryCacheManager(RepositoryCacheManager repositoryCacheManager) {
+ this.cacheManagerName = repositoryCacheManager.getName();
+ this.repositoryCacheManager = repositoryCacheManager;
+ }
+
+ public void setCache(String cacheName) {
+ cacheManagerName = cacheName;
+ }
+
+ public void setEventManager(EventManager eventManager) {
+ this.eventManager = eventManager;
+ }
+
+ public EventManager getEventManager() {
+ return eventManager;
+ }
+
+ public void validate() {
+ initRepositoryCacheManagerFromSettings();
+ initNamespaceFromSettings();
+ initLatestStrategyFromSettings();
+ }
+
+ protected CacheMetadataOptions getCacheOptions(ResolveData data) {
+ return (CacheMetadataOptions) new CacheMetadataOptions()
+ .setChangingMatcherName(getChangingMatcherName())
+ .setChangingPattern(getChangingPattern())
+ .setCheckTTL(!data.getOptions().isUseCacheOnly())
+ .setCheckmodified(
+ data.getOptions().isUseCacheOnly() ? Boolean.FALSE : checkmodified)
+ .setValidate(doValidate(data)).setNamespace(getNamespace())
+ .setForce(data.getOptions().isRefresh())
+ .setListener(getDownloadListener(getDownloadOptions(data.getOptions())));
+ }
+
+ protected CacheDownloadOptions getCacheDownloadOptions(DownloadOptions options) {
+ CacheDownloadOptions cacheDownloadOptions = new CacheDownloadOptions();
+ cacheDownloadOptions.setListener(getDownloadListener(options));
+ return cacheDownloadOptions;
+ }
+
+ protected DownloadOptions getDownloadOptions(ResolveOptions options) {
+ return (DownloadOptions) new DownloadOptions().setLog(options.getLog());
+ }
+
+ public void abortPublishTransaction() throws IOException {
+ /* Default implementation is a no-op */
+ }
+
+ public void commitPublishTransaction() throws IOException {
+ /* Default implementation is a no-op */
+ }
+
+ public void beginPublishTransaction(ModuleRevisionId module, boolean overwrite)
+ throws IOException {
+ /* Default implementation is a no-op */
+ }
+
+ private DownloadListener getDownloadListener(final DownloadOptions options) {
+ return new DownloadListener() {
+ public void needArtifact(RepositoryCacheManager cache, Artifact artifact) {
+ if (eventManager != null) {
+ eventManager
+ .fireIvyEvent(new NeedArtifactEvent(AbstractResolver.this, artifact));
+ }
+ }
+
+ public void startArtifactDownload(RepositoryCacheManager cache, ResolvedResource rres,
+ Artifact artifact, ArtifactOrigin origin) {
+ if (artifact.isMetadata() || LogOptions.LOG_QUIET.equals(options.getLog())) {
+ Message.verbose("downloading " + rres.getResource() + " ...");
+ } else {
+ Message.info("downloading " + rres.getResource() + " ...");
+ }
+ if (eventManager != null) {
+ eventManager.fireIvyEvent(new StartArtifactDownloadEvent(AbstractResolver.this,
+ artifact, origin));
+ }
+ }
+
+ public void endArtifactDownload(RepositoryCacheManager cache, Artifact artifact,
+ ArtifactDownloadReport adr, File archiveFile) {
+ if (eventManager != null) {
+ eventManager.fireIvyEvent(new EndArtifactDownloadEvent(AbstractResolver.this,
+ artifact, adr, archiveFile));
+ }
+ }
+ };
+ }
+
+ /**
+ * Returns true if rmr1 is after rmr2, using the latest strategy to determine which is the
+ * latest
+ *
+ * @param rmr1
+ * @param rmr2
+ * @return
+ */
+ protected boolean isAfter(ResolvedModuleRevision rmr1, ResolvedModuleRevision rmr2, Date date) {
+ ArtifactInfo[] ais = new ArtifactInfo[] {new ResolvedModuleRevisionArtifactInfo(rmr1),
+ new ResolvedModuleRevisionArtifactInfo(rmr2)};
+ return getLatestStrategy().findLatest(ais, date) == ais[0];
+ }
+
+ protected ResolvedModuleRevision checkLatest(DependencyDescriptor dd,
+ ResolvedModuleRevision newModuleFound, ResolveData data) {
+ Checks.checkNotNull(dd, "dd");
+ Checks.checkNotNull(data, "data");
+
+ // check if latest is asked and compare to return the most recent
+ ResolvedModuleRevision previousModuleFound = data.getCurrentResolvedModuleRevision();
+ String newModuleDesc = describe(newModuleFound);
+ Message.debug("\tchecking " + newModuleDesc + " against " + describe(previousModuleFound));
+ if (previousModuleFound == null) {
+ Message.debug("\tmodule revision kept as first found: " + newModuleDesc);
+ saveModuleRevisionIfNeeded(dd, newModuleFound);
+ return newModuleFound;
+ } else if (isAfter(newModuleFound, previousModuleFound, data.getDate())) {
+ Message.debug("\tmodule revision kept as younger: " + newModuleDesc);
+ saveModuleRevisionIfNeeded(dd, newModuleFound);
+ return newModuleFound;
+ } else if (!newModuleFound.getDescriptor().isDefault()
+ && previousModuleFound.getDescriptor().isDefault()) {
+ Message.debug("\tmodule revision kept as better (not default): " + newModuleDesc);
+ saveModuleRevisionIfNeeded(dd, newModuleFound);
+ return newModuleFound;
+ } else {
+ Message.debug("\tmodule revision discarded as older: " + newModuleDesc);
+ return previousModuleFound;
+ }
+ }
+
+ protected void saveModuleRevisionIfNeeded(DependencyDescriptor dd,
+ ResolvedModuleRevision newModuleFound) {
+ if (newModuleFound != null
+ && getSettings().getVersionMatcher().isDynamic(dd.getDependencyRevisionId())) {
+ getRepositoryCacheManager().saveResolvedRevision(dd.getDependencyRevisionId(),
+ newModuleFound.getId().getRevision());
+ }
+ }
+
+ private String describe(ResolvedModuleRevision rmr) {
+ if (rmr == null) {
+ return "[none]";
+ }
+ return rmr.getId() + (rmr.getDescriptor().isDefault() ? "[default]" : "") + " from "
+ + rmr.getResolver().getName();
+ }
+
+ private class ResolverParserSettings implements ParserSettings {
+
+ public ConflictManager getConflictManager(String name) {
+ return AbstractResolver.this.getSettings().getConflictManager(name);
+ }
+
+ public Namespace getContextNamespace() {
+ return AbstractResolver.this.getNamespace();
+ }
+
+ public String getDefaultBranch(ModuleId moduleId) {
+ return AbstractResolver.this.getSettings().getDefaultBranch(moduleId);
+ }
+
+ public PatternMatcher getMatcher(String matcherName) {
+ return AbstractResolver.this.getSettings().getMatcher(matcherName);
+ }
+
+ public Namespace getNamespace(String namespace) {
+ return AbstractResolver.this.getSettings().getNamespace(namespace);
+ }
+
+ public RelativeUrlResolver getRelativeUrlResolver() {
+ return AbstractResolver.this.getSettings().getRelativeUrlResolver();
+ }
+
+ public ResolutionCacheManager getResolutionCacheManager() {
+ return AbstractResolver.this.getSettings().getResolutionCacheManager();
+ }
+
+ public DependencyResolver getResolver(ModuleRevisionId mRevId) {
+ return AbstractResolver.this.getSettings().getResolver(mRevId);
+ }
+
+ public StatusManager getStatusManager() {
+ return AbstractResolver.this.getSettings().getStatusManager();
+ }
+
+ public File resolveFile(String filename) {
+ return AbstractResolver.this.getSettings().resolveFile(filename);
+ }
+
+ public Map substitute(Map strings) {
+ return AbstractResolver.this.getSettings().substitute(strings);
+ }
+
+ public String substitute(String value) {
+ return AbstractResolver.this.getSettings().substitute(value);
+ }
+
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/resolver/AbstractSshBasedResolver.java b/src/java/org/apache/ivy/plugins/resolver/AbstractSshBasedResolver.java
new file mode 100644
index 0000000..f85ce86
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/resolver/AbstractSshBasedResolver.java
@@ -0,0 +1,147 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.resolver;
+
+import java.io.File;
+
+import org.apache.ivy.core.settings.IvySettings;
+import org.apache.ivy.plugins.repository.ssh.AbstractSshBasedRepository;
+
+/**
+ * Abstract base class for all resolvers using SSH All necessary connection parameters can be set
+ * here via attributes. However all attributes defined in the pattern url of the resolver will have
+ * higher priority and will overwrite the values given here. To specify connection parameters in the
+ * pattern, you have to specify a full url and not just a path as pattern. e.g.
+ * pattern="/path/to/my/repos/[artifact].[ext]" will use all connection parameters from this class
+ * e.g. pattern="ssh://myserver.com/path/to/my/repos/[artifact].[ext]" will use all parameters from
+ * this class with the exception of the host, which will be "myserver.com" e.g.
+ * pattern="sftp://user:geheim@myserver.com:8022/path/to/my/repos/[artifact].[ext]" will use only
+ * the keyFile and keyFilePassword from this class (if needed). Rest will come from the url.
+ */
+public abstract class AbstractSshBasedResolver extends RepositoryResolver {
+
+ private boolean passfileSet = false;
+
+ public AbstractSshBasedResolver() {
+ super();
+ }
+
+ private AbstractSshBasedRepository getSshBasedRepository() {
+ return ((AbstractSshBasedRepository) getRepository());
+ }
+
+ /**
+ * Sets the location of the Public Key file to use for authentication
+ *
+ * @param filePath
+ * full file path name
+ */
+ public void setKeyFile(File filePath) {
+ getSshBasedRepository().setKeyFile(filePath);
+ }
+
+ /**
+ * Determines whether a local SSH agent may be used for authentication
+ *
+ * @param allowedAgentUse
+ * true if an agent may be used if available
+ */
+ public void setAllowedAgentUse(boolean allowedAgentUse) {
+ getSshBasedRepository().setAllowedAgentUse(allowedAgentUse);
+ }
+
+ /**
+ * Optional password file. If set the repository will use it as an encypted property file, to
+ * load username and passwd entries, and to store them if the user choose to do so. Defaults to
+ * user.dir/.ivy/[host].sftp.passwd, set it to null to disable this feature.
+ */
+ public void setPassfile(File passfile) {
+ getSshBasedRepository().setPassFile(passfile);
+ passfileSet = true;
+ }
+
+ public void setSettings(IvySettings settings) {
+ super.setSettings(settings);
+ if (!passfileSet) {
+ getSshBasedRepository().setPassFile(
+ new File(settings.getDefaultIvyUserDir(), getSshBasedRepository().getHost()
+ + ".ssh.passwd"));
+ }
+ }
+
+ /**
+ * Sets the password to authenticate the user if password based login is used if no password is
+ * set and password based login is used, user will be prompted for it the password can also be
+ * set by using a full url for the pattern, like
+ * "sftp://user:password@myserver.com/path/to/repos/[artifact].[ext]"
+ *
+ * @param password
+ * to use
+ */
+ public void setUserPassword(String password) {
+ getSshBasedRepository().setUserPassword(password);
+ }
+
+ /**
+ * Sets the password to use for decrypting key file (if it is encrypted) if no password is set
+ * and the keyfile is encrypted, the user will be prompted for the password if the keyfile is
+ * passwordless, this parameter will be ignored if given
+ *
+ * @param password
+ * to use
+ */
+ public void setKeyFilePassword(String password) {
+ getSshBasedRepository().setKeyFilePassword(password);
+ }
+
+ /**
+ * sets the user to use for the ssh communication the user can also be set by using a full url
+ * for the pattern, like "ssh://user@myserver.com/path/to/repos/[artifact].[ext]"
+ *
+ * @param user
+ * on the target system
+ */
+ public void setUser(String user) {
+ getSshBasedRepository().setUser(user);
+ }
+
+ /**
+ * sets the host to use for the ssh communication the host can also be set by using a full url
+ * for the pattern, like "ssh://myserver.com/path/to/repos/[artifact].[ext]"
+ *
+ * @param host
+ * of the target system
+ */
+ public void setHost(String host) {
+ getSshBasedRepository().setHost(host);
+ }
+
+ /**
+ * sets the port to use for the ssh communication port 22 is default the port can also be set by
+ * using a full url for the pattern, like
+ * "sftp://myserver.com:8022/path/to/repos/[artifact].[ext]"
+ *
+ * @param port
+ * of the target system
+ */
+ public void setPort(int port) {
+ getSshBasedRepository().setPort(port);
+ }
+
+ public abstract String getTypeName();
+}
diff --git a/src/java/org/apache/ivy/plugins/resolver/BasicResolver.java b/src/java/org/apache/ivy/plugins/resolver/BasicResolver.java
new file mode 100644
index 0000000..ce1309d
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/resolver/BasicResolver.java
@@ -0,0 +1,1164 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.resolver;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.ivy.core.IvyContext;
+import org.apache.ivy.core.IvyPatternHelper;
+import org.apache.ivy.core.LogOptions;
+import org.apache.ivy.core.cache.ArtifactOrigin;
+import org.apache.ivy.core.cache.ModuleDescriptorWriter;
+import org.apache.ivy.core.cache.RepositoryCacheManager;
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleId;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.report.ArtifactDownloadReport;
+import org.apache.ivy.core.report.DownloadReport;
+import org.apache.ivy.core.report.DownloadStatus;
+import org.apache.ivy.core.report.MetadataArtifactDownloadReport;
+import org.apache.ivy.core.resolve.DownloadOptions;
+import org.apache.ivy.core.resolve.IvyNode;
+import org.apache.ivy.core.resolve.ResolveData;
+import org.apache.ivy.core.resolve.ResolvedModuleRevision;
+import org.apache.ivy.core.search.ModuleEntry;
+import org.apache.ivy.core.search.OrganisationEntry;
+import org.apache.ivy.core.search.RevisionEntry;
+import org.apache.ivy.plugins.conflict.ConflictManager;
+import org.apache.ivy.plugins.namespace.Namespace;
+import org.apache.ivy.plugins.parser.ModuleDescriptorParser;
+import org.apache.ivy.plugins.parser.ModuleDescriptorParserRegistry;
+import org.apache.ivy.plugins.parser.xml.XmlModuleDescriptorWriter;
+import org.apache.ivy.plugins.repository.ArtifactResourceResolver;
+import org.apache.ivy.plugins.repository.Resource;
+import org.apache.ivy.plugins.repository.ResourceDownloader;
+import org.apache.ivy.plugins.repository.file.FileRepository;
+import org.apache.ivy.plugins.repository.file.FileResource;
+import org.apache.ivy.plugins.repository.url.URLRepository;
+import org.apache.ivy.plugins.repository.url.URLResource;
+import org.apache.ivy.plugins.resolver.util.MDResolvedResource;
+import org.apache.ivy.plugins.resolver.util.ResolvedResource;
+import org.apache.ivy.plugins.resolver.util.ResourceMDParser;
+import org.apache.ivy.plugins.version.VersionMatcher;
+import org.apache.ivy.util.Checks;
+import org.apache.ivy.util.ChecksumHelper;
+import org.apache.ivy.util.HostUtil;
+import org.apache.ivy.util.Message;
+
+/**
+ *
+ */
+public abstract class BasicResolver extends AbstractResolver {
+ public static final String DESCRIPTOR_OPTIONAL = "optional";
+
+ public static final String DESCRIPTOR_REQUIRED = "required";
+
+ /**
+ * Exception thrown internally in getDependency to indicate a dependency is unresolved.
+ * <p>
+ * Due to the contract of getDependency, this exception is never thrown publicly, but rather
+ * converted in a message (either error or verbose) and returning null
+ * </p>
+ */
+ private static class UnresolvedDependencyException extends RuntimeException {
+ private boolean error;
+
+ /**
+ * Dependency has not been resolved. This is not an error and won't log any message.
+ */
+ public UnresolvedDependencyException() {
+ this("", false);
+ }
+
+ /**
+ * Dependency has not been resolved. This is an error and will log a message.
+ */
+ public UnresolvedDependencyException(String message) {
+ this(message, true);
+ }
+
+ /**
+ * Dependency has not been resolved. The boolean tells if it is an error or not, a message
+ * will be logged if non empty.
+ */
+ public UnresolvedDependencyException(String message, boolean error) {
+ super(message);
+ this.error = error;
+ }
+
+ public boolean isError() {
+ return error;
+ }
+ }
+
+ public static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyyMMddHHmmss");
+
+ private String workspaceName;
+
+ /**
+ * True if the files resolved are dependent of the environment from which they have been
+ * resolved, false otherwise. In general, relative paths are dependent of the environment, and
+ * absolute paths including machine reference are not.
+ */
+ private boolean envDependent = true;
+
+ private List ivyattempts = new ArrayList();
+
+ private Map artattempts = new HashMap();
+
+ private boolean checkconsistency = true;
+
+ private boolean allownomd = true;
+
+ private boolean force = false;
+
+ private String checksums = null;
+
+ private URLRepository extartifactrep = new URLRepository(); // used only to download
+
+ // external artifacts
+
+ public BasicResolver() {
+ workspaceName = HostUtil.getLocalHostName();
+ }
+
+ public String getWorkspaceName() {
+ return workspaceName;
+ }
+
+ public void setWorkspaceName(String workspaceName) {
+ this.workspaceName = workspaceName;
+ }
+
+ public boolean isEnvDependent() {
+ return envDependent;
+ }
+
+ public void setEnvDependent(boolean envDependent) {
+ this.envDependent = envDependent;
+ }
+
+ public ResolvedModuleRevision getDependency(DependencyDescriptor dd, ResolveData data)
+ throws ParseException {
+ IvyContext context = IvyContext.pushNewCopyContext();
+ try {
+ ResolvedModuleRevision mr = data.getCurrentResolvedModuleRevision();
+ if (mr != null) {
+ if (shouldReturnResolvedModule(dd, mr)) {
+ return mr;
+ }
+ }
+
+ if (isForce()) {
+ dd = dd.clone(ModuleRevisionId.newInstance(dd.getDependencyRevisionId(),
+ "latest.integration"));
+ }
+ DependencyDescriptor systemDd = dd;
+ DependencyDescriptor nsDd = fromSystem(dd);
+ context.setDependencyDescriptor(systemDd);
+ context.setResolveData(data);
+
+ clearIvyAttempts();
+ clearArtifactAttempts();
+ ModuleRevisionId systemMrid = systemDd.getDependencyRevisionId();
+ ModuleRevisionId nsMrid = nsDd.getDependencyRevisionId();
+
+ checkRevision(systemMrid);
+
+ boolean isDynamic = getAndCheckIsDynamic(systemMrid);
+
+ // we first search for the dependency in cache
+ ResolvedModuleRevision rmr = null;
+ rmr = findModuleInCache(systemDd, data);
+ if (rmr != null) {
+ if (rmr.getDescriptor().isDefault() && rmr.getResolver() != this) {
+ Message.verbose("\t" + getName() + ": found revision in cache: " + systemMrid
+ + " (resolved by " + rmr.getResolver().getName()
+ + "): but it's a default one, maybe we can find a better one");
+ } else if (isForce() && rmr.getResolver() != this) {
+ Message.verbose("\t" + getName() + ": found revision in cache: " + systemMrid
+ + " (resolved by " + rmr.getResolver().getName()
+ + "): but we are in force mode, let's try to find one ourself");
+ } else {
+ Message.verbose("\t" + getName() + ": revision in cache: " + systemMrid);
+ return checkLatest(systemDd, checkForcedResolvedModuleRevision(rmr), data);
+ }
+ }
+ if (data.getOptions().isUseCacheOnly()) {
+ throw new UnresolvedDependencyException("\t" + getName()
+ + " (useCacheOnly) : no ivy file found for " + systemMrid, false);
+ }
+
+ checkInterrupted();
+
+ ResolvedResource ivyRef = findIvyFileRef(nsDd, data);
+ checkInterrupted();
+
+ // get module descriptor
+ ModuleDescriptor nsMd;
+ ModuleDescriptor systemMd = null;
+ if (ivyRef == null) {
+ if (!isAllownomd()) {
+ throw new UnresolvedDependencyException("\t" + getName()
+ + ": no ivy file found for " + systemMrid, false);
+ }
+ nsMd = DefaultModuleDescriptor.newDefaultInstance(nsMrid,
+ nsDd.getAllDependencyArtifacts());
+ ResolvedResource artifactRef = findFirstArtifactRef(nsMd, nsDd, data);
+ checkInterrupted();
+ if (artifactRef == null) {
+ throw new UnresolvedDependencyException("\t" + getName()
+ + ": no ivy file nor artifact found for " + systemMrid, false);
+ } else {
+ long lastModified = artifactRef.getLastModified();
+ if (lastModified != 0 && nsMd instanceof DefaultModuleDescriptor) {
+ ((DefaultModuleDescriptor) nsMd).setLastModified(lastModified);
+ }
+ Message.verbose("\t" + getName() + ": no ivy file found for " + systemMrid
+ + ": using default data");
+ if (isDynamic) {
+ nsMd.setResolvedModuleRevisionId(ModuleRevisionId.newInstance(nsMrid,
+ artifactRef.getRevision()));
+ }
+ systemMd = toSystem(nsMd);
+ MetadataArtifactDownloadReport madr = new MetadataArtifactDownloadReport(
+ systemMd.getMetadataArtifact());
+ madr.setDownloadStatus(DownloadStatus.NO);
+ madr.setSearched(true);
+ rmr = new ResolvedModuleRevision(this, this, systemMd, madr, isForce());
+ getRepositoryCacheManager().cacheModuleDescriptor(this, artifactRef,
+ toSystem(dd), systemMd.getAllArtifacts()[0], null, getCacheOptions(data));
+ }
+ } else {
+ if (ivyRef instanceof MDResolvedResource) {
+ rmr = ((MDResolvedResource) ivyRef).getResolvedModuleRevision();
+ }
+ if (rmr == null) {
+ rmr = parse(ivyRef, systemDd, data);
+ if (rmr == null) {
+ throw new UnresolvedDependencyException();
+ }
+ }
+ if (!rmr.getReport().isDownloaded() && rmr.getReport().getLocalFile() != null) {
+ return checkLatest(systemDd, checkForcedResolvedModuleRevision(rmr), data);
+ } else {
+ nsMd = rmr.getDescriptor();
+
+ // check descriptor data is in sync with resource revision and names
+ systemMd = toSystem(nsMd);
+ if (isCheckconsistency()) {
+ checkDescriptorConsistency(systemMrid, systemMd, ivyRef);
+ checkDescriptorConsistency(nsMrid, nsMd, ivyRef);
+ } else {
+ if (systemMd instanceof DefaultModuleDescriptor) {
+ DefaultModuleDescriptor defaultMd = (DefaultModuleDescriptor) systemMd;
+ ModuleRevisionId revision = getRevision(ivyRef, systemMrid, systemMd);
+ defaultMd.setModuleRevisionId(revision);
+ defaultMd.setResolvedModuleRevisionId(revision);
+ } else {
+ Message.warn("consistency disabled with instance of non DefaultModuleDescriptor..."
+ + " module info can't be updated, so consistency check will be done");
+ checkDescriptorConsistency(nsMrid, nsMd, ivyRef);
+ checkDescriptorConsistency(systemMrid, systemMd, ivyRef);
+ }
+ }
+ rmr = new ResolvedModuleRevision(this, this, systemMd,
+ toSystem(rmr.getReport()), isForce());
+ }
+ }
+
+ resolveAndCheckRevision(systemMd, systemMrid, ivyRef, isDynamic);
+ resolveAndCheckPublicationDate(systemDd, systemMd, systemMrid, data);
+ checkNotConvertedExclusionRule(systemMd, ivyRef, data);
+
+ if (ivyRef == null || ivyRef.getResource() != null) {
+ cacheModuleDescriptor(systemMd, systemMrid, ivyRef, rmr);
+ }
+
+ return checkLatest(systemDd, checkForcedResolvedModuleRevision(rmr), data);
+ } catch (UnresolvedDependencyException ex) {
+ if (ex.getMessage().length() > 0) {
+ if (ex.isError()) {
+ Message.error(ex.getMessage());
+ } else {
+ Message.verbose(ex.getMessage());
+ }
+ }
+ return data.getCurrentResolvedModuleRevision();
+ } finally {
+ IvyContext.popContext();
+ }
+ }
+
+ protected boolean shouldReturnResolvedModule(DependencyDescriptor dd, ResolvedModuleRevision mr) {
+ // a resolved module revision has already been found by a prior dependency resolver
+ // let's see if it should be returned and bypass this resolver
+
+ ModuleRevisionId mrid = dd.getDependencyRevisionId();
+ boolean isDynamic = getSettings().getVersionMatcher().isDynamic(mrid);
+ boolean shouldReturn = mr.isForce();
+ shouldReturn |= !isDynamic && !mr.getDescriptor().isDefault();
+ shouldReturn &= !isForce();
+
+ return shouldReturn;
+ }
+
+ private ResolvedModuleRevision checkForcedResolvedModuleRevision(ResolvedModuleRevision rmr) {
+ if (rmr == null) {
+ return null;
+ }
+ if (!isForce() || rmr.isForce()) {
+ return rmr;
+ }
+ return new ResolvedModuleRevision(rmr.getResolver(), rmr.getArtifactResolver(),
+ rmr.getDescriptor(), rmr.getReport(), true);
+ }
+
+ private void cacheModuleDescriptor(ModuleDescriptor systemMd, ModuleRevisionId systemMrid,
+ ResolvedResource ivyRef, ResolvedModuleRevision rmr) {
+ RepositoryCacheManager cacheManager = getRepositoryCacheManager();
+
+ final ModuleDescriptorParser parser = systemMd.getParser();
+
+ // the metadata artifact which was used to cache the original metadata file
+ Artifact requestedMetadataArtifact = ivyRef == null ? systemMd.getMetadataArtifact()
+ : parser.getMetadataArtifact(
+ ModuleRevisionId.newInstance(systemMrid, systemMd.getRevision()),
+ ivyRef.getResource());
+
+ cacheManager.originalToCachedModuleDescriptor(this, ivyRef, requestedMetadataArtifact, rmr,
+ new ModuleDescriptorWriter() {
+ public void write(ResolvedResource originalMdResource, ModuleDescriptor md,
+ File src, File dest) throws IOException, ParseException {
+ if (originalMdResource == null) {
+ // a basic ivy file is written containing default data
+ XmlModuleDescriptorWriter.write(md, dest);
+ } else {
+ // copy and update ivy file from source to cache
+ parser.toIvyFile(new FileInputStream(src),
+ originalMdResource.getResource(), dest, md);
+ long repLastModified = originalMdResource.getLastModified();
+ if (repLastModified > 0) {
+ dest.setLastModified(repLastModified);
+ }
+ }
+ }
+ });
+ }
+
+ private void checkNotConvertedExclusionRule(ModuleDescriptor systemMd, ResolvedResource ivyRef,
+ ResolveData data) {
+ if (!getNamespace().equals(Namespace.SYSTEM_NAMESPACE) && !systemMd.isDefault()
+ && data.getSettings().logNotConvertedExclusionRule()
+ && systemMd instanceof DefaultModuleDescriptor) {
+ DefaultModuleDescriptor dmd = (DefaultModuleDescriptor) systemMd;
+ if (dmd.isNamespaceUseful()) {
+ Message.warn("the module descriptor " + ivyRef.getResource()
+ + " has information which can't be converted into "
+ + "the system namespace. "
+ + "It will require the availability of the namespace '"
+ + getNamespace().getName() + "' to be fully usable.");
+ }
+ }
+ }
+
+ private void resolveAndCheckPublicationDate(DependencyDescriptor systemDd,
+ ModuleDescriptor systemMd, ModuleRevisionId systemMrid, ResolveData data) {
+ // resolve and check publication date
+ if (data.getDate() != null) {
+ long pubDate = getPublicationDate(systemMd, systemDd, data);
+ if (pubDate > data.getDate().getTime()) {
+ throw new UnresolvedDependencyException("\t" + getName()
+ + ": unacceptable publication date => was=" + new Date(pubDate)
+ + " required=" + data.getDate());
+ } else if (pubDate == -1) {
+ throw new UnresolvedDependencyException("\t" + getName()
+ + ": impossible to guess publication date: artifact missing for "
+ + systemMrid);
+ }
+ systemMd.setResolvedPublicationDate(new Date(pubDate));
+ }
+ }
+
+ protected void checkModuleDescriptorRevision(ModuleDescriptor systemMd,
+ ModuleRevisionId systemMrid) {
+ if (!getSettings().getVersionMatcher().accept(systemMrid, systemMd)) {
+ throw new UnresolvedDependencyException("\t" + getName()
+ + ": unacceptable revision => was="
+ + systemMd.getResolvedModuleRevisionId().getRevision() + " required="
+ + systemMrid.getRevision());
+ }
+ }
+
+ private boolean getAndCheckIsDynamic(ModuleRevisionId systemMrid) {
+ boolean isDynamic = getSettings().getVersionMatcher().isDynamic(systemMrid);
+ if (isDynamic && !acceptLatest()) {
+ throw new UnresolvedDependencyException("dynamic revisions not handled by "
+ + getClass().getName() + ". impossible to resolve " + systemMrid);
+ }
+ return isDynamic;
+ }
+
+ private void checkRevision(ModuleRevisionId systemMrid) {
+ // check revision
+ int index = systemMrid.getRevision().indexOf("@");
+ if (index != -1 && !systemMrid.getRevision().substring(index + 1).equals(workspaceName)) {
+ throw new UnresolvedDependencyException("\t" + getName() + ": unhandled revision => "
+ + systemMrid.getRevision());
+ }
+ }
+
+ private void resolveAndCheckRevision(ModuleDescriptor systemMd,
+ ModuleRevisionId dependencyConstraint, ResolvedResource ivyRef, boolean isDynamic) {
+ // we get the resolved module revision id from the descriptor: it may contain extra
+ // attributes that were not included in the dependency constraint
+ ModuleRevisionId resolvedMrid = systemMd.getResolvedModuleRevisionId();
+ if (resolvedMrid.getRevision() == null || resolvedMrid.getRevision().length() == 0
+ || resolvedMrid.getRevision().startsWith("working@")) {
+ if (!isDynamic) {
+ resolvedMrid = ModuleRevisionId.newInstance(resolvedMrid,
+ dependencyConstraint.getRevision());
+ } else if (ivyRef == null) {
+ resolvedMrid = systemMd.getMetadataArtifact().getModuleRevisionId();
+ } else if (ivyRef.getRevision() == null || ivyRef.getRevision().length() == 0) {
+ resolvedMrid = ModuleRevisionId.newInstance(resolvedMrid, "working@" + getName());
+ } else {
+ resolvedMrid = ModuleRevisionId.newInstance(resolvedMrid, ivyRef.getRevision());
+ }
+ }
+ if (isDynamic) {
+ Message.verbose("\t\t[" + toSystem(resolvedMrid).getRevision() + "] "
+ + dependencyConstraint.getModuleId());
+ }
+ systemMd.setResolvedModuleRevisionId(resolvedMrid);
+ checkModuleDescriptorRevision(systemMd, dependencyConstraint);
+ }
+
+ private ModuleRevisionId getRevision(ResolvedResource ivyRef, ModuleRevisionId askedMrid,
+ ModuleDescriptor md) throws ParseException {
+ Map allAttributes = new HashMap();
+ allAttributes.putAll(md.getQualifiedExtraAttributes());
+ allAttributes.putAll(askedMrid.getQualifiedExtraAttributes());
+
+ String revision = ivyRef.getRevision();
+ if (revision == null) {
+ Message.debug("no revision found in reference for " + askedMrid);
+ if (getSettings().getVersionMatcher().isDynamic(askedMrid)) {
+ if (md.getModuleRevisionId().getRevision() == null) {
+ revision = "working@" + getName();
+ } else {
+ Message.debug("using " + askedMrid);
+ revision = askedMrid.getRevision();
+ }
+ } else {
+ Message.debug("using " + askedMrid);
+ revision = askedMrid.getRevision();
+ }
+ }
+
+ return ModuleRevisionId.newInstance(askedMrid.getOrganisation(), askedMrid.getName(),
+ askedMrid.getBranch(), revision, allAttributes);
+ }
+
+ public ResolvedModuleRevision parse(final ResolvedResource mdRef, DependencyDescriptor dd,
+ ResolveData data) throws ParseException {
+
+ DependencyDescriptor nsDd = dd;
+ dd = toSystem(nsDd);
+
+ ModuleRevisionId mrid = dd.getDependencyRevisionId();
+ ModuleDescriptorParser parser = ModuleDescriptorParserRegistry.getInstance().getParser(
+ mdRef.getResource());
+ if (parser == null) {
+ Message.warn("no module descriptor parser available for " + mdRef.getResource());
+ return null;
+ }
+ Message.verbose("\t" + getName() + ": found md file for " + mrid);
+ Message.verbose("\t\t=> " + mdRef);
+ Message.debug("\tparser = " + parser);
+
+ ModuleRevisionId resolvedMrid = mrid;
+
+ // first check if this dependency has not yet been resolved
+ if (getSettings().getVersionMatcher().isDynamic(mrid)) {
+ resolvedMrid = ModuleRevisionId.newInstance(mrid, mdRef.getRevision());
+ IvyNode node = data.getNode(resolvedMrid);
+ if (node != null && node.getModuleRevision() != null) {
+ // this revision has already be resolved : return it
+ if (node.getDescriptor() != null && node.getDescriptor().isDefault()) {
+ Message.verbose("\t" + getName() + ": found already resolved revision: "
+ + resolvedMrid
+ + ": but it's a default one, maybe we can find a better one");
+ } else {
+ Message.verbose("\t" + getName() + ": revision already resolved: "
+ + resolvedMrid);
+ node.getModuleRevision().getReport().setSearched(true);
+ return node.getModuleRevision();
+ }
+ }
+ }
+
+ Artifact moduleArtifact = parser.getMetadataArtifact(resolvedMrid, mdRef.getResource());
+ return getRepositoryCacheManager().cacheModuleDescriptor(this, mdRef, dd, moduleArtifact,
+ downloader, getCacheOptions(data));
+ }
+
+ protected ResourceMDParser getRMDParser(final DependencyDescriptor dd, final ResolveData data) {
+ return new ResourceMDParser() {
+ public MDResolvedResource parse(Resource resource, String rev) {
+ try {
+ ResolvedModuleRevision rmr = BasicResolver.this.parse(new ResolvedResource(
+ resource, rev), dd, data);
+ if (rmr == null) {
+ return null;
+ } else {
+ return new MDResolvedResource(resource, rev, rmr);
+ }
+ } catch (ParseException e) {
+ Message.warn("Failed to parse the file '" + resource + "'", e);
+ return null;
+ }
+ }
+
+ };
+ }
+
+ protected ResourceMDParser getDefaultRMDParser(final ModuleId mid) {
+ return new ResourceMDParser() {
+ public MDResolvedResource parse(Resource resource, String rev) {
+ DefaultModuleDescriptor md = DefaultModuleDescriptor
+ .newDefaultInstance(new ModuleRevisionId(mid, rev));
+ MetadataArtifactDownloadReport madr = new MetadataArtifactDownloadReport(
+ md.getMetadataArtifact());
+ madr.setDownloadStatus(DownloadStatus.NO);
+ madr.setSearched(true);
+ return new MDResolvedResource(resource, rev, new ResolvedModuleRevision(
+ BasicResolver.this, BasicResolver.this, md, madr, isForce()));
+ }
+ };
+ }
+
+ // private boolean isResolved(ResolveData data, ModuleRevisionId mrid) {
+ // IvyNode node = getSystemNode(data, mrid);
+ // return node != null && node.getModuleRevision() != null;
+ // }
+ //
+ private void checkDescriptorConsistency(ModuleRevisionId mrid, ModuleDescriptor md,
+ ResolvedResource ivyRef) throws ParseException {
+ boolean ok = true;
+ StringBuffer errors = new StringBuffer();
+ if (!mrid.getOrganisation().equals(md.getModuleRevisionId().getOrganisation())) {
+ Message.error("\t" + getName() + ": bad organisation found in " + ivyRef.getResource()
+ + ": expected='" + mrid.getOrganisation() + "' found='"
+ + md.getModuleRevisionId().getOrganisation() + "'");
+ errors.append("bad organisation: expected='" + mrid.getOrganisation() + "' found='"
+ + md.getModuleRevisionId().getOrganisation() + "'; ");
+ ok = false;
+ }
+ if (!mrid.getName().equals(md.getModuleRevisionId().getName())) {
+ Message.error("\t" + getName() + ": bad module name found in " + ivyRef.getResource()
+ + ": expected='" + mrid.getName() + " found='"
+ + md.getModuleRevisionId().getName() + "'");
+ errors.append("bad module name: expected='" + mrid.getName() + "' found='"
+ + md.getModuleRevisionId().getName() + "'; ");
+ ok = false;
+ }
+ if (mrid.getBranch() != null
+ && !mrid.getBranch().equals(md.getModuleRevisionId().getBranch())) {
+ Message.error("\t" + getName() + ": bad branch name found in " + ivyRef.getResource()
+ + ": expected='" + mrid.getBranch() + " found='"
+ + md.getModuleRevisionId().getBranch() + "'");
+ errors.append("bad branch name: expected='" + mrid.getBranch() + "' found='"
+ + md.getModuleRevisionId().getBranch() + "'; ");
+ ok = false;
+ }
+ if (ivyRef.getRevision() != null && !ivyRef.getRevision().startsWith("working@")
+ && !mrid.getRevision().equals(md.getModuleRevisionId().getRevision())) {
+ ModuleRevisionId expectedMrid = ModuleRevisionId.newInstance(mrid, mrid.getRevision());
+ if (!getSettings().getVersionMatcher().accept(expectedMrid, md)) {
+ Message.error("\t" + getName() + ": bad revision found in " + ivyRef.getResource()
+ + ": expected='" + ivyRef.getRevision() + " found='"
+ + md.getModuleRevisionId().getRevision() + "'");
+ errors.append("bad revision: expected='" + ivyRef.getRevision() + "' found='"
+ + md.getModuleRevisionId().getRevision() + "'; ");
+ ok = false;
+ }
+ }
+ if (!getSettings().getStatusManager().isStatus(md.getStatus())) {
+ Message.error("\t" + getName() + ": bad status found in " + ivyRef.getResource()
+ + ": '" + md.getStatus() + "'");
+ errors.append("bad status: '" + md.getStatus() + "'; ");
+ ok = false;
+ }
+ for (Iterator it = mrid.getExtraAttributes().entrySet().iterator(); it.hasNext();) {
+ Entry extra = (Entry) it.next();
+ if (extra.getValue() != null
+ && !extra.getValue().equals(md.getExtraAttribute((String) extra.getKey()))) {
+ String errorMsg = "bad " + extra.getKey() + " found in " + ivyRef.getResource()
+ + ": expected='" + extra.getValue() + "' found='"
+ + md.getExtraAttribute((String) extra.getKey()) + "'";
+ Message.error("\t" + getName() + ": " + errorMsg);
+ errors.append(errorMsg + ";");
+ ok = false;
+ }
+ }
+ if (!ok) {
+ throw new ParseException("inconsistent module descriptor file found in '"
+ + ivyRef.getResource() + "': " + errors, 0);
+ }
+ }
+
+ /**
+ * When the resolver has many choices, this function helps choosing one
+ *
+ * @param rress
+ * the list of resolved resource which the resolver found to fit the requirement
+ * @param rmdparser
+ * the parser of module descriptor
+ * @param mrid
+ * the module being resolved
+ * @param date
+ * the current date
+ * @return the selected resource
+ */
+ public ResolvedResource findResource(ResolvedResource[] rress, ResourceMDParser rmdparser,
+ ModuleRevisionId mrid, Date date) {
+ String name = getName();
+ VersionMatcher versionMatcher = getSettings().getVersionMatcher();
+
+ ResolvedResource found = null;
+ List sorted = getLatestStrategy().sort(rress);
+ List rejected = new ArrayList();
+ List foundBlacklisted = new ArrayList();
+ IvyContext context = IvyContext.getContext();
+
+ for (ListIterator iter = sorted.listIterator(sorted.size()); iter.hasPrevious();) {
+ ResolvedResource rres = (ResolvedResource) iter.previous();
+ // we start by filtering based on information already available,
+ // even though we don't even know if the resource actually exist.
+ // But checking for existence is most of the time more costly than checking
+ // name, blacklisting and first level version matching
+ if (filterNames(new ArrayList(Collections.singleton(rres.getRevision()))).isEmpty()) {
+ Message.debug("\t" + name + ": filtered by name: " + rres);
+ continue;
+ }
+ ModuleRevisionId foundMrid = ModuleRevisionId.newInstance(mrid, rres.getRevision());
+
+ ResolveData data = context.getResolveData();
+ if (data != null && data.getReport() != null
+ && data.isBlacklisted(data.getReport().getConfiguration(), foundMrid)) {
+ Message.debug("\t" + name + ": blacklisted: " + rres);
+ rejected.add(rres.getRevision() + " (blacklisted)");
+ foundBlacklisted.add(foundMrid);
+ continue;
+ }
+
+ if (!versionMatcher.accept(mrid, foundMrid)) {
+ Message.debug("\t" + name + ": rejected by version matcher: " + rres);
+ rejected.add(rres.getRevision());
+ continue;
+ }
+ if (rres.getResource() != null && !rres.getResource().exists()) {
+ Message.debug("\t" + name + ": unreachable: " + rres + "; res="
+ + rres.getResource());
+ rejected.add(rres.getRevision() + " (unreachable)");
+ continue;
+ }
+ if ((date != null && rres.getLastModified() > date.getTime())) {
+ Message.verbose("\t" + name + ": too young: " + rres);
+ rejected.add(rres.getRevision() + " (" + rres.getLastModified() + ")");
+ continue;
+ }
+ if (versionMatcher.needModuleDescriptor(mrid, foundMrid)) {
+ ResolvedResource r = rmdparser.parse(rres.getResource(), rres.getRevision());
+ if (r == null) {
+ Message.debug("\t" + name + ": impossible to get module descriptor resource: "
+ + rres);
+ rejected.add(rres.getRevision() + " (no or bad MD)");
+ continue;
+ }
+ ModuleDescriptor md = ((MDResolvedResource) r).getResolvedModuleRevision()
+ .getDescriptor();
+ if (md.isDefault()) {
+ Message.debug("\t" + name + ": default md rejected by version matcher"
+ + "requiring module descriptor: " + rres);
+ rejected.add(rres.getRevision() + " (MD)");
+ continue;
+ } else if (!versionMatcher.accept(mrid, md)) {
+ Message.debug("\t" + name + ": md rejected by version matcher: " + rres);
+ rejected.add(rres.getRevision() + " (MD)");
+ continue;
+ } else {
+ found = r;
+ }
+ } else {
+ found = rres;
+ }
+
+ if (found != null) {
+ break;
+ }
+ }
+ if (found == null && !rejected.isEmpty()) {
+ logAttempt(rejected.toString());
+ }
+ if (found == null && !foundBlacklisted.isEmpty()) {
+ // all acceptable versions have been blacklisted, this means that an unsolvable conflict
+ // has been found
+ DependencyDescriptor dd = context.getDependencyDescriptor();
+ IvyNode parentNode = context.getResolveData().getNode(dd.getParentRevisionId());
+ ConflictManager cm = parentNode.getConflictManager(mrid.getModuleId());
+ cm.handleAllBlacklistedRevisions(dd, foundBlacklisted);
+ }
+
+ return found;
+ }
+
+ /**
+ * Filters names before returning them in the findXXXNames or findTokenValues method.
+ * <p>
+ * Remember to call the super implementation when overriding this method.
+ * </p>
+ *
+ * @param names
+ * the list to filter.
+ * @return the filtered list
+ */
+ protected Collection filterNames(Collection names) {
+ getSettings().filterIgnore(names);
+ return names;
+ }
+
+ protected void clearIvyAttempts() {
+ ivyattempts.clear();
+ clearArtifactAttempts();
+ }
+
+ protected void logIvyAttempt(String attempt) {
+ ivyattempts.add(attempt);
+ Message.verbose("\t\ttried " + attempt);
+ }
+
+ protected void logArtifactAttempt(Artifact art, String attempt) {
+ List attempts = (List) artattempts.get(art);
+ if (attempts == null) {
+ attempts = new ArrayList();
+ artattempts.put(art, attempts);
+ }
+ attempts.add(attempt);
+ Message.verbose("\t\ttried " + attempt);
+ }
+
+ protected void logAttempt(String attempt) {
+ Artifact currentArtifact = (Artifact) IvyContext.getContext().get(getName() + ".artifact");
+ if (currentArtifact != null) {
+ logArtifactAttempt(currentArtifact, attempt);
+ } else {
+ logIvyAttempt(attempt);
+ }
+ }
+
+ public void reportFailure() {
+ Message.warn("==== " + getName() + ": tried");
+ for (ListIterator iter = ivyattempts.listIterator(); iter.hasNext();) {
+ String m = (String) iter.next();
+ Message.warn(" " + m);
+ }
+ for (Iterator iter = artattempts.keySet().iterator(); iter.hasNext();) {
+ Artifact art = (Artifact) iter.next();
+ List attempts = (List) artattempts.get(art);
+ if (attempts != null) {
+ Message.warn(" -- artifact " + art + ":");
+ for (ListIterator iterator = attempts.listIterator(); iterator.hasNext();) {
+ String m = (String) iterator.next();
+ Message.warn(" " + m);
+ }
+ }
+ }
+ }
+
+ public void reportFailure(Artifact art) {
+ Message.warn("==== " + getName() + ": tried");
+ List attempts = (List) artattempts.get(art);
+ if (attempts != null) {
+ for (ListIterator iter = attempts.listIterator(); iter.hasNext();) {
+ String m = (String) iter.next();
+ Message.warn(" " + m);
+ }
+ }
+ }
+
+ protected boolean acceptLatest() {
+ return true;
+ }
+
+ public DownloadReport download(Artifact[] artifacts, DownloadOptions options) {
+ RepositoryCacheManager cacheManager = getRepositoryCacheManager();
+
+ clearArtifactAttempts();
+ DownloadReport dr = new DownloadReport();
+ for (int i = 0; i < artifacts.length; i++) {
+ ArtifactDownloadReport adr = cacheManager.download(artifacts[i],
+ artifactResourceResolver, downloader, getCacheDownloadOptions(options));
+ if (DownloadStatus.FAILED == adr.getDownloadStatus()) {
+ if (!ArtifactDownloadReport.MISSING_ARTIFACT.equals(adr.getDownloadDetails())) {
+ Message.warn("\t" + adr);
+ }
+ } else if (DownloadStatus.NO == adr.getDownloadStatus()) {
+ Message.verbose("\t" + adr);
+ } else if (LogOptions.LOG_QUIET.equals(options.getLog())) {
+ Message.verbose("\t" + adr);
+ } else {
+ Message.info("\t" + adr);
+ }
+ dr.addArtifactReport(adr);
+ checkInterrupted();
+ }
+ return dr;
+ }
+
+ protected void clearArtifactAttempts() {
+ artattempts.clear();
+ }
+
+ public ArtifactDownloadReport download(final ArtifactOrigin origin, DownloadOptions options) {
+ Checks.checkNotNull(origin, "origin");
+ return getRepositoryCacheManager().download(origin.getArtifact(),
+ new ArtifactResourceResolver() {
+ public ResolvedResource resolve(Artifact artifact) {
+ try {
+ Resource resource = getResource(origin.getLocation());
+ if (resource == null) {
+ return null;
+ }
+ String revision = origin.getArtifact().getModuleRevisionId().getRevision();
+ return new ResolvedResource(resource, revision);
+ } catch (IOException e) {
+ Message.debug(e);
+ return null;
+ }
+ }
+ }, downloader, getCacheDownloadOptions(options));
+ }
+
+ protected abstract Resource getResource(String source) throws IOException;
+
+ public boolean exists(Artifact artifact) {
+ ResolvedResource artifactRef = getArtifactRef(artifact, null);
+ if (artifactRef != null) {
+ return artifactRef.getResource().exists();
+ }
+ return false;
+ }
+
+ public ArtifactOrigin locate(Artifact artifact) {
+ ArtifactOrigin origin = getRepositoryCacheManager().getSavedArtifactOrigin(
+ toSystem(artifact));
+ if (!ArtifactOrigin.isUnknown(origin)) {
+ return origin;
+ }
+ ResolvedResource artifactRef = getArtifactRef(artifact, null);
+ if (artifactRef != null && artifactRef.getResource().exists()) {
+ return new ArtifactOrigin(artifact, artifactRef.getResource().isLocal(), artifactRef
+ .getResource().getName());
+ }
+ return null;
+ }
+
+ protected long getPublicationDate(ModuleDescriptor md, DependencyDescriptor dd, ResolveData data) {
+ if (md.getPublicationDate() != null) {
+ return md.getPublicationDate().getTime();
+ }
+ ResolvedResource artifactRef = findFirstArtifactRef(md, dd, data);
+ if (artifactRef != null) {
+ return artifactRef.getLastModified();
+ }
+ return -1;
+ }
+
+ public String toString() {
+ return getName();
+ }
+
+ public String[] listTokenValues(String token, Map otherTokenValues) {
+ Collection ret = findNames(otherTokenValues, token);
+ return (String[]) ret.toArray(new String[ret.size()]);
+ }
+
+ public OrganisationEntry[] listOrganisations() {
+ Collection names = findNames(Collections.EMPTY_MAP, IvyPatternHelper.ORGANISATION_KEY);
+ OrganisationEntry[] ret = new OrganisationEntry[names.size()];
+ int i = 0;
+ for (Iterator iter = names.iterator(); iter.hasNext(); i++) {
+ String org = (String) iter.next();
+ ret[i] = new OrganisationEntry(this, org);
+ }
+ return ret;
+ }
+
+ public ModuleEntry[] listModules(OrganisationEntry org) {
+ Map tokenValues = new HashMap();
+ tokenValues.put(IvyPatternHelper.ORGANISATION_KEY, org.getOrganisation());
+ Collection names = findNames(tokenValues, IvyPatternHelper.MODULE_KEY);
+ ModuleEntry[] ret = new ModuleEntry[names.size()];
+ int i = 0;
+ for (Iterator iter = names.iterator(); iter.hasNext(); i++) {
+ String name = (String) iter.next();
+ ret[i] = new ModuleEntry(org, name);
+ }
+ return ret;
+ }
+
+ public RevisionEntry[] listRevisions(ModuleEntry mod) {
+ Map tokenValues = new HashMap();
+ tokenValues.put(IvyPatternHelper.ORGANISATION_KEY, mod.getOrganisation());
+ tokenValues.put(IvyPatternHelper.MODULE_KEY, mod.getModule());
+ Collection names = findNames(tokenValues, IvyPatternHelper.REVISION_KEY);
+ RevisionEntry[] ret = new RevisionEntry[names.size()];
+ int i = 0;
+ for (Iterator iter = names.iterator(); iter.hasNext(); i++) {
+ String name = (String) iter.next();
+ ret[i] = new RevisionEntry(mod, name);
+ }
+ return ret;
+ }
+
+ protected abstract Collection findNames(Map tokenValues, String token);
+
+ protected ResolvedResource findFirstArtifactRef(ModuleDescriptor md, DependencyDescriptor dd,
+ ResolveData data) {
+ ResolvedResource ret = null;
+ String[] conf = md.getConfigurationsNames();
+ for (int i = 0; i < conf.length; i++) {
+ Artifact[] artifacts = md.getArtifacts(conf[i]);
+ for (int j = 0; j < artifacts.length; j++) {
+ ret = getArtifactRef(artifacts[j], data.getDate());
+ if (ret != null) {
+ return ret;
+ }
+ }
+ }
+ return null;
+ }
+
+ protected long getAndCheck(Resource resource, File dest) throws IOException {
+ long size = get(resource, dest);
+ String[] checksums = getChecksumAlgorithms();
+ boolean checked = false;
+ for (int i = 0; i < checksums.length && !checked; i++) {
+ checked = check(resource, dest, checksums[i]);
+ }
+ return size;
+ }
+
+ /**
+ * Checks the given resource checksum if a checksum resource exists.
+ *
+ * @param resource
+ * the resource to check
+ * @param dest
+ * the file where the resource has been downloaded
+ * @param algorithm
+ * the checksum algorithm to use
+ * @return true if the checksum has been successfully checked, false if the checksum wasn't
+ * available
+ * @throws IOException
+ * if a checksum exist but do not match the downloaded file checksum
+ */
+ private boolean check(Resource resource, File dest, String algorithm) throws IOException {
+ if (!ChecksumHelper.isKnownAlgorithm(algorithm)) {
+ throw new IllegalArgumentException("Unknown checksum algorithm: " + algorithm);
+ }
+
+ Resource csRes = resource.clone(resource.getName() + "." + algorithm);
+ if (csRes.exists()) {
+ Message.debug(algorithm + " file found for " + resource + ": checking...");
+ File csFile = File.createTempFile("ivytmp", algorithm);
+ try {
+ get(csRes, csFile);
+ try {
+ ChecksumHelper.check(dest, csFile, algorithm);
+ Message.verbose(algorithm + " OK for " + resource);
+ return true;
+ } catch (IOException ex) {
+ dest.delete();
+ throw ex;
+ }
+ } finally {
+ csFile.delete();
+ }
+ } else {
+ return false;
+ }
+ }
+
+ protected ResolvedResource getArtifactRef(Artifact artifact, Date date) {
+ IvyContext.getContext().set(getName() + ".artifact", artifact);
+ try {
+ ResolvedResource ret = findArtifactRef(artifact, date);
+ if (ret == null && artifact.getUrl() != null) {
+ URL url = artifact.getUrl();
+ Message.verbose("\tusing url for " + artifact + ": " + url);
+ logArtifactAttempt(artifact, url.toExternalForm());
+ Resource resource;
+ if ("file".equals(url.getProtocol())) {
+ File f;
+ try {
+ f = new File(new URI(url.toExternalForm()));
+ } catch (URISyntaxException e) {
+ // unexpected, try to get the best of it
+ f = new File(url.getPath());
+ }
+ resource = new FileResource(new FileRepository(), f);
+ } else {
+ resource = new URLResource(url);
+ }
+ ret = new ResolvedResource(resource, artifact.getModuleRevisionId().getRevision());
+ }
+ return ret;
+ } finally {
+ IvyContext.getContext().set(getName() + ".artifact", null);
+ }
+ }
+
+ public ResolvedResource doFindArtifactRef(Artifact artifact, Date date) {
+ return findArtifactRef(artifact, date);
+ }
+
+ protected abstract ResolvedResource findArtifactRef(Artifact artifact, Date date);
+
+ protected abstract long get(Resource resource, File dest) throws IOException;
+
+ public boolean isCheckconsistency() {
+ return checkconsistency;
+ }
+
+ public void setCheckconsistency(boolean checkConsitency) {
+ checkconsistency = checkConsitency;
+ }
+
+ public void setForce(boolean force) {
+ this.force = force;
+ }
+
+ public boolean isForce() {
+ return force;
+ }
+
+ public boolean isAllownomd() {
+ return allownomd;
+ }
+
+ public void setAllownomd(boolean b) {
+ Message.deprecated("allownomd is deprecated, please use descriptor=\""
+ + (b ? DESCRIPTOR_OPTIONAL : DESCRIPTOR_REQUIRED) + "\" instead");
+ allownomd = b;
+ }
+
+ /**
+ * Sets the module descriptor presence rule. Should be one of {@link #DESCRIPTOR_REQUIRED} or
+ * {@link #DESCRIPTOR_OPTIONAL}.
+ *
+ * @param descriptorRule
+ * the descriptor rule to use with this resolver.
+ */
+ public void setDescriptor(String descriptorRule) {
+ if (DESCRIPTOR_REQUIRED.equals(descriptorRule)) {
+ allownomd = false;
+ } else if (DESCRIPTOR_OPTIONAL.equals(descriptorRule)) {
+ allownomd = true;
+ } else {
+ throw new IllegalArgumentException("unknown descriptor rule '" + descriptorRule
+ + "'. Allowed rules are: "
+ + Arrays.asList(new String[] {DESCRIPTOR_REQUIRED, DESCRIPTOR_OPTIONAL}));
+ }
+ }
+
+ public String[] getChecksumAlgorithms() {
+ String csDef = checksums == null ? getSettings().getVariable("ivy.checksums") : checksums;
+ if (csDef == null) {
+ return new String[0];
+ }
+ // csDef is a comma separated list of checksum algorithms to use with this resolver
+ // we parse and return it as a String[]
+ String[] checksums = csDef.split(",");
+ List algos = new ArrayList();
+ for (int i = 0; i < checksums.length; i++) {
+ String cs = checksums[i].trim();
+ if (!"".equals(cs) && !"none".equals(cs)) {
+ algos.add(cs);
+ }
+ }
+ return (String[]) algos.toArray(new String[algos.size()]);
+ }
+
+ public void setChecksums(String checksums) {
+ this.checksums = checksums;
+ }
+
+ private final ArtifactResourceResolver artifactResourceResolver = new ArtifactResourceResolver() {
+ public ResolvedResource resolve(Artifact artifact) {
+ artifact = fromSystem(artifact);
+ return getArtifactRef(artifact, null);
+ }
+ };
+
+ private final ResourceDownloader downloader = new ResourceDownloader() {
+ public void download(Artifact artifact, Resource resource, File dest) throws IOException {
+ if (dest.exists()) {
+ dest.delete();
+ }
+ File part = new File(dest.getAbsolutePath() + ".part");
+ if (resource.getName().equals(String.valueOf(artifact.getUrl()))) {
+ if (part.getParentFile() != null) {
+ part.getParentFile().mkdirs();
+ }
+ extartifactrep.get(resource.getName(), part);
+ } else {
+ getAndCheck(resource, part);
+ }
+ if (!part.renameTo(dest)) {
+ throw new IOException("impossible to move part file to definitive one: " + part
+ + " -> " + dest);
+ }
+
+ }
+ };
+
+}
diff --git a/src/java/org/apache/ivy/plugins/resolver/BintrayResolver.java b/src/java/org/apache/ivy/plugins/resolver/BintrayResolver.java
new file mode 100644
index 0000000..c8d7b26
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/resolver/BintrayResolver.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.resolver;
+
+/**
+ * BintrayResolver is a resolver which can be used to resolve dependencies found in the Bintray
+ * artifacts repository.
+ */
+ at SuppressWarnings("ClassTooDeepInInheritanceTree")
+public class BintrayResolver extends IBiblioResolver {
+
+ private static final String JCENTER = "https://jcenter.bintray.com/";
+
+ private static final String DL_BINTRAY = "https://dl.bintray.com/";
+
+ private static final String DEFAULT_NAME = "bintray/jcenter";
+
+ private String subject;
+
+ private String repo;
+
+ private boolean isNameUpdatable; // Whether resolver's name was not originally specified and can
+ // be updated.
+
+ public BintrayResolver() {
+ setRoot(JCENTER);
+ updateName(DEFAULT_NAME);
+ setM2compatible(true);
+ setUsepoms(true);
+ setUseMavenMetadata(true);
+ }
+
+ public void setSubject(String subject) {
+ this.subject = subject;
+ updateRoot();
+ }
+
+ public void setRepo(String repo) {
+ this.repo = repo;
+ updateRoot();
+ }
+
+ @SuppressWarnings("MethodWithMultipleReturnPoints")
+ private void updateRoot() {
+ if (isEmpty(subject) || isEmpty(repo)) {
+ return;
+ }
+
+ setRoot(String.format("%s%s/%s/", DL_BINTRAY, subject, repo));
+ updateName(String.format("bintray/%s/%s", subject, repo));
+ }
+
+ private void updateName(String defaultName) {
+ if (isEmpty(defaultName)) {
+ throw new IllegalArgumentException("Default resolver name must not be null or empty");
+ }
+ if (isEmpty(getName()) || isNameUpdatable) {
+ isNameUpdatable = true;
+ setName(defaultName);
+ }
+ }
+
+ private boolean isEmpty(String s) {
+ return ((s == null) || (s.trim().length() < 1));
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/resolver/CacheResolver.java b/src/java/org/apache/ivy/plugins/resolver/CacheResolver.java
new file mode 100644
index 0000000..27ac887
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/resolver/CacheResolver.java
@@ -0,0 +1,185 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.resolver;
+
+import java.io.File;
+import java.io.IOException;
+import java.text.ParseException;
+import java.util.ArrayList;
+
+import org.apache.ivy.core.cache.ArtifactOrigin;
+import org.apache.ivy.core.cache.DefaultRepositoryCacheManager;
+import org.apache.ivy.core.cache.RepositoryCacheManager;
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.report.ArtifactDownloadReport;
+import org.apache.ivy.core.report.DownloadReport;
+import org.apache.ivy.core.report.DownloadStatus;
+import org.apache.ivy.core.resolve.DownloadOptions;
+import org.apache.ivy.core.resolve.IvyNode;
+import org.apache.ivy.core.resolve.ResolveData;
+import org.apache.ivy.core.resolve.ResolvedModuleRevision;
+import org.apache.ivy.core.search.ModuleEntry;
+import org.apache.ivy.core.search.OrganisationEntry;
+import org.apache.ivy.core.search.RevisionEntry;
+import org.apache.ivy.plugins.repository.file.FileResource;
+import org.apache.ivy.plugins.resolver.util.ResolvedResource;
+import org.apache.ivy.util.Message;
+
+public class CacheResolver extends FileSystemResolver {
+ public CacheResolver() {
+ }
+
+ public CacheResolver(ResolverSettings settings) {
+ setSettings(settings);
+ setName("cache");
+ }
+
+ public ResolvedModuleRevision getDependency(DependencyDescriptor dd, ResolveData data)
+ throws ParseException {
+ clearIvyAttempts();
+
+ ModuleRevisionId mrid = dd.getDependencyRevisionId();
+ // check revision
+
+ ResolvedModuleRevision rmr = getRepositoryCacheManager().findModuleInCache(dd, mrid,
+ getCacheOptions(data), null);
+ if (rmr != null) {
+ Message.verbose("\t" + getName() + ": revision in cache: " + mrid);
+ return rmr;
+ } else if (!getSettings().getVersionMatcher().isDynamic(mrid)) {
+ Message.verbose("\t" + getName() + ": no ivy file in cache found for " + mrid);
+ return null;
+ } else {
+ ensureConfigured();
+ ResolvedResource ivyRef = findIvyFileRef(dd, data);
+ if (ivyRef != null) {
+ Message.verbose("\t" + getName() + ": found ivy file in cache for " + mrid);
+ Message.verbose("\t\t=> " + ivyRef);
+
+ ModuleRevisionId resolvedMrid = ModuleRevisionId.newInstance(mrid,
+ ivyRef.getRevision());
+ IvyNode node = data.getNode(resolvedMrid);
+ if (node != null && node.getModuleRevision() != null) {
+ // this revision has already be resolved : return it
+ Message.verbose("\t" + getName() + ": revision already resolved: "
+ + resolvedMrid);
+ return node.getModuleRevision();
+ }
+ rmr = getRepositoryCacheManager().findModuleInCache(
+ dd.clone(ModuleRevisionId.newInstance(dd.getDependencyRevisionId(),
+ ivyRef.getRevision())), dd.getDependencyRevisionId(),
+ getCacheOptions(data), null);
+ if (rmr != null) {
+ Message.verbose("\t" + getName() + ": revision in cache: " + resolvedMrid);
+ return rmr;
+ } else {
+ Message.error("\t" + getName()
+ + ": inconsistent cache: clean it and resolve again");
+ return null;
+ }
+ } else {
+ Message.verbose("\t" + getName() + ": no ivy file in cache found for " + mrid);
+ return null;
+ }
+ }
+ }
+
+ public DownloadReport download(Artifact[] artifacts, DownloadOptions options) {
+ ensureConfigured();
+ clearArtifactAttempts();
+ DownloadReport dr = new DownloadReport();
+ for (int i = 0; i < artifacts.length; i++) {
+ final ArtifactDownloadReport adr = new ArtifactDownloadReport(artifacts[i]);
+ dr.addArtifactReport(adr);
+ ResolvedResource artifactRef = getArtifactRef(artifacts[i], null);
+ if (artifactRef != null) {
+ Message.verbose("\t[NOT REQUIRED] " + artifacts[i]);
+ ArtifactOrigin origin = new ArtifactOrigin(artifacts[i], true, artifactRef
+ .getResource().getName());
+ File archiveFile = ((FileResource) artifactRef.getResource()).getFile();
+ adr.setDownloadStatus(DownloadStatus.NO);
+ adr.setSize(archiveFile.length());
+ adr.setArtifactOrigin(origin);
+ adr.setLocalFile(archiveFile);
+ } else {
+ adr.setDownloadStatus(DownloadStatus.FAILED);
+ }
+ }
+ return dr;
+ }
+
+ public boolean exists(Artifact artifact) {
+ ensureConfigured();
+ return super.exists(artifact);
+ }
+
+ public ArtifactOrigin locate(Artifact artifact) {
+ ensureConfigured();
+ return super.locate(artifact);
+ }
+
+ public void publish(Artifact artifact, File src, boolean overwrite) throws IOException {
+ ensureConfigured();
+ super.publish(artifact, src, overwrite);
+ }
+
+ public OrganisationEntry[] listOrganisations() {
+ ensureConfigured();
+ return super.listOrganisations();
+ }
+
+ public ModuleEntry[] listModules(OrganisationEntry org) {
+ ensureConfigured();
+ return super.listModules(org);
+ }
+
+ public RevisionEntry[] listRevisions(ModuleEntry module) {
+ ensureConfigured();
+ return super.listRevisions(module);
+ }
+
+ public void dumpSettings() {
+ Message.verbose("\t" + getName() + " [cache]");
+ }
+
+ private void ensureConfigured() {
+ if (getIvyPatterns().isEmpty()) {
+ setIvyPatterns(new ArrayList());
+ setArtifactPatterns(new ArrayList());
+ RepositoryCacheManager[] caches = getSettings().getRepositoryCacheManagers();
+ for (int i = 0; i < caches.length; i++) {
+ if (caches[i] instanceof DefaultRepositoryCacheManager) {
+ DefaultRepositoryCacheManager c = (DefaultRepositoryCacheManager) caches[i];
+ addIvyPattern(c.getBasedir().getAbsolutePath() + "/" + c.getIvyPattern());
+ addArtifactPattern(c.getBasedir().getAbsolutePath() + "/"
+ + c.getArtifactPattern());
+ } else {
+ Message.verbose(caches[i]
+ + ": cache implementation is not a DefaultRepositoryCacheManager:"
+ + " unable to configure cache resolver with it");
+ }
+ }
+ }
+ }
+
+ public String getTypeName() {
+ return "cache";
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/resolver/ChainResolver.java b/src/java/org/apache/ivy/plugins/resolver/ChainResolver.java
new file mode 100644
index 0000000..63b6c1a
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/resolver/ChainResolver.java
@@ -0,0 +1,349 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.resolver;
+
+import java.io.File;
+import java.io.IOException;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.ivy.core.cache.ArtifactOrigin;
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.report.ArtifactDownloadReport;
+import org.apache.ivy.core.report.DownloadReport;
+import org.apache.ivy.core.report.DownloadStatus;
+import org.apache.ivy.core.resolve.DownloadOptions;
+import org.apache.ivy.core.resolve.ResolveData;
+import org.apache.ivy.core.resolve.ResolvedModuleRevision;
+import org.apache.ivy.plugins.latest.ArtifactInfo;
+import org.apache.ivy.plugins.latest.LatestStrategy;
+import org.apache.ivy.plugins.resolver.util.HasLatestStrategy;
+import org.apache.ivy.plugins.resolver.util.ResolvedResource;
+import org.apache.ivy.util.Message;
+import org.apache.ivy.util.StringUtils;
+
+/**
+ *
+ */
+public class ChainResolver extends AbstractResolver {
+ public static class ResolvedModuleRevisionArtifactInfo implements ArtifactInfo {
+ private ResolvedModuleRevision rmr;
+
+ public ResolvedModuleRevisionArtifactInfo(ResolvedModuleRevision rmr) {
+ this.rmr = rmr;
+ }
+
+ public String getRevision() {
+ return rmr.getId().getRevision();
+ }
+
+ public long getLastModified() {
+ return rmr.getPublicationDate().getTime();
+ }
+
+ }
+
+ private boolean returnFirst = false;
+
+ private List chain = new ArrayList();
+
+ private boolean dual;
+
+ public void add(DependencyResolver resolver) {
+ chain.add(resolver);
+ }
+
+ public ResolvedModuleRevision getDependency(DependencyDescriptor dd, ResolveData data)
+ throws ParseException {
+ data = new ResolveData(data, doValidate(data));
+
+ List errors = new ArrayList();
+
+ ResolvedModuleRevision resolved = data.getCurrentResolvedModuleRevision();
+ ResolvedModuleRevision mr = resolved;
+
+ if (mr == null) {
+ Message.verbose(getName() + ": Checking cache for: " + dd);
+ mr = findModuleInCache(dd, data, true);
+ if (mr != null) {
+ Message.verbose(getName() + ": module revision found in cache: " + mr.getId());
+ mr = forcedRevision(mr);
+ }
+ }
+
+ for (Iterator iter = chain.iterator(); iter.hasNext();) {
+ DependencyResolver resolver = (DependencyResolver) iter.next();
+ LatestStrategy oldLatest = setLatestIfRequired(resolver, getLatestStrategy());
+ try {
+ ResolvedModuleRevision previouslyResolved = mr;
+ data.setCurrentResolvedModuleRevision(previouslyResolved);
+ mr = resolver.getDependency(dd, data);
+ if (mr != previouslyResolved && isReturnFirst()) {
+ mr = forcedRevision(mr);
+ }
+ } catch (Exception ex) {
+ Message.verbose("problem occurred while resolving " + dd + " with " + resolver, ex);
+ errors.add(ex);
+ } finally {
+ if (oldLatest != null) {
+ setLatest(resolver, oldLatest);
+ }
+ }
+ checkInterrupted();
+ }
+ if (mr == null && !errors.isEmpty()) {
+ if (errors.size() == 1) {
+ Exception ex = (Exception) errors.get(0);
+ if (ex instanceof RuntimeException) {
+ throw (RuntimeException) ex;
+ } else if (ex instanceof ParseException) {
+ throw (ParseException) ex;
+ } else {
+ throw new RuntimeException(ex.toString(), ex);
+ }
+ } else {
+ StringBuffer err = new StringBuffer();
+ for (Iterator iter = errors.iterator(); iter.hasNext();) {
+ Exception ex = (Exception) iter.next();
+ err.append("\t").append(StringUtils.getErrorMessage(ex)).append("\n");
+ }
+ err.setLength(err.length() - 1);
+ throw new RuntimeException("several problems occurred while resolving " + dd
+ + ":\n" + err);
+ }
+ }
+ if (resolved == mr) {
+ // nothing has actually been resolved here, we don't need to touch the returned rmr
+ return resolved;
+ }
+ return resolvedRevision(mr);
+ }
+
+ private ResolvedModuleRevision resolvedRevision(ResolvedModuleRevision mr) {
+ if (isDual() && mr != null) {
+ return new ResolvedModuleRevision(mr.getResolver(), this, mr.getDescriptor(),
+ mr.getReport(), mr.isForce());
+ } else {
+ return mr;
+ }
+ }
+
+ private ResolvedModuleRevision forcedRevision(ResolvedModuleRevision rmr) {
+ if (rmr == null) {
+ return null;
+ }
+ return new ResolvedModuleRevision(rmr.getResolver(), rmr.getArtifactResolver(),
+ rmr.getDescriptor(), rmr.getReport(), true);
+ }
+
+ private LatestStrategy setLatestIfRequired(DependencyResolver resolver,
+ LatestStrategy latestStrategy) {
+ String latestName = getLatestStrategyName(resolver);
+ if (latestName != null && !"default".equals(latestName)) {
+ LatestStrategy oldLatest = getLatest(resolver);
+ setLatest(resolver, latestStrategy);
+ return oldLatest;
+ } else {
+ return null;
+ }
+ }
+
+ public ResolvedResource findIvyFileRef(DependencyDescriptor dd, ResolveData data) {
+ for (Iterator iter = chain.iterator(); iter.hasNext();) {
+ DependencyResolver resolver = (DependencyResolver) iter.next();
+ ResolvedResource result = resolver.findIvyFileRef(dd, data);
+ if (result != null) {
+ return result;
+ }
+ }
+
+ return null;
+ }
+
+ public Map[] listTokenValues(String[] tokens, Map criteria) {
+ Set result = new HashSet();
+ for (Iterator iter = chain.iterator(); iter.hasNext();) {
+ DependencyResolver resolver = (DependencyResolver) iter.next();
+ Map[] temp = resolver.listTokenValues(tokens, new HashMap(criteria));
+ result.addAll(Arrays.asList(temp));
+ }
+
+ return (Map[]) result.toArray(new Map[result.size()]);
+ }
+
+ public void reportFailure() {
+ for (Iterator iter = chain.iterator(); iter.hasNext();) {
+ DependencyResolver resolver = (DependencyResolver) iter.next();
+ resolver.reportFailure();
+ }
+ }
+
+ public void reportFailure(Artifact art) {
+ for (Iterator iter = chain.iterator(); iter.hasNext();) {
+ DependencyResolver resolver = (DependencyResolver) iter.next();
+ resolver.reportFailure(art);
+ }
+ }
+
+ public DownloadReport download(Artifact[] artifacts, DownloadOptions options) {
+ List artifactsToDownload = new ArrayList(Arrays.asList(artifacts));
+ DownloadReport report = new DownloadReport();
+ for (Iterator iter = chain.iterator(); iter.hasNext() && !artifactsToDownload.isEmpty();) {
+ DependencyResolver resolver = (DependencyResolver) iter.next();
+ DownloadReport r = resolver.download(
+ (Artifact[]) artifactsToDownload.toArray(new Artifact[artifactsToDownload.size()]),
+ options);
+ ArtifactDownloadReport[] adr = r.getArtifactsReports();
+ for (int i = 0; i < adr.length; i++) {
+ if (adr[i].getDownloadStatus() != DownloadStatus.FAILED) {
+ artifactsToDownload.remove(adr[i].getArtifact());
+ report.addArtifactReport(adr[i]);
+ }
+ }
+ }
+ for (Iterator iter = artifactsToDownload.iterator(); iter.hasNext();) {
+ Artifact art = (Artifact) iter.next();
+ ArtifactDownloadReport adr = new ArtifactDownloadReport(art);
+ adr.setDownloadStatus(DownloadStatus.FAILED);
+ report.addArtifactReport(adr);
+ }
+ return report;
+ }
+
+ public List getResolvers() {
+ return chain;
+ }
+
+ public void publish(Artifact artifact, File src, boolean overwrite) throws IOException {
+
+ getFirstResolver().publish(artifact, src, overwrite);
+ }
+
+ public void abortPublishTransaction() throws IOException {
+ getFirstResolver().abortPublishTransaction();
+ }
+
+ public void beginPublishTransaction(ModuleRevisionId module, boolean overwrite)
+ throws IOException {
+ getFirstResolver().beginPublishTransaction(module, overwrite);
+ }
+
+ public void commitPublishTransaction() throws IOException {
+ getFirstResolver().commitPublishTransaction();
+ }
+
+ private DependencyResolver getFirstResolver() {
+ if (chain.isEmpty()) {
+ throw new IllegalStateException("invalid chain resolver with no sub resolver");
+ }
+ return ((DependencyResolver) chain.get(0));
+ }
+
+ public boolean isReturnFirst() {
+ return returnFirst;
+ }
+
+ public void setReturnFirst(boolean returnFirst) {
+ this.returnFirst = returnFirst;
+ }
+
+ public void dumpSettings() {
+ Message.verbose("\t" + getName() + " [chain] " + chain);
+ Message.debug("\t\treturn first: " + isReturnFirst());
+ Message.debug("\t\tdual: " + isDual());
+ for (Iterator iter = chain.iterator(); iter.hasNext();) {
+ DependencyResolver r = (DependencyResolver) iter.next();
+ Message.debug("\t\t-> " + r.getName());
+ }
+ }
+
+ public boolean exists(Artifact artifact) {
+ for (Iterator iter = chain.iterator(); iter.hasNext();) {
+ DependencyResolver resolver = (DependencyResolver) iter.next();
+ if (resolver.exists(artifact)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public ArtifactOrigin locate(Artifact artifact) {
+ for (Iterator iter = chain.iterator(); iter.hasNext();) {
+ DependencyResolver resolver = (DependencyResolver) iter.next();
+ ArtifactOrigin origin = resolver.locate(artifact);
+ if (!ArtifactOrigin.isUnknown(origin)) {
+ return origin;
+ }
+ }
+ return ArtifactOrigin.unkwnown(artifact);
+ }
+
+ public ArtifactDownloadReport download(ArtifactOrigin artifact, DownloadOptions options) {
+ for (Iterator iter = chain.iterator(); iter.hasNext();) {
+ DependencyResolver resolver = (DependencyResolver) iter.next();
+ ArtifactDownloadReport adr = resolver.download(artifact, options);
+ if (adr.getDownloadStatus() != DownloadStatus.FAILED) {
+ return adr;
+ }
+ }
+ ArtifactDownloadReport adr = new ArtifactDownloadReport(artifact.getArtifact());
+ adr.setDownloadStatus(DownloadStatus.FAILED);
+ return adr;
+ }
+
+ private static void setLatest(DependencyResolver resolver, LatestStrategy latest) {
+ if (resolver instanceof HasLatestStrategy) {
+ HasLatestStrategy r = (HasLatestStrategy) resolver;
+ r.setLatestStrategy(latest);
+ }
+ }
+
+ private static LatestStrategy getLatest(DependencyResolver resolver) {
+ if (resolver instanceof HasLatestStrategy) {
+ HasLatestStrategy r = (HasLatestStrategy) resolver;
+ return r.getLatestStrategy();
+ }
+ return null;
+ }
+
+ private static String getLatestStrategyName(DependencyResolver resolver) {
+ if (resolver instanceof HasLatestStrategy) {
+ HasLatestStrategy r = (HasLatestStrategy) resolver;
+ return r.getLatest();
+ }
+ return null;
+ }
+
+ public void setDual(boolean b) {
+ dual = b;
+ }
+
+ public boolean isDual() {
+ return dual;
+ }
+
+}
diff --git a/src/java/org/apache/ivy/plugins/resolver/DependencyResolver.java b/src/java/org/apache/ivy/plugins/resolver/DependencyResolver.java
new file mode 100644
index 0000000..c1ee376
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/resolver/DependencyResolver.java
@@ -0,0 +1,210 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.resolver;
+
+import java.io.File;
+import java.io.IOException;
+import java.text.ParseException;
+import java.util.Map;
+
+import org.apache.ivy.core.cache.ArtifactOrigin;
+import org.apache.ivy.core.cache.RepositoryCacheManager;
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.report.ArtifactDownloadReport;
+import org.apache.ivy.core.report.DownloadReport;
+import org.apache.ivy.core.resolve.DownloadOptions;
+import org.apache.ivy.core.resolve.ResolveData;
+import org.apache.ivy.core.resolve.ResolvedModuleRevision;
+import org.apache.ivy.core.search.ModuleEntry;
+import org.apache.ivy.core.search.OrganisationEntry;
+import org.apache.ivy.core.search.RevisionEntry;
+import org.apache.ivy.plugins.namespace.Namespace;
+import org.apache.ivy.plugins.resolver.util.ResolvedResource;
+
+/**
+ *
+ */
+public interface DependencyResolver {
+ String getName();
+
+ /**
+ * Should only be used by configurator
+ *
+ * @param name
+ * the new name of the resolver
+ */
+ void setName(String name);
+
+ /**
+ * Resolve a module by id, getting its module descriptor and resolving the revision if it's a
+ * latest one (i.e. a revision uniquely identifying the revision of a module in the current
+ * environment - If this revision is not able to identify uniquely the revision of the module
+ * outside of the current environment, then the resolved revision must begin by ##)
+ *
+ * @throws ParseException
+ */
+ ResolvedModuleRevision getDependency(DependencyDescriptor dd, ResolveData data)
+ throws ParseException;
+
+ /**
+ * Finds the module descriptor for the specified <tt>DependencyDescriptor</tt>. If this resolver
+ * can't find the module descriptor, <tt>null</tt> is returned.
+ *
+ * @param dd
+ * the dependency descriptor
+ * @param data
+ * the resolve data
+ * @return the module descriptor, or <tt>null</tt>
+ */
+ ResolvedResource findIvyFileRef(DependencyDescriptor dd, ResolveData data);
+
+ /**
+ * Download artifacts with specified DownloadOptions.
+ * <p>
+ * The resolver will always make a best effort, and do not stop when an artifact is not
+ * available. It rather continue to attempt to download other requested artifacts, and report
+ * what has been done in the returned DownloadReport.
+ * </p>
+ * <p>
+ * The returned DownloadReport is never <code>null</code>, and always contain an
+ * {@link ArtifactDownloadReport} for each requested Artifact.
+ * </p>
+ *
+ * @param artifacts
+ * an array of artifacts to download. Must not be <code>null</code>.
+ * @param options
+ * options to apply for this download. Must not be <code>null</code>.
+ * @return a DownloadReport with details about each Artifact download.
+ */
+ DownloadReport download(Artifact[] artifacts, DownloadOptions options);
+
+ /**
+ * Download an artifact according to the given DownloadOptions.
+ * <p>
+ * This methods is an alternative to {@link #download(Artifact[], DownloadOptions)}, which
+ * locates and downloads a set of artifacts. This method uses an {@link ArtifactOrigin}, and as
+ * such is only used to materialize an already located Artifact.
+ * </p>
+ *
+ * @param artifact
+ * the location of the artifact to download. Must not be <code>null</code>.
+ * @param options
+ * options to apply for this download. Must not be <code>null</code>.
+ * @return a report detailing how the download has gone, is never <code>null</code>.
+ */
+ ArtifactDownloadReport download(ArtifactOrigin artifact, DownloadOptions options);
+
+ /**
+ * Returns <code>true</code> if the given artifact can be located by this resolver and actually
+ * exist.
+ *
+ * @param artifact
+ * the artifact which should be tested.
+ * @return <code>true</code> if the given artifact can be located by this resolver and actually
+ * exist.
+ */
+ boolean exists(Artifact artifact);
+
+ /**
+ * Locates the given artifact and returns its location if it can be located by this resolver and
+ * if it actually exists, or <code>null</code> in other cases.
+ *
+ * @param artifact
+ * the artifact which should be located
+ * @return the artifact location, or <code>null</code> if it can't be located by this resolver
+ * or doesn't exist.
+ */
+ ArtifactOrigin locate(Artifact artifact);
+
+ void publish(Artifact artifact, File src, boolean overwrite) throws IOException;
+
+ void beginPublishTransaction(ModuleRevisionId module, boolean overwrite) throws IOException;
+
+ void abortPublishTransaction() throws IOException;
+
+ void commitPublishTransaction() throws IOException;
+
+ /**
+ * Reports last resolve failure as Messages
+ */
+ void reportFailure();
+
+ /**
+ * Reports last artifact download failure as Messages
+ *
+ * @param art
+ */
+ void reportFailure(Artifact art);
+
+ // listing methods, enable to know what is available from this resolver
+ // the listing methods must only list entries directly
+ // available from them, no recursion is needed as long as sub resolvers
+ // are registered in ivy too.
+
+ /**
+ * List all the values the given token can take if other tokens are set as described in the
+ * otherTokenValues map. For instance, if token = "revision" and the map contains
+ * "organisation"->"foo" "module"->"bar" The results will be the list of revisions of the module
+ * bar from the org foo.
+ * <p>
+ * Note that listing does not take into account namespaces, and return raw information without
+ * any namespace transformation. The caller is responsible for calling namespace transformation
+ * with the Namespace returned by {@link #getNamespace()}.
+ * </p>
+ */
+ String[] listTokenValues(String token, Map otherTokenValues);
+
+ /**
+ * Same as {@link #listTokenValues(String, Map)} but more generic.
+ *
+ * @param tokens
+ * the tokens of the query
+ * @param criteria
+ * the token which have values
+ * @return the list of token values (Map<Strin, String>[]), must not be <code>null</code>
+ */
+ Map[] listTokenValues(String[] tokens, Map criteria);
+
+ OrganisationEntry[] listOrganisations();
+
+ ModuleEntry[] listModules(OrganisationEntry org);
+
+ RevisionEntry[] listRevisions(ModuleEntry module);
+
+ /**
+ * Returns the namespace associated with this resolver.
+ *
+ * @return the namespace associated with this resolver.
+ */
+ Namespace getNamespace();
+
+ void dumpSettings();
+
+ void setSettings(ResolverSettings settings);
+
+ /**
+ * Returns the {@link RepositoryCacheManager} used to manage the repository cache associated
+ * with this dependency resolver.
+ *
+ * @return the {@link RepositoryCacheManager} used to manage the repository cache associated
+ * with this dependency resolver.
+ */
+ RepositoryCacheManager getRepositoryCacheManager();
+}
diff --git a/src/java/org/apache/ivy/plugins/resolver/DualResolver.java b/src/java/org/apache/ivy/plugins/resolver/DualResolver.java
new file mode 100644
index 0000000..e23b9ff
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/resolver/DualResolver.java
@@ -0,0 +1,218 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.resolver;
+
+import java.io.File;
+import java.io.IOException;
+import java.text.ParseException;
+import java.util.Arrays;
+
+import org.apache.ivy.core.cache.ArtifactOrigin;
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.report.ArtifactDownloadReport;
+import org.apache.ivy.core.report.DownloadReport;
+import org.apache.ivy.core.resolve.DownloadOptions;
+import org.apache.ivy.core.resolve.ResolveData;
+import org.apache.ivy.core.resolve.ResolvedModuleRevision;
+import org.apache.ivy.plugins.resolver.util.ResolvedResource;
+import org.apache.ivy.util.Message;
+
+/**
+ * DualResolver is used to resolve dependencies with one dependency revolver, called ivy resolver,
+ * and then download artifacts found in the resolved dependencies from a second dependency resolver,
+ * called artifact resolver. It is especially useful with resolvers using repository where there is
+ * a lot of artifact, but no ivy file, like the maven ibiblio repository. If no ivy file is found by
+ * the ivy resolver, the artifact resolver is used to check if there is artifact corresponding to
+ * the request (with default ivy file). For artifact download, however, only the artifact resolver
+ * is used. Exactly two resolvers should be added to this resolver for it to work properly. The
+ * first resolver added if the ivy resolver, the second is the artifact one.
+ */
+public class DualResolver extends AbstractResolver {
+ public static final String DESCRIPTOR_OPTIONAL = "optional";
+
+ public static final String DESCRIPTOR_REQUIRED = "required";
+
+ private DependencyResolver ivyResolver;
+
+ private DependencyResolver artifactResolver;
+
+ private boolean allownomd = true;
+
+ public void add(DependencyResolver resolver) {
+ if (ivyResolver == null) {
+ ivyResolver = resolver;
+ } else if (artifactResolver == null) {
+ artifactResolver = resolver;
+ } else {
+ throw new IllegalStateException(
+ "exactly two resolvers must be added: ivy(1) and artifact(2) one");
+ }
+ }
+
+ public ResolvedModuleRevision getDependency(DependencyDescriptor dd, ResolveData data)
+ throws ParseException {
+ if (ivyResolver == null || artifactResolver == null) {
+ throw new IllegalStateException(
+ "exactly two resolvers must be added: ivy(1) and artifact(2) one");
+ }
+ ResolvedModuleRevision resolved = data.getCurrentResolvedModuleRevision();
+
+ data = new ResolveData(data, doValidate(data));
+ final ResolvedModuleRevision mr = ivyResolver.getDependency(dd, data);
+ if (mr == null) {
+ checkInterrupted();
+ if (isAllownomd()) {
+ Message.verbose("ivy resolver didn't find " + dd
+ + ": trying with artifact resolver");
+ return artifactResolver.getDependency(dd, data);
+ } else {
+ return null;
+ }
+ } else {
+ if (mr == resolved) {
+ // nothing has actually been resolved here, we don't need to touch the returned rmr
+ return mr;
+ }
+ return new ResolvedModuleRevision(mr.getResolver(), this, mr.getDescriptor(),
+ mr.getReport(), mr.isForce());
+ }
+ }
+
+ public ResolvedResource findIvyFileRef(DependencyDescriptor dd, ResolveData data) {
+ return ivyResolver.findIvyFileRef(dd, data);
+ }
+
+ public void reportFailure() {
+ ivyResolver.reportFailure();
+ artifactResolver.reportFailure();
+ }
+
+ public void reportFailure(Artifact art) {
+ ivyResolver.reportFailure(art);
+ artifactResolver.reportFailure(art);
+ }
+
+ public DownloadReport download(Artifact[] artifacts, DownloadOptions options) {
+ return artifactResolver.download(artifacts, options);
+ }
+
+ public DependencyResolver getArtifactResolver() {
+ return artifactResolver;
+ }
+
+ public void setArtifactResolver(DependencyResolver artifactResolver) {
+ this.artifactResolver = artifactResolver;
+ }
+
+ public DependencyResolver getIvyResolver() {
+ return ivyResolver;
+ }
+
+ public void setIvyResolver(DependencyResolver ivyResolver) {
+ this.ivyResolver = ivyResolver;
+ }
+
+ public void publish(Artifact artifact, File src, boolean overwrite) throws IOException {
+ if ("ivy".equals(artifact.getType())) {
+ ivyResolver.publish(artifact, src, overwrite);
+ } else {
+ artifactResolver.publish(artifact, src, overwrite);
+ }
+ }
+
+ public void abortPublishTransaction() throws IOException {
+ ivyResolver.abortPublishTransaction();
+ artifactResolver.abortPublishTransaction();
+ }
+
+ public void beginPublishTransaction(ModuleRevisionId module, boolean overwrite)
+ throws IOException {
+ ivyResolver.beginPublishTransaction(module, overwrite);
+ artifactResolver.beginPublishTransaction(module, overwrite);
+ }
+
+ public void commitPublishTransaction() throws IOException {
+ ivyResolver.commitPublishTransaction();
+ artifactResolver.commitPublishTransaction();
+ }
+
+ public void dumpSettings() {
+ if (ivyResolver == null || artifactResolver == null) {
+ throw new IllegalStateException(
+ "exactly two resolvers must be added: ivy(1) and artifact(2) one");
+ }
+ Message.verbose("\t" + getName() + " [dual " + ivyResolver.getName() + " "
+ + artifactResolver.getName() + "]");
+ }
+
+ public boolean exists(Artifact artifact) {
+ if (artifact.isMetadata()) {
+ return ivyResolver.exists(artifact);
+ } else {
+ return artifactResolver.exists(artifact);
+ }
+ }
+
+ public ArtifactOrigin locate(Artifact artifact) {
+ if (artifact.isMetadata()) {
+ return ivyResolver.locate(artifact);
+ } else {
+ return artifactResolver.locate(artifact);
+ }
+ }
+
+ public ArtifactDownloadReport download(ArtifactOrigin artifact, DownloadOptions options) {
+ if (artifact.getArtifact().isMetadata()) {
+ return ivyResolver.download(artifact, options);
+ } else {
+ return artifactResolver.download(artifact, options);
+ }
+ }
+
+ public boolean isAllownomd() {
+ return allownomd;
+ }
+
+ public void setAllownomd(boolean allownomd) {
+ Message.deprecated("allownomd is deprecated, please use descriptor=\""
+ + (allownomd ? DESCRIPTOR_OPTIONAL : DESCRIPTOR_REQUIRED) + "\" instead");
+ this.allownomd = allownomd;
+ }
+
+ /**
+ * Sets the module descriptor presence rule. Should be one of {@link #DESCRIPTOR_REQUIRED} or
+ * {@link #DESCRIPTOR_OPTIONAL}.
+ *
+ * @param descriptorRule
+ * the descriptor rule to use with this resolver.
+ */
+ public void setDescriptor(String descriptorRule) {
+ if (DESCRIPTOR_REQUIRED.equals(descriptorRule)) {
+ allownomd = false;
+ } else if (DESCRIPTOR_OPTIONAL.equals(descriptorRule)) {
+ allownomd = true;
+ } else {
+ throw new IllegalArgumentException("unknown descriptor rule '" + descriptorRule
+ + "'. Allowed rules are: "
+ + Arrays.asList(new String[] {DESCRIPTOR_REQUIRED, DESCRIPTOR_OPTIONAL}));
+ }
+ }
+
+}
diff --git a/src/java/org/apache/ivy/plugins/resolver/FileSystemResolver.java b/src/java/org/apache/ivy/plugins/resolver/FileSystemResolver.java
new file mode 100644
index 0000000..a7cee6b
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/resolver/FileSystemResolver.java
@@ -0,0 +1,323 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.resolver;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.ivy.core.IvyPatternHelper;
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.settings.IvyPattern;
+import org.apache.ivy.plugins.repository.file.FileRepository;
+import org.apache.ivy.util.Checks;
+import org.apache.ivy.util.Message;
+
+/**
+ */
+public class FileSystemResolver extends RepositoryResolver {
+
+ private static final String TRANSACTION_DESTINATION_SUFFIX = ".part";
+
+ private static final Pattern TRANSACTION_PATTERN = Pattern
+ .compile("(.*[/\\\\]\\[revision\\])([/\\\\].+)");
+
+ /**
+ * Transactional mode.
+ *
+ * auto: use transaction if possible, only log verbose message if not true: always use
+ * transaction, fail if not supported false: never use transactions
+ */
+ private String transactional = "auto"; // one of 'auto', 'true' or 'false'
+
+ /**
+ * When set indicates if this resolver supports transaction
+ */
+ private Boolean supportTransaction;
+
+ /**
+ * The pattern leading to the directory where files are published before being moved at the end
+ * of a transaction
+ */
+ private String baseTransactionPattern;
+
+ /**
+ * Map between actual patterns and patterns used during the transaction to put files in a
+ * temporary directory
+ */
+ private Map/* <String,String> */fullTransactionPatterns = new HashMap();
+
+ /**
+ * Location where files are published during the transaction
+ */
+ private File transactionTempDir;
+
+ /**
+ * Location where files should end up at the end of the transaction
+ */
+ private File transactionDestDir;
+
+ public FileSystemResolver() {
+ setRepository(new FileRepository());
+ }
+
+ public String getTypeName() {
+ return "file";
+ }
+
+ public boolean isLocal() {
+ return getFileRepository().isLocal();
+ }
+
+ public void setLocal(boolean local) {
+ getFileRepository().setLocal(local);
+ }
+
+ private FileRepository getFileRepository() {
+ return (FileRepository) getRepository();
+ }
+
+ protected String getDestination(String pattern, Artifact artifact, ModuleRevisionId mrid) {
+ if (supportTransaction() && isTransactionStarted()) {
+
+ String destPattern = (String) fullTransactionPatterns.get(pattern);
+ if (destPattern == null) {
+ throw new IllegalArgumentException(
+ "unsupported pattern for publish destination pattern: " + pattern
+ + ". supported patterns: " + fullTransactionPatterns.keySet());
+ }
+ return IvyPatternHelper.substitute(destPattern, mrid, artifact);
+ } else {
+ return super.getDestination(pattern, artifact, mrid);
+ }
+ }
+
+ private boolean isTransactionStarted() {
+ return transactionTempDir != null;
+ }
+
+ public void abortPublishTransaction() throws IOException {
+ if (supportTransaction()) {
+ if (isTransactionStarted()) {
+ try {
+ getFileRepository().delete(transactionTempDir);
+ Message.info("\tpublish aborted: deleted " + transactionTempDir);
+ } finally {
+ closeTransaction();
+ }
+ } else {
+ Message.info("\tpublish aborted: nothing was started");
+ }
+ }
+ }
+
+ public void commitPublishTransaction() throws IOException {
+ if (supportTransaction()) {
+ if (!isTransactionStarted()) {
+ throw new IllegalStateException("no current transaction!");
+ }
+ if (transactionDestDir.exists()) {
+ throw new IOException(
+ "impossible to commit transaction: transaction destination directory "
+ + "already exists: "
+ + transactionDestDir
+ + "\npossible cause: usage of identifying tokens after the revision token");
+ }
+ try {
+ getFileRepository().move(transactionTempDir, transactionDestDir);
+
+ Message.info("\tpublish commited: moved " + transactionTempDir + " \n\t\tto "
+ + transactionDestDir);
+ } catch (IOException ex) {
+ IOException commitEx;
+ try {
+ getFileRepository().delete(transactionTempDir);
+ commitEx = new IOException("publish transaction commit error for "
+ + transactionDestDir + ": rolled back");
+ } catch (IOException deleteEx) {
+ commitEx = new IOException("publish transaction commit error for "
+ + transactionDestDir + ": rollback impossible either, "
+ + "please remove " + transactionTempDir + " manually");
+ }
+ commitEx.initCause(ex);
+ throw commitEx;
+ } finally {
+ closeTransaction();
+ }
+ }
+ }
+
+ public void beginPublishTransaction(ModuleRevisionId module, boolean overwrite)
+ throws IOException {
+ if (supportTransaction()) {
+ if (isTransactionStarted()) {
+ throw new IllegalStateException("a transaction is already started and not closed!");
+ }
+ if (overwrite) {
+ unsupportedTransaction("overwrite transaction not supported yet");
+ } else {
+ initTransaction(module);
+ if (transactionDestDir.exists()) {
+ unsupportedTransaction("transaction destination directory already exists: "
+ + transactionDestDir
+ + "\npossible cause: usage of identifying tokens after the revision token");
+ closeTransaction();
+ } else {
+ Message.verbose("\tstarting transaction: publish during transaction will be done in \n\t\t"
+ + transactionTempDir
+ + "\n\tand on commit moved to \n\t\t"
+ + transactionDestDir);
+ }
+ }
+ }
+ }
+
+ protected Collection filterNames(Collection values) {
+ if (supportTransaction()) {
+ values = super.filterNames(values);
+ for (Iterator iterator = values.iterator(); iterator.hasNext();) {
+ String v = (String) iterator.next();
+ if (v.endsWith(TRANSACTION_DESTINATION_SUFFIX)) {
+ iterator.remove();
+ }
+ }
+ return values;
+ } else {
+ return super.filterNames(values);
+ }
+ }
+
+ public boolean supportTransaction() {
+ if ("false".equals(transactional)) {
+ return false;
+ }
+ checkSupportTransaction();
+ return supportTransaction.booleanValue();
+ }
+
+ private void closeTransaction() {
+ transactionTempDir = null;
+ transactionDestDir = null;
+ }
+
+ private void checkSupportTransaction() {
+ if (supportTransaction == null) {
+ supportTransaction = Boolean.FALSE;
+ List ivyPatterns = getIvyPatterns();
+ List artifactPatterns = getArtifactPatterns();
+
+ if (ivyPatterns.size() > 0) {
+ String pattern = (String) ivyPatterns.get(0);
+ Matcher m = TRANSACTION_PATTERN.matcher(pattern);
+ if (!m.matches()) {
+ unsupportedTransaction("ivy pattern does not use revision as a directory");
+ return;
+ } else {
+ baseTransactionPattern = m.group(1);
+ fullTransactionPatterns.put(pattern, m.group(1)
+ + TRANSACTION_DESTINATION_SUFFIX + m.group(2));
+ }
+ }
+ if (artifactPatterns.size() > 0) {
+ String pattern = (String) artifactPatterns.get(0);
+ Matcher m = TRANSACTION_PATTERN.matcher(pattern);
+ if (!m.matches()) {
+ unsupportedTransaction("artifact pattern does not use revision as a directory");
+ return;
+ } else if (baseTransactionPattern != null) {
+ if (!baseTransactionPattern.equals(m.group(1))) {
+ unsupportedTransaction("ivy pattern and artifact pattern "
+ + "do not use the same directory for revision");
+ return;
+ } else {
+ fullTransactionPatterns.put(pattern, m.group(1)
+ + TRANSACTION_DESTINATION_SUFFIX + m.group(2));
+ }
+ } else {
+ baseTransactionPattern = m.group(1);
+ fullTransactionPatterns.put(pattern, m.group(1)
+ + TRANSACTION_DESTINATION_SUFFIX + m.group(2));
+ }
+ }
+ supportTransaction = Boolean.TRUE;
+ }
+ }
+
+ private void unsupportedTransaction(String msg) {
+ String fullMsg = getName() + " do not support transaction. " + msg;
+ if ("true".equals(transactional)) {
+ throw new IllegalStateException(fullMsg
+ + ". Set transactional attribute to 'auto' or 'false' or fix the problem.");
+ } else {
+ Message.verbose(fullMsg);
+ supportTransaction = Boolean.FALSE;
+ }
+ }
+
+ private void initTransaction(ModuleRevisionId module) {
+ ModuleRevisionId mrid = module;
+ if (isM2compatible()) {
+ mrid = convertM2IdForResourceSearch(module);
+ }
+
+ transactionTempDir = Checks.checkAbsolute(
+ IvyPatternHelper.substitute(
+ baseTransactionPattern,
+ ModuleRevisionId.newInstance(mrid, mrid.getRevision()
+ + TRANSACTION_DESTINATION_SUFFIX)), "baseTransactionPattern");
+ transactionDestDir = Checks.checkAbsolute(
+ IvyPatternHelper.substitute(baseTransactionPattern, mrid), "baseTransactionPattern");
+ }
+
+ public String getTransactional() {
+ return transactional;
+ }
+
+ public void setTransactional(String transactional) {
+ this.transactional = transactional;
+ }
+
+ public void addConfiguredIvy(IvyPattern p) {
+ File file = Checks.checkAbsolute(p.getPattern(), "ivy pattern");
+ p.setPattern(file.getAbsolutePath());
+ super.addConfiguredIvy(p);
+ }
+
+ public void addIvyPattern(String pattern) {
+ File file = Checks.checkAbsolute(pattern, "ivy pattern");
+ super.addIvyPattern(file.getAbsolutePath());
+ }
+
+ public void addConfiguredArtifact(IvyPattern p) {
+ File file = Checks.checkAbsolute(p.getPattern(), "artifact pattern");
+ p.setPattern(file.getAbsolutePath());
+ super.addConfiguredArtifact(p);
+ }
+
+ public void addArtifactPattern(String pattern) {
+ File file = Checks.checkAbsolute(pattern, "artifact pattern");
+ super.addArtifactPattern(file.getAbsolutePath());
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/resolver/IBiblioResolver.java b/src/java/org/apache/ivy/plugins/resolver/IBiblioResolver.java
new file mode 100644
index 0000000..8072e3f
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/resolver/IBiblioResolver.java
@@ -0,0 +1,554 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.resolver;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.apache.ivy.core.IvyPatternHelper;
+import org.apache.ivy.core.cache.ArtifactOrigin;
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.DefaultArtifact;
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.report.DownloadReport;
+import org.apache.ivy.core.resolve.DownloadOptions;
+import org.apache.ivy.core.resolve.ResolveData;
+import org.apache.ivy.core.resolve.ResolvedModuleRevision;
+import org.apache.ivy.core.search.ModuleEntry;
+import org.apache.ivy.core.search.OrganisationEntry;
+import org.apache.ivy.core.search.RevisionEntry;
+import org.apache.ivy.plugins.matcher.PatternMatcher;
+import org.apache.ivy.plugins.repository.Repository;
+import org.apache.ivy.plugins.repository.Resource;
+import org.apache.ivy.plugins.resolver.util.ResolvedResource;
+import org.apache.ivy.util.ContextualSAXHandler;
+import org.apache.ivy.util.Message;
+import org.apache.ivy.util.XMLHelper;
+import org.xml.sax.SAXException;
+
+/**
+ * IBiblioResolver is a resolver which can be used to resolve dependencies found in the ibiblio
+ * maven repository, or similar repositories.
+ * <p>
+ * For more flexibility with url and patterns, see
+ * {@link org.apache.ivy.plugins.resolver.URLResolver}.
+ */
+public class IBiblioResolver extends URLResolver {
+ private static final String M2_PER_MODULE_PATTERN = "[revision]/[artifact]-[revision](-[classifier]).[ext]";
+
+ private static final String M2_PATTERN = "[organisation]/[module]/" + M2_PER_MODULE_PATTERN;
+
+ public static final String DEFAULT_PATTERN = "[module]/[type]s/[artifact]-[revision].[ext]";
+
+ public static final String DEFAULT_ROOT = "http://www.ibiblio.org/maven/";
+
+ public static final String DEFAULT_M2_ROOT = "https://repo1.maven.org/maven2/";
+
+ private String root = null;
+
+ private String pattern = null;
+
+ // use poms if m2 compatible is true
+ private boolean usepoms = true;
+
+ // use maven-metadata.xml is exists to list revisions
+ private boolean useMavenMetadata = true;
+
+ public IBiblioResolver() {
+ // SNAPSHOT revisions are changing revisions
+ setChangingMatcher(PatternMatcher.REGEXP);
+ setChangingPattern(".*-SNAPSHOT");
+ }
+
+ public ResolvedResource findIvyFileRef(DependencyDescriptor dd, ResolveData data) {
+ if (isM2compatible() && isUsepoms()) {
+ ModuleRevisionId mrid = dd.getDependencyRevisionId();
+ mrid = convertM2IdForResourceSearch(mrid);
+
+ ResolvedResource rres = null;
+ if (dd.getDependencyRevisionId().getRevision().endsWith("SNAPSHOT")) {
+ rres = findSnapshotDescriptor(dd, data, mrid);
+ if (rres != null) {
+ return rres;
+ }
+ }
+
+ rres = findResourceUsingPatterns(mrid, getIvyPatterns(),
+ DefaultArtifact.newPomArtifact(mrid, data.getDate()), getRMDParser(dd, data),
+ data.getDate());
+ return rres;
+ } else {
+ return null;
+ }
+ }
+
+ public ResolvedResource findArtifactRef(Artifact artifact, Date date) {
+ ensureConfigured(getSettings());
+ ModuleRevisionId mrid = artifact.getModuleRevisionId();
+ if (isM2compatible()) {
+ mrid = convertM2IdForResourceSearch(mrid);
+ }
+ ResolvedResource rres = null;
+ if (artifact.getId().getRevision().endsWith("SNAPSHOT") && isM2compatible()) {
+ rres = findSnapshotArtifact(artifact, date, mrid);
+ if (rres != null) {
+ return rres;
+ }
+ }
+ return findResourceUsingPatterns(mrid, getArtifactPatterns(), artifact,
+ getDefaultRMDParser(artifact.getModuleRevisionId().getModuleId()), date);
+ }
+
+ private ResolvedResource findSnapshotArtifact(Artifact artifact, Date date,
+ ModuleRevisionId mrid) {
+ String rev = findSnapshotVersion(mrid);
+ if (rev != null) {
+ // replace the revision token in file name with the resolved revision
+ String pattern = getWholePattern().replaceFirst("\\-\\[revision\\]", "-" + rev);
+ return findResourceUsingPattern(mrid, pattern, artifact, getDefaultRMDParser(artifact
+ .getModuleRevisionId().getModuleId()), date);
+ }
+ return null;
+ }
+
+ private ResolvedResource findSnapshotDescriptor(DependencyDescriptor dd, ResolveData data,
+ ModuleRevisionId mrid) {
+ String rev = findSnapshotVersion(mrid);
+ if (rev != null) {
+ // here it would be nice to be able to store the resolved snapshot version, to avoid
+ // having to follow the same process to download artifacts
+
+ Message.verbose("[" + rev + "] " + mrid);
+
+ // replace the revision token in file name with the resolved revision
+ String pattern = getWholePattern().replaceFirst("\\-\\[revision\\]", "-" + rev);
+ return findResourceUsingPattern(mrid, pattern,
+ DefaultArtifact.newPomArtifact(mrid, data.getDate()), getRMDParser(dd, data),
+ data.getDate());
+ }
+ return null;
+ }
+
+ private String findSnapshotVersion(ModuleRevisionId mrid) {
+ if (!isM2compatible()) {
+ return null;
+ }
+
+ if (shouldUseMavenMetadata(getWholePattern())) {
+ InputStream metadataStream = null;
+ try {
+ String metadataLocation = IvyPatternHelper.substitute(root
+ + "[organisation]/[module]/[revision]/maven-metadata.xml", mrid);
+ Resource metadata = getRepository().getResource(metadataLocation);
+ if (metadata.exists()) {
+ metadataStream = metadata.openStream();
+ final StringBuffer timestamp = new StringBuffer();
+ final StringBuffer buildNumer = new StringBuffer();
+ XMLHelper.parse(metadataStream, null, new ContextualSAXHandler() {
+ public void endElement(String uri, String localName, String qName)
+ throws SAXException {
+ if ("metadata/versioning/snapshot/timestamp".equals(getContext())) {
+ timestamp.append(getText());
+ }
+ if ("metadata/versioning/snapshot/buildNumber".equals(getContext())) {
+ buildNumer.append(getText());
+ }
+ super.endElement(uri, localName, qName);
+ }
+ }, null);
+ if (timestamp.length() > 0) {
+ // we have found a timestamp, so this is a snapshot unique version
+ String rev = mrid.getRevision();
+ rev = rev.substring(0, rev.length() - "SNAPSHOT".length());
+ rev = rev + timestamp.toString() + "-" + buildNumer.toString();
+
+ return rev;
+ }
+ } else {
+ Message.verbose("\tmaven-metadata not available: " + metadata);
+ }
+ } catch (IOException e) {
+ Message.verbose("impossible to access maven metadata file, ignored", e);
+ } catch (SAXException e) {
+ Message.verbose("impossible to parse maven metadata file, ignored", e);
+ } catch (ParserConfigurationException e) {
+ Message.verbose("impossible to parse maven metadata file, ignored", e);
+ } finally {
+ if (metadataStream != null) {
+ try {
+ metadataStream.close();
+ } catch (IOException e) {
+ // ignored
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ public void setM2compatible(boolean m2compatible) {
+ super.setM2compatible(m2compatible);
+ if (m2compatible) {
+ if (root == null) {
+ root = DEFAULT_M2_ROOT;
+ }
+ if (pattern == null) {
+ pattern = M2_PATTERN;
+ }
+ updateWholePattern();
+ }
+ }
+
+ public void ensureConfigured(ResolverSettings settings) {
+ if (settings != null && (root == null || pattern == null)) {
+ if (root == null) {
+ String root = settings.getVariable("ivy.ibiblio.default.artifact.root");
+ if (root != null) {
+ this.root = root;
+ } else {
+ settings.configureRepositories(true);
+ this.root = settings.getVariable("ivy.ibiblio.default.artifact.root");
+ }
+ }
+ if (pattern == null) {
+ String pattern = settings.getVariable("ivy.ibiblio.default.artifact.pattern");
+ if (pattern != null) {
+ this.pattern = pattern;
+ } else {
+ settings.configureRepositories(false);
+ this.pattern = settings.getVariable("ivy.ibiblio.default.artifact.pattern");
+ }
+ }
+ updateWholePattern();
+ }
+ }
+
+ protected String getModuleDescriptorExtension() {
+ return "pom";
+ }
+
+ private String getWholePattern() {
+ return root + pattern;
+ }
+
+ public String getPattern() {
+ return pattern;
+ }
+
+ public void setPattern(String pattern) {
+ if (pattern == null) {
+ throw new NullPointerException("pattern must not be null");
+ }
+ this.pattern = pattern;
+ ensureConfigured(getSettings());
+ updateWholePattern();
+ }
+
+ public String getRoot() {
+ return root;
+ }
+
+ /**
+ * Sets the root of the maven like repository. The maven like repository is necessarily an http
+ * repository.
+ *
+ * @param root
+ * the root of the maven like repository
+ * @throws IllegalArgumentException
+ * if root does not start with "http://"
+ */
+ public void setRoot(String root) {
+ if (root == null) {
+ throw new NullPointerException("root must not be null");
+ }
+ if (!root.endsWith("/")) {
+ this.root = root + "/";
+ } else {
+ this.root = root;
+ }
+ ensureConfigured(getSettings());
+ updateWholePattern();
+ }
+
+ private void updateWholePattern() {
+ if (isM2compatible() && isUsepoms()) {
+ setIvyPatterns(Collections.singletonList(getWholePattern()));
+ } else {
+ setIvyPatterns(Collections.EMPTY_LIST);
+ }
+ setArtifactPatterns(Collections.singletonList(getWholePattern()));
+ }
+
+ public void publish(Artifact artifact, File src) {
+ throw new UnsupportedOperationException("publish not supported by IBiblioResolver");
+ }
+
+ // we do not allow to list organisations on ibiblio, nor modules in ibiblio 1
+ public String[] listTokenValues(String token, Map otherTokenValues) {
+ if (IvyPatternHelper.ORGANISATION_KEY.equals(token)) {
+ return new String[0];
+ }
+ if (IvyPatternHelper.MODULE_KEY.equals(token) && !isM2compatible()) {
+ return new String[0];
+ }
+ ensureConfigured(getSettings());
+ return super.listTokenValues(token, otherTokenValues);
+ }
+
+ protected String[] listTokenValues(String pattern, String token) {
+ if (IvyPatternHelper.ORGANISATION_KEY.equals(token)) {
+ return new String[0];
+ }
+ if (IvyPatternHelper.MODULE_KEY.equals(token) && !isM2compatible()) {
+ return new String[0];
+ }
+ ensureConfigured(getSettings());
+
+ // let's see if we should use maven metadata for this listing...
+ if (IvyPatternHelper.REVISION_KEY.equals(token)
+ && shouldUseMavenMetadata(getWholePattern())) {
+ // now we must use metadata if available
+ /*
+ * we substitute tokens with ext token only in the m2 per module pattern, to match has
+ * has been done in the given pattern
+ */
+ String partiallyResolvedM2PerModulePattern = IvyPatternHelper.substituteTokens(
+ M2_PER_MODULE_PATTERN, Collections.singletonMap(IvyPatternHelper.EXT_KEY, "pom"));
+ if (pattern.endsWith(partiallyResolvedM2PerModulePattern)) {
+ /*
+ * the given pattern already contain resolved org and module, we just have to
+ * replace the per module pattern at the end by 'maven-metadata.xml' to have the
+ * maven metadata file location
+ */
+ String metadataLocation = pattern.substring(0,
+ pattern.lastIndexOf(partiallyResolvedM2PerModulePattern))
+ + "maven-metadata.xml";
+ List revs = listRevisionsWithMavenMetadata(getRepository(), metadataLocation);
+ if (revs != null) {
+ return (String[]) revs.toArray(new String[revs.size()]);
+ }
+ } else {
+ /*
+ * this is probably because the given pattern has been substituted with jar ext, if
+ * this resolver has optional module descriptors. But since we have to use maven
+ * metadata, we don't care about this case, maven metadata has already been used
+ * when looking for revisions with the pattern substituted with ext=xml for the
+ * "ivy" pattern.
+ */
+ return new String[0];
+ }
+ }
+ return super.listTokenValues(pattern, token);
+ }
+
+ public OrganisationEntry[] listOrganisations() {
+ return new OrganisationEntry[0];
+ }
+
+ public ModuleEntry[] listModules(OrganisationEntry org) {
+ if (isM2compatible()) {
+ ensureConfigured(getSettings());
+ return super.listModules(org);
+ }
+ return new ModuleEntry[0];
+ }
+
+ public RevisionEntry[] listRevisions(ModuleEntry mod) {
+ ensureConfigured(getSettings());
+ return super.listRevisions(mod);
+ }
+
+ protected ResolvedResource[] listResources(Repository repository, ModuleRevisionId mrid,
+ String pattern, Artifact artifact) {
+ if (shouldUseMavenMetadata(pattern)) {
+ List revs = listRevisionsWithMavenMetadata(repository, mrid.getModuleId()
+ .getAttributes());
+ if (revs != null) {
+ Message.debug("\tfound revs: " + revs);
+ List rres = new ArrayList();
+ for (Iterator iter = revs.iterator(); iter.hasNext();) {
+ String rev = (String) iter.next();
+ ModuleRevisionId historicalMrid = ModuleRevisionId.newInstance(mrid, rev);
+
+ String patternForRev = pattern;
+ if (rev.endsWith("SNAPSHOT")) {
+ String snapshotVersion = findSnapshotVersion(historicalMrid);
+ if (snapshotVersion != null) {
+ patternForRev = pattern.replaceFirst("\\-\\[revision\\]", "-"
+ + snapshotVersion);
+ }
+ }
+ String resolvedPattern = IvyPatternHelper.substitute(patternForRev,
+ historicalMrid, artifact);
+ try {
+ Resource res = repository.getResource(resolvedPattern);
+ if (res != null) {
+ // we do not test if the resource actually exist here, it would cause
+ // a lot of checks which are not always necessary depending on the usage
+ // which is done of the returned ResolvedResource array
+ rres.add(new ResolvedResource(res, rev));
+ }
+ } catch (IOException e) {
+ Message.warn(
+ "impossible to get resource from name listed by maven-metadata.xml:"
+ + rres, e);
+ }
+ }
+ return (ResolvedResource[]) rres.toArray(new ResolvedResource[rres.size()]);
+ } else {
+ // maven metadata not available or something went wrong,
+ // use default listing capability
+ return super.listResources(repository, mrid, pattern, artifact);
+ }
+ } else {
+ return super.listResources(repository, mrid, pattern, artifact);
+ }
+ }
+
+ private List listRevisionsWithMavenMetadata(Repository repository, Map tokenValues) {
+ String metadataLocation = IvyPatternHelper.substituteTokens(root
+ + "[organisation]/[module]/maven-metadata.xml", tokenValues);
+ return listRevisionsWithMavenMetadata(repository, metadataLocation);
+ }
+
+ private List listRevisionsWithMavenMetadata(Repository repository, String metadataLocation) {
+ List revs = null;
+ InputStream metadataStream = null;
+ try {
+ Resource metadata = repository.getResource(metadataLocation);
+ if (metadata.exists()) {
+ Message.verbose("\tlisting revisions from maven-metadata: " + metadata);
+ final List metadataRevs = new ArrayList();
+ metadataStream = metadata.openStream();
+ XMLHelper.parse(metadataStream, null, new ContextualSAXHandler() {
+ public void endElement(String uri, String localName, String qName)
+ throws SAXException {
+ if ("metadata/versioning/versions/version".equals(getContext())) {
+ metadataRevs.add(getText().trim());
+ }
+ super.endElement(uri, localName, qName);
+ }
+ }, null);
+ revs = metadataRevs;
+ } else {
+ Message.verbose("\tmaven-metadata not available: " + metadata);
+ }
+ } catch (IOException e) {
+ Message.verbose("impossible to access maven metadata file, ignored", e);
+ } catch (SAXException e) {
+ Message.verbose("impossible to parse maven metadata file, ignored", e);
+ } catch (ParserConfigurationException e) {
+ Message.verbose("impossible to parse maven metadata file, ignored", e);
+ } finally {
+ if (metadataStream != null) {
+ try {
+ metadataStream.close();
+ } catch (IOException e) {
+ // ignored
+ }
+ }
+ }
+ return revs;
+ }
+
+ protected void findTokenValues(Collection names, List patterns, Map tokenValues, String token) {
+ if (IvyPatternHelper.REVISION_KEY.equals(token)) {
+ if (shouldUseMavenMetadata(getWholePattern())) {
+ List revs = listRevisionsWithMavenMetadata(getRepository(), tokenValues);
+ if (revs != null) {
+ names.addAll(filterNames(revs));
+ return;
+ }
+ }
+ }
+ super.findTokenValues(names, patterns, tokenValues, token);
+ }
+
+ private boolean shouldUseMavenMetadata(String pattern) {
+ return isUseMavenMetadata() && isM2compatible() && pattern.endsWith(M2_PATTERN);
+ }
+
+ public String getTypeName() {
+ return "ibiblio";
+ }
+
+ // override some methods to ensure configuration
+ public ResolvedModuleRevision getDependency(DependencyDescriptor dd, ResolveData data)
+ throws ParseException {
+ ensureConfigured(data.getSettings());
+ return super.getDependency(dd, data);
+ }
+
+ public DownloadReport download(Artifact[] artifacts, DownloadOptions options) {
+ ensureConfigured(getSettings());
+ return super.download(artifacts, options);
+ }
+
+ public boolean exists(Artifact artifact) {
+ ensureConfigured(getSettings());
+ return super.exists(artifact);
+ }
+
+ public ArtifactOrigin locate(Artifact artifact) {
+ ensureConfigured(getSettings());
+ return super.locate(artifact);
+ }
+
+ public List getArtifactPatterns() {
+ ensureConfigured(getSettings());
+ return super.getArtifactPatterns();
+ }
+
+ public boolean isUsepoms() {
+ return usepoms;
+ }
+
+ public void setUsepoms(boolean usepoms) {
+ this.usepoms = usepoms;
+ updateWholePattern();
+ }
+
+ public boolean isUseMavenMetadata() {
+ return useMavenMetadata;
+ }
+
+ public void setUseMavenMetadata(boolean useMavenMetadata) {
+ this.useMavenMetadata = useMavenMetadata;
+ }
+
+ public void dumpSettings() {
+ ensureConfigured(getSettings());
+ super.dumpSettings();
+ Message.debug("\t\troot: " + getRoot());
+ Message.debug("\t\tpattern: " + getPattern());
+ Message.debug("\t\tusepoms: " + usepoms);
+ Message.debug("\t\tuseMavenMetadata: " + useMavenMetadata);
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/resolver/IvyRepResolver.java b/src/java/org/apache/ivy/plugins/resolver/IvyRepResolver.java
new file mode 100644
index 0000000..12e1e2b
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/resolver/IvyRepResolver.java
@@ -0,0 +1,309 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.resolver;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.ivy.core.IvyPatternHelper;
+import org.apache.ivy.core.cache.ArtifactOrigin;
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
+import org.apache.ivy.core.report.DownloadReport;
+import org.apache.ivy.core.resolve.DownloadOptions;
+import org.apache.ivy.core.resolve.ResolveData;
+import org.apache.ivy.core.resolve.ResolvedModuleRevision;
+import org.apache.ivy.core.search.ModuleEntry;
+import org.apache.ivy.core.search.OrganisationEntry;
+import org.apache.ivy.core.search.RevisionEntry;
+import org.apache.ivy.plugins.resolver.util.ResolvedResource;
+import org.apache.ivy.util.Message;
+import org.apache.ivy.util.XMLHelper;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+/**
+ * IvyRepResolver is a resolver which can be used to resolve dependencies found in the ivy official
+ * repository for ivy files and ibiblio maven repository for the artifacts, or similar repositories.
+ * For more flexibility with url and patterns, see
+ * {@link org.apache.ivy.plugins.resolver.URLResolver}.
+ */
+public class IvyRepResolver extends URLResolver {
+ public static final String DEFAULT_IVYPATTERN = "[organisation]/[module]/ivy-[revision].xml";
+
+ public static final String DEFAULT_IVYROOT = "http://ivyrep.jayasoft.org/";
+
+ private String ivyroot = null;
+
+ private String ivypattern = null;
+
+ private String artroot = null;
+
+ private String artpattern = null;
+
+ public IvyRepResolver() {
+ }
+
+ private void ensureArtifactConfigured(ResolverSettings settings) {
+ if (settings != null && (artroot == null || artpattern == null)) {
+ if (artroot == null) {
+ String root = settings.getVariable("ivy.ivyrep.default.artifact.root");
+ if (root != null) {
+ artroot = root;
+ } else {
+ settings.configureRepositories(true);
+ artroot = settings.getVariable("ivy.ivyrep.default.artifact.root");
+ }
+ }
+ if (artpattern == null) {
+ String pattern = settings.getVariable("ivy.ivyrep.default.artifact.pattern");
+ if (pattern != null) {
+ artpattern = pattern;
+ } else {
+ settings.configureRepositories(false);
+ artpattern = settings.getVariable("ivy.ivyrep.default.artifact.pattern");
+ }
+ }
+ updateWholeArtPattern();
+ }
+ }
+
+ private void ensureIvyConfigured(ResolverSettings settings) {
+ if (settings != null && (ivyroot == null || ivypattern == null)) {
+ if (ivyroot == null) {
+ String root = settings.getVariable("ivy.ivyrep.default.ivy.root");
+ if (root != null) {
+ ivyroot = root;
+ } else {
+ throw new IllegalStateException("ivyroot is mandatory on IvyRepResolver. "
+ + "Make sure to set it in your settings, before setting ivypattern "
+ + "if you wish to set ivypattern too.");
+ }
+ }
+ if (ivypattern == null) {
+ String pattern = settings.getVariable("ivy.ivyrep.default.ivy.pattern");
+ if (pattern != null) {
+ ivypattern = pattern;
+ } else {
+ settings.configureRepositories(false);
+ ivypattern = settings.getVariable("ivy.ivyrep.default.ivy.pattern");
+ }
+ }
+ updateWholeIvyPattern();
+ }
+ }
+
+ private String getWholeIvyPattern() {
+ if (ivyroot == null || ivypattern == null) {
+ return null;
+ }
+ return ivyroot + ivypattern;
+ }
+
+ private String getWholeArtPattern() {
+ return artroot + artpattern;
+ }
+
+ public String getIvypattern() {
+ return ivypattern;
+ }
+
+ public void setIvypattern(String pattern) {
+ if (pattern == null) {
+ throw new NullPointerException("pattern must not be null");
+ }
+ ivypattern = pattern;
+ ensureIvyConfigured(getSettings());
+ updateWholeIvyPattern();
+ }
+
+ public String getIvyroot() {
+ return ivyroot;
+ }
+
+ /**
+ * Sets the root of the maven like repository. The maven like repository is necessarily an http
+ * repository.
+ *
+ * @param root
+ * the root of the maven like repository
+ * @throws IllegalArgumentException
+ * if root does not start with "http://"
+ */
+ public void setIvyroot(String root) {
+ if (root == null) {
+ throw new NullPointerException("root must not be null");
+ }
+ if (!root.endsWith("/")) {
+ ivyroot = root + "/";
+ } else {
+ ivyroot = root;
+ }
+ ensureIvyConfigured(getSettings());
+ updateWholeIvyPattern();
+ }
+
+ public void setM2compatible(boolean m2compatible) {
+ if (m2compatible) {
+ throw new IllegalArgumentException(
+ "ivyrep does not support maven2 compatibility. "
+ + "Please use ibiblio resolver instead, or even url or filesystem resolvers for"
+ + " more specific needs.");
+ }
+ }
+
+ private void updateWholeIvyPattern() {
+ setIvyPatterns(Collections.singletonList(getWholeIvyPattern()));
+ }
+
+ private void updateWholeArtPattern() {
+ setArtifactPatterns(Collections.singletonList(getWholeArtPattern()));
+ }
+
+ public void publish(Artifact artifact, File src) {
+ throw new UnsupportedOperationException("publish not supported by IBiblioResolver");
+ }
+
+ public String getArtroot() {
+ return artroot;
+ }
+
+ public String getArtpattern() {
+ return artpattern;
+ }
+
+ public void setArtpattern(String pattern) {
+ if (pattern == null) {
+ throw new NullPointerException("pattern must not be null");
+ }
+ artpattern = pattern;
+ ensureArtifactConfigured(getSettings());
+ updateWholeArtPattern();
+ }
+
+ public void setArtroot(String root) {
+ if (root == null) {
+ throw new NullPointerException("root must not be null");
+ }
+ if (!root.endsWith("/")) {
+ artroot = root + "/";
+ } else {
+ artroot = root;
+ }
+ ensureArtifactConfigured(getSettings());
+ updateWholeArtPattern();
+ }
+
+ public OrganisationEntry[] listOrganisations() {
+ ensureIvyConfigured(getSettings());
+ try {
+ URL content = new URL(ivyroot + "content.xml");
+ final List ret = new ArrayList();
+ XMLHelper.parse(content, null, new DefaultHandler() {
+ public void startElement(String uri, String localName, String qName,
+ org.xml.sax.Attributes attributes) throws SAXException {
+ if ("organisation".equals(qName)) {
+ String org = attributes.getValue("name");
+ if (org != null) {
+ ret.add(new OrganisationEntry(IvyRepResolver.this, org));
+ }
+ }
+ }
+ });
+ return (OrganisationEntry[]) ret.toArray(new OrganisationEntry[ret.size()]);
+ } catch (MalformedURLException e) {
+ // ???
+ } catch (Exception e) {
+ Message.warn("unable to parse content.xml file on ivyrep", e);
+ }
+ return super.listOrganisations();
+ }
+
+ // overwrite parent to use only ivy patterns (and not artifact ones, cause ibiblio is too slow
+ // to answer)
+ public ModuleEntry[] listModules(OrganisationEntry org) {
+ ensureIvyConfigured(getSettings());
+ Map tokenValues = new HashMap();
+ tokenValues.put(IvyPatternHelper.ORGANISATION_KEY, org.getOrganisation());
+ Collection names = findIvyNames(tokenValues, IvyPatternHelper.MODULE_KEY);
+ ModuleEntry[] ret = new ModuleEntry[names.size()];
+ int i = 0;
+ for (Iterator iter = names.iterator(); iter.hasNext(); i++) {
+ String name = (String) iter.next();
+ ret[i] = new ModuleEntry(org, name);
+ }
+ return ret;
+ }
+
+ public RevisionEntry[] listRevisions(ModuleEntry mod) {
+ ensureIvyConfigured(getSettings());
+ ensureArtifactConfigured(getSettings());
+ return super.listRevisions(mod);
+ }
+
+ public String getTypeName() {
+ return "ivyrep";
+ }
+
+ // override some methods to ensure configuration
+ public ResolvedModuleRevision getDependency(DependencyDescriptor dd, ResolveData data)
+ throws ParseException {
+ ensureIvyConfigured(data.getSettings());
+ return super.getDependency(dd, data);
+ }
+
+ public ResolvedResource findArtifactRef(Artifact artifact, Date date) {
+ ensureArtifactConfigured(getSettings());
+ return super.findArtifactRef(artifact, date);
+ }
+
+ public DownloadReport download(Artifact[] artifacts, DownloadOptions options) {
+ ensureArtifactConfigured(getSettings());
+ return super.download(artifacts, options);
+ }
+
+ public boolean exists(Artifact artifact) {
+ ensureArtifactConfigured(getSettings());
+ return super.exists(artifact);
+ }
+
+ public ArtifactOrigin locate(Artifact artifact) {
+ ensureArtifactConfigured(getSettings());
+ return super.locate(artifact);
+ }
+
+ public List getIvyPatterns() {
+ ensureIvyConfigured(getSettings());
+ return super.getIvyPatterns();
+ }
+
+ public List getArtifactPatterns() {
+ ensureArtifactConfigured(getSettings());
+ return super.getArtifactPatterns();
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/resolver/JarResolver.java b/src/java/org/apache/ivy/plugins/resolver/JarResolver.java
new file mode 100644
index 0000000..f305cf6
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/resolver/JarResolver.java
@@ -0,0 +1,101 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.resolver;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.jar.JarFile;
+
+import org.apache.ivy.core.cache.CacheResourceOptions;
+import org.apache.ivy.core.event.EventManager;
+import org.apache.ivy.core.report.ArtifactDownloadReport;
+import org.apache.ivy.core.report.DownloadStatus;
+import org.apache.ivy.plugins.repository.Resource;
+import org.apache.ivy.plugins.repository.jar.JarRepository;
+import org.apache.ivy.plugins.repository.url.URLRepository;
+import org.apache.ivy.plugins.repository.url.URLResource;
+
+public class JarResolver extends RepositoryResolver {
+
+ private URL url;
+
+ public JarResolver() {
+ setRepository(new JarRepository());
+ }
+
+ public String getTypeName() {
+ return "jar";
+ }
+
+ public void setFile(String jarFile) {
+ setJarFile(new File(jarFile));
+ }
+
+ public void setUrl(String jarUrl) {
+ try {
+ url = new URL(jarUrl);
+ } catch (MalformedURLException e) {
+ throw new RuntimeException("the jar repository " + getName()
+ + " has an malformed url : " + jarUrl + " (" + e.getMessage() + ")");
+ }
+ }
+
+ public JarRepository getJarRepository() {
+ return (JarRepository) super.getRepository();
+ }
+
+ private void setJarFile(File jarLocalFile) {
+ JarFile jar;
+ try {
+ jar = new JarFile(jarLocalFile);
+ } catch (IOException e) {
+ throw new RuntimeException("the jar repository " + getName() + " could not be read ("
+ + e.getMessage() + ")", e);
+ }
+ getJarRepository().setJarFile(jar);
+ }
+
+ public void setSettings(ResolverSettings settings) {
+ super.setSettings(settings);
+ // let's resolve the url
+ if (url != null) {
+ ArtifactDownloadReport report;
+ EventManager eventManager = getEventManager();
+ try {
+ if (eventManager != null) {
+ getRepository().addTransferListener(eventManager);
+ }
+ Resource jarResource = new URLResource(url);
+ CacheResourceOptions options = new CacheResourceOptions();
+ report = getRepositoryCacheManager().downloadRepositoryResource(jarResource,
+ "jarrepository", "jar", "jar", options, new URLRepository());
+ } finally {
+ if (eventManager != null) {
+ getRepository().removeTransferListener(eventManager);
+ }
+ }
+ if (report.getDownloadStatus() == DownloadStatus.FAILED) {
+ throw new RuntimeException("The jar file " + url.toExternalForm()
+ + " could not be downloaded (" + report.getDownloadDetails() + ")");
+ }
+ setJarFile(report.getLocalFile());
+ }
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/resolver/MirroredURLResolver.java b/src/java/org/apache/ivy/plugins/resolver/MirroredURLResolver.java
new file mode 100644
index 0000000..09777ad
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/resolver/MirroredURLResolver.java
@@ -0,0 +1,115 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.resolver;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.ivy.core.cache.CacheResourceOptions;
+import org.apache.ivy.core.report.ArtifactDownloadReport;
+import org.apache.ivy.osgi.repo.RelativeURLRepository;
+import org.apache.ivy.plugins.repository.url.ChainedRepository;
+import org.apache.ivy.plugins.repository.url.URLRepository;
+import org.apache.ivy.plugins.repository.url.URLResource;
+import org.apache.ivy.util.Message;
+
+public class MirroredURLResolver extends RepositoryResolver {
+
+ private URL mirrorListUrl;
+
+ public MirroredURLResolver() {
+ setRepository(new ChainedRepository());
+ }
+
+ public void setMirrorListUrl(URL mirrorListUrl) {
+ this.mirrorListUrl = mirrorListUrl;
+ }
+
+ private void setupMirrors() {
+ File mirrorListFile = downloadMirrorList();
+ List mirrorBaseUrls;
+ try {
+ mirrorBaseUrls = readMirrorList(mirrorListFile);
+ } catch (IOException e) {
+ throw new IllegalStateException("The mirror list could not be read from "
+ + mirrorListUrl + " (" + e.getMessage() + ")");
+ }
+ List/* <Repository> */repositories = new ArrayList();
+ Iterator it = mirrorBaseUrls.iterator();
+ while (it.hasNext()) {
+ String baseUrl = (String) it.next();
+ URL url = null;
+ try {
+ url = new URL(baseUrl);
+ } catch (MalformedURLException e) {
+ Message.warn("In the mirror list from " + mirrorListUrl
+ + ", an incorrect url has been found and will then not be used: " + baseUrl);
+ }
+ if (url != null) {
+ RelativeURLRepository repo = new RelativeURLRepository(url);
+ repositories.add(repo);
+ }
+ }
+ ((ChainedRepository) getRepository()).setRepositories(repositories);
+ }
+
+ private File downloadMirrorList() {
+ URLRepository urlRepository = new URLRepository();
+ if (getEventManager() != null) {
+ urlRepository.addTransferListener(getEventManager());
+ }
+ URLResource mirrorResource = new URLResource(mirrorListUrl);
+ CacheResourceOptions options = new CacheResourceOptions();
+ ArtifactDownloadReport report = getRepositoryCacheManager().downloadRepositoryResource(
+ mirrorResource, "mirrorlist", "text", "txt", options, urlRepository);
+ return report.getLocalFile();
+ }
+
+ private List/* <String> */readMirrorList(File mirrorListFile) throws IOException {
+ BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(
+ mirrorListFile)));
+ List/* <String> */list = new ArrayList();
+ try {
+ String line = in.readLine();
+ while (line != null) {
+ list.add(line);
+ line = in.readLine();
+ }
+ } finally {
+ in.close();
+ }
+ return list;
+ }
+
+ public String getTypeName() {
+ return "mirroredurl";
+ }
+
+ public void validate() {
+ super.validate();
+ setupMirrors();
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/resolver/RepositoryResolver.java b/src/java/org/apache/ivy/plugins/resolver/RepositoryResolver.java
new file mode 100644
index 0000000..59dbf67
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/resolver/RepositoryResolver.java
@@ -0,0 +1,347 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.resolver;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.ivy.core.IvyPatternHelper;
+import org.apache.ivy.core.event.EventManager;
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.DefaultArtifact;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.report.DownloadReport;
+import org.apache.ivy.core.resolve.DownloadOptions;
+import org.apache.ivy.plugins.parser.ModuleDescriptorParser;
+import org.apache.ivy.plugins.parser.ModuleDescriptorParserRegistry;
+import org.apache.ivy.plugins.repository.AbstractRepository;
+import org.apache.ivy.plugins.repository.Repository;
+import org.apache.ivy.plugins.repository.Resource;
+import org.apache.ivy.plugins.resolver.util.ResolvedResource;
+import org.apache.ivy.plugins.resolver.util.ResolverHelper;
+import org.apache.ivy.plugins.resolver.util.ResourceMDParser;
+import org.apache.ivy.plugins.signer.SignatureGenerator;
+import org.apache.ivy.plugins.version.VersionMatcher;
+import org.apache.ivy.util.ChecksumHelper;
+import org.apache.ivy.util.FileUtil;
+import org.apache.ivy.util.Message;
+
+/**
+ *
+ */
+public class RepositoryResolver extends AbstractPatternsBasedResolver {
+
+ private Repository repository;
+
+ private Boolean alwaysCheckExactRevision = null;
+
+ private String signerName = null;
+
+ public RepositoryResolver() {
+ }
+
+ public Repository getRepository() {
+ return repository;
+ }
+
+ public void setRepository(Repository repository) {
+ this.repository = repository;
+ }
+
+ public void setName(String name) {
+ super.setName(name);
+ if (repository instanceof AbstractRepository) {
+ ((AbstractRepository) repository).setName(name);
+ }
+ }
+
+ public void setSigner(String signerName) {
+ this.signerName = signerName;
+ }
+
+ protected ResolvedResource findResourceUsingPattern(ModuleRevisionId mrid, String pattern,
+ Artifact artifact, ResourceMDParser rmdparser, Date date) {
+ String name = getName();
+ VersionMatcher versionMatcher = getSettings().getVersionMatcher();
+ try {
+ if (!versionMatcher.isDynamic(mrid) || isAlwaysCheckExactRevision()) {
+ String resourceName = IvyPatternHelper.substitute(pattern, mrid, artifact);
+ Message.debug("\t trying " + resourceName);
+ logAttempt(resourceName);
+ Resource res = repository.getResource(resourceName);
+ boolean reachable = res.exists();
+ if (reachable) {
+ String revision;
+ if (pattern.indexOf(IvyPatternHelper.REVISION_KEY) == -1) {
+ if ("ivy".equals(artifact.getType()) || "pom".equals(artifact.getType())) {
+ // we can't determine the revision from the pattern, get it
+ // from the moduledescriptor itself
+ File temp = File.createTempFile("ivy", artifact.getExt());
+ temp.deleteOnExit();
+ repository.get(res.getName(), temp);
+ ModuleDescriptorParser parser = ModuleDescriptorParserRegistry
+ .getInstance().getParser(res);
+ ModuleDescriptor md = parser.parseDescriptor(getParserSettings(), temp
+ .toURI().toURL(), res, false);
+ revision = md.getRevision();
+ if ((revision == null) || (revision.length() == 0)) {
+ revision = "working@" + name;
+ }
+ } else {
+ revision = "working@" + name;
+ }
+ } else {
+ revision = mrid.getRevision();
+ }
+ return new ResolvedResource(res, revision);
+ } else if (versionMatcher.isDynamic(mrid)) {
+ return findDynamicResourceUsingPattern(rmdparser, mrid, pattern, artifact, date);
+ } else {
+ Message.debug("\t" + name + ": resource not reachable for " + mrid + ": res="
+ + res);
+ return null;
+ }
+ } else {
+ return findDynamicResourceUsingPattern(rmdparser, mrid, pattern, artifact, date);
+ }
+ } catch (IOException ex) {
+ throw new RuntimeException(name + ": unable to get resource for " + mrid + ": res="
+ + IvyPatternHelper.substitute(pattern, mrid, artifact) + ": " + ex, ex);
+ } catch (ParseException ex) {
+ throw new RuntimeException(name + ": unable to get resource for " + mrid + ": res="
+ + IvyPatternHelper.substitute(pattern, mrid, artifact) + ": " + ex, ex);
+ }
+ }
+
+ private ResolvedResource findDynamicResourceUsingPattern(ResourceMDParser rmdparser,
+ ModuleRevisionId mrid, String pattern, Artifact artifact, Date date) {
+ String name = getName();
+ logAttempt(IvyPatternHelper.substitute(
+ pattern,
+ ModuleRevisionId.newInstance(mrid,
+ IvyPatternHelper.getTokenString(IvyPatternHelper.REVISION_KEY)), artifact));
+ ResolvedResource[] rress = listResources(repository, mrid, pattern, artifact);
+ if (rress == null) {
+ Message.debug("\t" + name + ": unable to list resources for " + mrid + ": pattern="
+ + pattern);
+ return null;
+ } else {
+ ResolvedResource found = findResource(rress, rmdparser, mrid, date);
+ if (found == null) {
+ Message.debug("\t" + name + ": no resource found for " + mrid + ": pattern="
+ + pattern);
+ }
+ return found;
+ }
+ }
+
+ protected Resource getResource(String source) throws IOException {
+ return repository.getResource(source);
+ }
+
+ /**
+ * List all revisions as resolved resources for the given artifact in the given repository using
+ * the given pattern, and using the given mrid except its revision.
+ *
+ * @param repository
+ * the repository in which revisions should be located
+ * @param mrid
+ * the module revision id to look for (except revision)
+ * @param pattern
+ * the pattern to use to locate the revisions
+ * @param artifact
+ * the artifact to find
+ * @return an array of ResolvedResource, all pointing to a different revision of the given
+ * Artifact.
+ */
+ protected ResolvedResource[] listResources(Repository repository, ModuleRevisionId mrid,
+ String pattern, Artifact artifact) {
+ return ResolverHelper.findAll(repository, mrid, pattern, artifact);
+ }
+
+ protected long get(Resource resource, File dest) throws IOException {
+ Message.verbose("\t" + getName() + ": downloading " + resource.getName());
+ Message.debug("\t\tto " + dest);
+ if (dest.getParentFile() != null) {
+ dest.getParentFile().mkdirs();
+ }
+ repository.get(resource.getName(), dest);
+ return dest.length();
+ }
+
+ public void publish(Artifact artifact, File src, boolean overwrite) throws IOException {
+ String destPattern;
+ if ("ivy".equals(artifact.getType()) && !getIvyPatterns().isEmpty()) {
+ destPattern = (String) getIvyPatterns().get(0);
+ } else if (!getArtifactPatterns().isEmpty()) {
+ destPattern = (String) getArtifactPatterns().get(0);
+ } else {
+ throw new IllegalStateException("impossible to publish " + artifact + " using " + this
+ + ": no artifact pattern defined");
+ }
+ // Check for m2 compatibility
+ ModuleRevisionId mrid = artifact.getModuleRevisionId();
+ if (isM2compatible()) {
+ mrid = convertM2IdForResourceSearch(mrid);
+ }
+
+ String dest = getDestination(destPattern, artifact, mrid);
+
+ put(artifact, src, dest, overwrite);
+ Message.info("\tpublished " + artifact.getName() + " to "
+ + hidePassword(repository.standardize(dest)));
+ }
+
+ protected String getDestination(String pattern, Artifact artifact, ModuleRevisionId mrid) {
+ return IvyPatternHelper.substitute(pattern, mrid, artifact);
+ }
+
+ protected void put(Artifact artifact, File src, String dest, boolean overwrite)
+ throws IOException {
+ // verify the checksum algorithms before uploading artifacts!
+ String[] checksums = getChecksumAlgorithms();
+ for (int i = 0; i < checksums.length; i++) {
+ if (!ChecksumHelper.isKnownAlgorithm(checksums[i])) {
+ throw new IllegalArgumentException("Unknown checksum algorithm: " + checksums[i]);
+ }
+ }
+
+ repository.put(artifact, src, dest, overwrite);
+ for (int i = 0; i < checksums.length; i++) {
+ putChecksum(artifact, src, dest, overwrite, checksums[i]);
+ }
+
+ if (signerName != null) {
+ putSignature(artifact, src, dest, overwrite);
+ }
+ }
+
+ protected void putChecksum(Artifact artifact, File src, String dest, boolean overwrite,
+ String algorithm) throws IOException {
+ File csFile = File.createTempFile("ivytemp", algorithm);
+ try {
+ FileUtil.copy(new ByteArrayInputStream(ChecksumHelper.computeAsString(src, algorithm)
+ .getBytes()), csFile, null);
+ repository.put(
+ DefaultArtifact.cloneWithAnotherTypeAndExt(artifact, algorithm, artifact.getExt()
+ + "." + algorithm), csFile, dest + "." + algorithm, overwrite);
+ } finally {
+ csFile.delete();
+ }
+ }
+
+ protected void putSignature(Artifact artifact, File src, String dest, boolean overwrite)
+ throws IOException {
+ SignatureGenerator gen = getSettings().getSignatureGenerator(signerName);
+ if (gen == null) {
+ throw new IllegalArgumentException("Couldn't sign the artifacts! "
+ + "Unknown signer name: '" + signerName + "'");
+ }
+
+ File tempFile = File.createTempFile("ivytemp", gen.getExtension());
+
+ try {
+ gen.sign(src, tempFile);
+ repository.put(
+ DefaultArtifact.cloneWithAnotherTypeAndExt(artifact, gen.getExtension(),
+ artifact.getExt() + "." + gen.getExtension()), tempFile,
+ dest + "." + gen.getExtension(), overwrite);
+ } finally {
+ tempFile.delete();
+ }
+ }
+
+ public DownloadReport download(Artifact[] artifacts, DownloadOptions options) {
+ EventManager eventManager = getEventManager();
+ try {
+ if (eventManager != null) {
+ repository.addTransferListener(eventManager);
+ }
+ return super.download(artifacts, options);
+ } finally {
+ if (eventManager != null) {
+ repository.removeTransferListener(eventManager);
+ }
+ }
+ }
+
+ protected void findTokenValues(Collection names, List patterns, Map tokenValues, String token) {
+ for (Iterator iter = patterns.iterator(); iter.hasNext();) {
+ String pattern = (String) iter.next();
+ String partiallyResolvedPattern = IvyPatternHelper.substituteTokens(pattern,
+ tokenValues);
+ String[] values = ResolverHelper.listTokenValues(repository, partiallyResolvedPattern,
+ token);
+ if (values != null) {
+ names.addAll(filterNames(new ArrayList(Arrays.asList(values))));
+ }
+ }
+ }
+
+ protected String[] listTokenValues(String pattern, String token) {
+ return ResolverHelper.listTokenValues(repository, pattern, token);
+ }
+
+ protected boolean exist(String path) {
+ try {
+ Resource resource = repository.getResource(path);
+ return resource.exists();
+ } catch (IOException e) {
+ Message.debug(e);
+ return false;
+ }
+ }
+
+ public String getTypeName() {
+ return "repository";
+ }
+
+ public void dumpSettings() {
+ super.dumpSettings();
+ Message.debug("\t\trepository: " + getRepository());
+ }
+
+ public void setSettings(ResolverSettings settings) {
+ super.setSettings(settings);
+ if (settings != null) {
+ if (alwaysCheckExactRevision == null) {
+ alwaysCheckExactRevision = Boolean.valueOf(settings
+ .getVariable("ivy.default.always.check.exact.revision"));
+ }
+ }
+ }
+
+ public boolean isAlwaysCheckExactRevision() {
+ return alwaysCheckExactRevision == null ? true : alwaysCheckExactRevision.booleanValue();
+ }
+
+ public void setAlwaysCheckExactRevision(boolean alwaysCheckExactRevision) {
+ this.alwaysCheckExactRevision = Boolean.valueOf(alwaysCheckExactRevision);
+ }
+
+}
diff --git a/src/java/org/apache/ivy/plugins/resolver/ResolverSettings.java b/src/java/org/apache/ivy/plugins/resolver/ResolverSettings.java
new file mode 100644
index 0000000..5401801
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/resolver/ResolverSettings.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.resolver;
+
+import java.util.Collection;
+
+import org.apache.ivy.core.cache.RepositoryCacheManager;
+import org.apache.ivy.core.module.id.ModuleId;
+import org.apache.ivy.plugins.latest.LatestStrategy;
+import org.apache.ivy.plugins.namespace.Namespace;
+import org.apache.ivy.plugins.parser.ParserSettings;
+import org.apache.ivy.plugins.signer.SignatureGenerator;
+import org.apache.ivy.plugins.version.VersionMatcher;
+
+public interface ResolverSettings extends ParserSettings {
+
+ LatestStrategy getLatestStrategy(String latestStrategyName);
+
+ LatestStrategy getDefaultLatestStrategy();
+
+ RepositoryCacheManager getRepositoryCacheManager(String name);
+
+ RepositoryCacheManager getDefaultRepositoryCacheManager();
+
+ RepositoryCacheManager[] getRepositoryCacheManagers();
+
+ Namespace getNamespace(String namespaceName);
+
+ Namespace getSystemNamespace();
+
+ String getVariable(String string);
+
+ void configureRepositories(boolean b);
+
+ VersionMatcher getVersionMatcher();
+
+ String getResolveMode(ModuleId moduleId);
+
+ void filterIgnore(Collection names);
+
+ SignatureGenerator getSignatureGenerator(String name);
+
+}
diff --git a/src/java/org/apache/ivy/plugins/resolver/SFTPResolver.java b/src/java/org/apache/ivy/plugins/resolver/SFTPResolver.java
new file mode 100644
index 0000000..190d150
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/resolver/SFTPResolver.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.resolver;
+
+import org.apache.ivy.plugins.repository.sftp.SFTPRepository;
+
+/**
+ * This resolver is able to work with any sftp server. It supports listing and publishing. The
+ * server host should absolutely be set using setHost. basedir defaults to . port default to 22
+ * username and password will be prompted using a dialog box if not set. So if you are in an
+ * headless environment, provide username and password.
+ */
+public class SFTPResolver extends AbstractSshBasedResolver {
+
+ public SFTPResolver() {
+ setRepository(new SFTPRepository());
+ }
+
+ public String getTypeName() {
+ return "sftp";
+ }
+
+ public SFTPRepository getSFTPRepository() {
+ return (SFTPRepository) getRepository();
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/resolver/SshResolver.java b/src/java/org/apache/ivy/plugins/resolver/SshResolver.java
new file mode 100644
index 0000000..8a4f859
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/resolver/SshResolver.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.resolver;
+
+import org.apache.ivy.plugins.repository.ssh.SshRepository;
+
+/**
+ * Resolver for SSH resolver for ivy
+ */
+public class SshResolver extends AbstractSshBasedResolver {
+
+ public SshResolver() {
+ setRepository(new SshRepository());
+ }
+
+ /**
+ * A four digit string (e.g., 0644, see "man chmod", "man open") specifying the permissions of
+ * the published files.
+ */
+ public void setPublishPermissions(String permissions) {
+ ((SshRepository) getRepository()).setPublishPermissions(permissions);
+ }
+
+ /**
+ * sets the path separator used on the target system. Not sure if this is used or if '/' is used
+ * on all implementation. default is to use '/'
+ *
+ * @param sep
+ * file separator to use on the target system
+ */
+ public void setFileSeparator(String sep) {
+ if (sep == null || sep.length() != 1) {
+ throw new IllegalArgumentException(
+ "File Separator has to be a single character and not " + sep);
+ }
+ ((SshRepository) getRepository()).setFileSeparator(sep.trim().charAt(0));
+ }
+
+ /**
+ * set the command to get a directory listing the command has to be a shell command working on
+ * the target system and has to produce a listing of filenames, with each filename on a new line
+ * the term %arg can be used in the command to substitue the path to be listed (e.g.
+ * "ls -1 %arg | grep -v CVS" to get a listing without CVS directory) if %arg is not part of the
+ * command, the path will be appended to the command default is: "ls -1"
+ */
+ public void setListCommand(String cmd) {
+ ((SshRepository) getRepository()).setListCommand(cmd);
+ }
+
+ /**
+ * set the command to check for existence of a file the command has to be a shell command
+ * working on the target system and has to create an exit status of 0 for an existent file and
+ * <> 0 for a non existing file given as argument the term %arg can be used in the command to
+ * substitue the path to be listed if %arg is not part of the command, the path will be appended
+ * to the command default is: "ls"
+ */
+ public void setExistCommand(String cmd) {
+ ((SshRepository) getRepository()).setExistCommand(cmd);
+ }
+
+ /**
+ * set the command to create a directory on the target system the command has to be a shell
+ * command working on the target system and has to create a directory with the given argument
+ * the term %arg can be used in the command to substitue the path to be listed if %arg is not
+ * part of the command, the path will be appended to the command default is: "mkdir"
+ */
+ public void setCreateDirCommand(String cmd) {
+ ((SshRepository) getRepository()).setExistCommand(cmd);
+ }
+
+ public String getTypeName() {
+ return "ssh";
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/resolver/URLResolver.java b/src/java/org/apache/ivy/plugins/resolver/URLResolver.java
new file mode 100644
index 0000000..7afacdc
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/resolver/URLResolver.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.resolver;
+
+import org.apache.ivy.plugins.repository.url.URLRepository;
+
+/**
+ * This resolver is able to work with any URLs, it handles latest revisions with file and http urls
+ * only, and it does not handle publishing
+ */
+public class URLResolver extends RepositoryResolver {
+ public URLResolver() {
+ setRepository(new URLRepository());
+ }
+
+ public String getTypeName() {
+ return "url";
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/resolver/VfsResolver.java b/src/java/org/apache/ivy/plugins/resolver/VfsResolver.java
new file mode 100644
index 0000000..a897a36
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/resolver/VfsResolver.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.resolver;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.ivy.plugins.repository.vfs.VfsRepository;
+
+/**
+ *
+ */
+public class VfsResolver extends RepositoryResolver {
+ private static final Pattern URL_PATTERN = Pattern.compile("[a-z]*://(.+):(.+)@.*");
+
+ private static final int PASSWORD_GROUP = 2;
+
+ public VfsResolver() {
+ setRepository(new VfsRepository());
+ }
+
+ public String getTypeName() {
+ return "vfs";
+ }
+
+ public String hidePassword(String name) {
+ return prepareForDisplay(name);
+ }
+
+ public static String prepareForDisplay(String name) {
+ StringBuffer s = new StringBuffer(name);
+ Matcher m = URL_PATTERN.matcher(s);
+ if (m.matches()) {
+ final String password = m.group(PASSWORD_GROUP);
+ final int passwordposi = s.indexOf(password);
+ StringBuffer stars = new StringBuffer(password);
+ for (int posi = 0; posi < password.length(); posi++) {
+ stars.setCharAt(posi, '*');
+ }
+ String replacement = stars.toString();
+ s = s.replace(passwordposi, passwordposi + password.length(), replacement);
+ }
+ return s.toString();
+
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/resolver/VsftpResolver.java b/src/java/org/apache/ivy/plugins/resolver/VsftpResolver.java
new file mode 100644
index 0000000..e2c4e24
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/resolver/VsftpResolver.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.resolver;
+
+import org.apache.ivy.plugins.repository.vsftp.VsftpRepository;
+
+/**
+ * This resolver uses SecureCRT vsft to access an sftp server. It supports listing and publishing.
+ * The server host should absolutely be set using setHost, so does the username.
+ */
+public class VsftpResolver extends RepositoryResolver {
+ public VsftpResolver() {
+ setRepository(new VsftpRepository());
+ }
+
+ public String getTypeName() {
+ return "vsftp";
+ }
+
+ public VsftpRepository getVsftpRepository() {
+ return (VsftpRepository) getRepository();
+ }
+
+ public void disconnect() {
+ getVsftpRepository().disconnect();
+ }
+
+ public String getAuthentication() {
+ return getVsftpRepository().getAuthentication();
+ }
+
+ public String getHost() {
+ return getVsftpRepository().getHost();
+ }
+
+ public String getUsername() {
+ return getVsftpRepository().getUsername();
+ }
+
+ public void setAuthentication(String authentication) {
+ getVsftpRepository().setAuthentication(authentication);
+ }
+
+ public void setHost(String host) {
+ getVsftpRepository().setHost(host);
+ }
+
+ public void setUsername(String username) {
+ getVsftpRepository().setUsername(username);
+ }
+
+ public void setReuseConnection(long time) {
+ getVsftpRepository().setReuseConnection(time);
+ }
+
+ public void setReadTimeout(long readTimeout) {
+ getVsftpRepository().setReadTimeout(readTimeout);
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/resolver/packager/BuiltFileResource.java b/src/java/org/apache/ivy/plugins/resolver/packager/BuiltFileResource.java
new file mode 100644
index 0000000..ce8912a
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/resolver/packager/BuiltFileResource.java
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.resolver.packager;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.apache.ivy.core.IvyPatternHelper;
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.plugins.repository.Resource;
+
+/**
+ * Represents an artifact built by a {@link PackagerResolver}.
+ */
+public class BuiltFileResource implements Resource {
+
+ /**
+ * Where the build file should put built artifacts (relative to the build directory). Value is:
+ * * {@value}
+ */
+ public static final String BUILT_ARTIFACT_PATTERN = "artifacts/[type]s/[artifact].[ext]";
+
+ private final File file;
+
+ public BuiltFileResource(File file) {
+ this.file = file;
+ }
+
+ public BuiltFileResource(File dir, Artifact artifact) {
+ this(new File(dir, IvyPatternHelper.substitute(BUILT_ARTIFACT_PATTERN, artifact)));
+ }
+
+ public String getName() {
+ return file.toURI().toString();
+ }
+
+ public Resource clone(String name) {
+ return new BuiltFileResource(new File(name));
+ }
+
+ public long getLastModified() {
+ return file.lastModified();
+ }
+
+ public long getContentLength() {
+ return file.length();
+ }
+
+ public boolean exists() {
+ return file.exists();
+ }
+
+ public String toString() {
+ return getName();
+ }
+
+ public File getFile() {
+ return file;
+ }
+
+ public boolean isLocal() {
+ return false;
+ }
+
+ public InputStream openStream() throws IOException {
+ return new FileInputStream(file);
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/resolver/packager/PackagerCacheEntry.java b/src/java/org/apache/ivy/plugins/resolver/packager/PackagerCacheEntry.java
new file mode 100644
index 0000000..a7fe87a
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/resolver/packager/PackagerCacheEntry.java
@@ -0,0 +1,242 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.resolver.packager;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.ivy.core.IvyPatternHelper;
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.plugins.repository.Resource;
+import org.apache.ivy.plugins.resolver.util.ResolvedResource;
+import org.apache.ivy.util.FileUtil;
+import org.apache.ivy.util.Message;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.BuildLogger;
+import org.apache.tools.ant.DefaultLogger;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectHelper;
+
+/**
+ * Represents one entry in the cache of a {@link PackagerResolver}.
+ */
+public class PackagerCacheEntry {
+
+ private final ModuleRevisionId mr;
+
+ private final File dir;
+
+ private final File resourceCache;
+
+ private final String resourceURL;
+
+ private final boolean validate;
+
+ private final boolean preserve;
+
+ private final boolean restricted;
+
+ private final boolean verbose;
+
+ private final boolean quiet;
+
+ private boolean built;
+
+ // CheckStyle:ParameterNumber OFF
+ public PackagerCacheEntry(ModuleRevisionId mr, File rootDir, File resourceCache,
+ String resourceURL, boolean validate, boolean preserve, boolean restricted,
+ boolean verbose, boolean quiet) {
+ this.mr = mr;
+ this.dir = getSubdir(rootDir, this.mr);
+ this.resourceCache = resourceCache;
+ this.resourceURL = resourceURL;
+ this.validate = validate;
+ this.preserve = preserve;
+ this.restricted = restricted;
+ this.verbose = verbose;
+ this.quiet = quiet;
+ }
+
+ // CheckStyle:ParameterNumber ON
+
+ /**
+ * Attempt to build this entry.
+ *
+ * @param packagerResource
+ * packager metadata resource
+ * @param properties
+ * a map of properties to pass to the child Ant build responsible for dependency
+ * packaging
+ *
+ * @throws IllegalStateException
+ * if this entry has already been built
+ */
+ public synchronized void build(Resource packagerResource, Map properties) throws IOException {
+ // Sanity check
+ if (this.built) {
+ throw new IllegalStateException("build in directory `" + this.dir
+ + "' already completed");
+ }
+
+ // Remove work directory if it exists (e.g. left over from last time)
+ if (this.dir.exists()) {
+ if (!cleanup()) {
+ throw new IOException("can't remove directory `" + this.dir + "'");
+ }
+ }
+
+ // Create work directory
+ if (!this.dir.mkdirs()) {
+ throw new IOException("can't create directory `" + this.dir + "'");
+ }
+
+ // Write out packager XML
+ InputStream packagerXML = packagerResource.openStream();
+ saveFile("packager.xml", packagerXML);
+
+ // Write packager XSLT
+ saveFile("packager.xsl");
+
+ // Write packager XSD
+ saveFile("packager-1.0.xsd");
+
+ // Write master Ant build file
+ saveFile("build.xml");
+
+ // Execute the Ant build file
+ Project project = new Project();
+ project.init();
+ project.setUserProperty("ant.file", new File(dir, "build.xml").getAbsolutePath());
+ ProjectHelper.configureProject(project, new File(dir, "build.xml"));
+ project.setBaseDir(dir);
+
+ // Configure logging verbosity
+ BuildLogger logger = new DefaultLogger();
+ logger.setMessageOutputLevel(this.verbose ? Project.MSG_VERBOSE
+ : this.quiet ? Project.MSG_WARN : Project.MSG_INFO);
+ logger.setOutputPrintStream(System.out);
+ logger.setErrorPrintStream(System.err);
+ project.addBuildListener(logger);
+
+ // Set properties
+ project.setUserProperty("ivy.packager.organisation", ""
+ + this.mr.getModuleId().getOrganisation());
+ project.setUserProperty("ivy.packager.module", "" + this.mr.getModuleId().getName());
+ project.setUserProperty("ivy.packager.revision", "" + this.mr.getRevision());
+ project.setUserProperty("ivy.packager.branch", "" + this.mr.getBranch());
+ if (this.resourceCache != null) {
+ project.setUserProperty("ivy.packager.resourceCache",
+ "" + this.resourceCache.getCanonicalPath());
+ }
+ if (this.resourceURL != null) {
+ project.setUserProperty("ivy.packager.resourceURL", "" + getResourceURL());
+ }
+ if (this.validate) {
+ project.setUserProperty("ivy.packager.validate", "true");
+ }
+ project.setUserProperty("ivy.packager.restricted", "" + this.restricted);
+ project.setUserProperty("ivy.packager.quiet", String.valueOf(quiet));
+ if (properties != null) {
+ for (Iterator it = properties.entrySet().iterator(); it.hasNext();) {
+ Entry entry = (Entry) it.next();
+ project.setUserProperty((String) entry.getKey(), (String) entry.getValue());
+ }
+ }
+
+ // Execute task
+ Message.verbose("performing packager resolver build in " + this.dir);
+ try {
+ project.executeTarget("build");
+ this.built = true;
+ } catch (BuildException e) {
+ Message.verbose("packager resolver build failed: " + e);
+ throw e;
+ }
+ }
+
+ /**
+ * Has this entry been successfully built?
+ */
+ public synchronized boolean isBuilt() {
+ return this.built;
+ }
+
+ /**
+ * Get a built artifact.
+ *
+ * @throws IllegalStateException
+ * if this entry's built has not (yet) completed successfully
+ */
+ public ResolvedResource getBuiltArtifact(Artifact artifact) {
+ if (!this.built) {
+ throw new IllegalStateException("build in directory `" + this.dir
+ + "' has not yet successfully completed");
+ }
+ return new ResolvedResource(new BuiltFileResource(this.dir, artifact),
+ this.mr.getRevision());
+ }
+
+ public synchronized boolean cleanup() {
+ this.built = false;
+ return FileUtil.forceDelete(this.dir);
+ }
+
+ protected void saveFile(String name, InputStream input) throws IOException {
+ FileUtil.copy(input, new File(this.dir, name), null);
+ }
+
+ protected void saveFile(String name) throws IOException {
+ InputStream input = getClass().getResourceAsStream(name);
+ if (input == null) {
+ throw new IOException("can't find resource `" + name + "'");
+ }
+ saveFile(name, input);
+ }
+
+ // @Override
+ protected void finalize() throws Throwable {
+ try {
+ if (!this.preserve) {
+ cleanup();
+ }
+ } finally {
+ super.finalize();
+ }
+ }
+
+ private String getResourceURL() {
+ String baseURL = IvyPatternHelper.substitute(resourceURL, mr.getOrganisation(),
+ mr.getName(), mr.getRevision(), null, null, null, null,
+ mr.getQualifiedExtraAttributes(), null);
+ int slash = baseURL.lastIndexOf('/');
+ if (slash != -1) {
+ baseURL = baseURL.substring(0, slash + 1);
+ }
+ return baseURL;
+ }
+
+ private static File getSubdir(File rootDir, ModuleRevisionId mr) {
+ return new File(rootDir, mr.getOrganisation() + File.separatorChar + mr.getName()
+ + File.separatorChar + mr.getRevision());
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/resolver/packager/PackagerResolver.java b/src/java/org/apache/ivy/plugins/resolver/packager/PackagerResolver.java
new file mode 100644
index 0000000..6a12ba1
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/resolver/packager/PackagerResolver.java
@@ -0,0 +1,255 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.resolver.packager;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.DefaultArtifact;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.plugins.resolver.URLResolver;
+import org.apache.ivy.plugins.resolver.util.ResolvedResource;
+import org.apache.ivy.util.FileUtil;
+import org.apache.ivy.util.Message;
+
+/**
+ * Resolver that performs a "build" operation to resolve artifacts.
+ *
+ * <p>
+ * The resolver is configured with a base URL, from which the "ivy.xml" and "packager.xml" files are
+ * resolved. The latter file contains instructions describing how to build the actual artifacts.
+ */
+public class PackagerResolver extends URLResolver {
+
+ private static final String PACKAGER_ARTIFACT_NAME = "packager";
+
+ private static final String PACKAGER_ARTIFACT_TYPE = "packager";
+
+ private static final String PACKAGER_ARTIFACT_EXT = "xml";
+
+ private final HashMap/* <ModuleRevisionId, PackagerCacheEntry> */packagerCache = new HashMap();
+
+ private File buildRoot;
+
+ private File resourceCache;
+
+ private String resourceURL;
+
+ private Map/* <String,String> */properties = new LinkedHashMap();
+
+ private boolean validate = true;
+
+ private boolean preserve;
+
+ private boolean restricted = true;
+
+ private boolean verbose;
+
+ private boolean quiet;
+
+ public PackagerResolver() {
+ Runtime.getRuntime().addShutdownHook(new Thread() {
+ public void run() {
+ clearCache();
+ }
+ });
+ }
+
+ protected synchronized void clearCache() {
+ if (this.preserve) {
+ return;
+ }
+ for (Iterator i = packagerCache.values().iterator(); i.hasNext();) {
+ PackagerCacheEntry entry = (PackagerCacheEntry) i.next();
+ entry.cleanup();
+ }
+ packagerCache.clear();
+ if (this.buildRoot != null) {
+ FileUtil.forceDelete(this.buildRoot);
+ }
+ }
+
+ /**
+ * Set root directory under which builds take place.
+ */
+ public void setBuildRoot(File buildRoot) {
+ this.buildRoot = buildRoot;
+ }
+
+ /**
+ * Returns root directory under which builds take place.
+ */
+ public File getBuildRoot() {
+ return buildRoot;
+ }
+
+ /**
+ * Set resource cache directory.
+ */
+ public void setResourceCache(File resourceCache) {
+ this.resourceCache = resourceCache;
+ }
+
+ /**
+ * Get resource cache directory.
+ */
+ public File getResourceCache() {
+ return resourceCache;
+ }
+
+ /**
+ * Set base resource override URL pattern.
+ */
+ public void setResourceURL(String resourceURL) {
+ this.resourceURL = resourceURL;
+ }
+
+ /**
+ * Set pattern for locating "packager.xml" files.
+ */
+ public void setPackagerPattern(String pattern) {
+ ArrayList list = new ArrayList();
+ list.add(pattern);
+ setArtifactPatterns(list);
+ }
+
+ /**
+ * Set whether to preserve build directories. Default is false.
+ */
+ public void setPreserveBuildDirectories(boolean preserve) {
+ this.preserve = preserve;
+ }
+
+ /**
+ * Set whether to enable restricted mode. Default is true.
+ */
+ public void setRestricted(boolean restricted) {
+ this.restricted = restricted;
+ }
+
+ /**
+ * Set whether to run ant with the -verbose flag. Default is false.
+ */
+ public void setVerbose(boolean verbose) {
+ this.verbose = verbose;
+ }
+
+ /**
+ * Set whether to run ant with the -quiet flag. Default is false.
+ */
+ public void setQuiet(boolean quiet) {
+ this.quiet = quiet;
+ }
+
+ /**
+ * Set whether to validate downloaded packager.xml files. Default is true.
+ */
+ public void setValidate(boolean validate) {
+ this.validate = validate;
+ }
+
+ public void setAllownomd(boolean b) {
+ Message.error("allownomd not supported by resolver " + this);
+ }
+
+ public void setDescriptor(String rule) {
+ if (DESCRIPTOR_OPTIONAL.equals(rule)) {
+ Message.error("descriptor=\"" + DESCRIPTOR_OPTIONAL + "\" not supported by resolver "
+ + this);
+ return;
+ }
+ super.setDescriptor(rule);
+ }
+
+ /**
+ * Sets a property to be passed to the child Ant build responsible for packaging the dependency.
+ *
+ * @param propertyKey
+ * the property to pass
+ * @param propertyValue
+ * the value of the property to pass
+ */
+ public void setProperty(String propertyKey, String propertyValue) {
+ properties.put(propertyKey, propertyValue);
+ }
+
+ // @Override
+ public void validate() {
+ super.validate();
+ if (this.buildRoot == null) {
+ throw new IllegalStateException("no buildRoot specified");
+ }
+ if (getArtifactPatterns().size() == 0) {
+ throw new IllegalStateException("no packager pattern specified");
+ }
+ }
+
+ // @Override
+ public synchronized ResolvedResource findArtifactRef(Artifact artifact, Date date) {
+
+ // For our special packager.xml file, defer to superclass
+ if (PACKAGER_ARTIFACT_NAME.equals(artifact.getName())
+ && PACKAGER_ARTIFACT_TYPE.equals(artifact.getType())
+ && PACKAGER_ARTIFACT_EXT.equals(artifact.getExt())) {
+ return super.findArtifactRef(artifact, date);
+ }
+
+ // Check the cache
+ ModuleRevisionId mr = artifact.getModuleRevisionId();
+ PackagerCacheEntry entry = (PackagerCacheEntry) packagerCache.get(mr);
+
+ // Ignore invalid entries
+ if (entry != null && !entry.isBuilt()) {
+ packagerCache.remove(mr);
+ entry.cleanup();
+ entry = null;
+ }
+
+ // Build the artifacts (if not done already)
+ if (entry == null) {
+ ResolvedResource packager = findArtifactRef(new DefaultArtifact(mr, null,
+ PACKAGER_ARTIFACT_NAME, PACKAGER_ARTIFACT_TYPE, PACKAGER_ARTIFACT_EXT), date);
+ if (packager == null) {
+ return null;
+ }
+ entry = new PackagerCacheEntry(mr, this.buildRoot, this.resourceCache,
+ this.resourceURL, this.validate, this.preserve, this.restricted, this.verbose,
+ this.quiet);
+ try {
+ entry.build(packager.getResource(), properties);
+ } catch (IOException e) {
+ throw new RuntimeException("can't build artifact " + artifact, e);
+ }
+ packagerCache.put(mr, entry);
+ }
+
+ // Return reference to desired artifact
+ return entry.getBuiltArtifact(artifact);
+ }
+
+ public String getTypeName() {
+ return "packager";
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/resolver/packager/build.xml b/src/java/org/apache/ivy/plugins/resolver/packager/build.xml
new file mode 100644
index 0000000..f112fec
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/resolver/packager/build.xml
@@ -0,0 +1,58 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project name="packager" default="build">
+
+ <!-- Validate packager.xml -->
+ <target name="validate" if="ivy.packager.validate">
+ <xmlvalidate failonerror="true" warn="yes" lenient="no">
+ <attribute name="http://xml.org/sax/features/namespaces" value="true"/>
+ <attribute name="http://apache.org/xml/features/validation/schema" value="true"/>
+ <attribute name="http://apache.org/xml/features/validation/schema-full-checking" value="true"/>
+ <property name="http://apache.org/xml/properties/schema/external-noNamespaceSchemaLocation" value="packager-1.0.xsd"/>
+ <fileset dir=".">
+ <include name="packager.xml"/>
+ </fileset>
+ </xmlvalidate>
+ </target>
+
+ <!-- Set ${resourceURL} (case 1) -->
+ <target name="setResourceURL1" if="ivy.packager.resourceURL">
+ <property name="resourceURL" value="${ivy.packager.resourceURL}"/>
+ </target>
+
+ <!-- Set ${resourceURL} (case 2) -->
+ <target name="setResourceURL2" unless="ivy.packager.resourceURL">
+ <property name="resourceURL" value=""/>
+ </target>
+
+ <!-- Apply XSLT to generate ant build file -->
+ <target name="style">
+ <xslt style="packager.xsl" in="packager.xml" out="packager-output.xml">
+ <param name="resourceURL" expression="${resourceURL}"/>
+ <param name="restricted" expression="${ivy.packager.restricted}"/>
+ <param name="quiet" expression="${ivy.packager.quiet}"/>
+ </xslt>
+ </target>
+
+ <!-- Main build target: invoke generated build file -->
+ <target name="build" depends="validate, setResourceURL1, setResourceURL2, style">
+ <ant antfile="packager-output.xml"/>
+ </target>
+
+</project>
diff --git a/src/java/org/apache/ivy/plugins/resolver/packager/packager-1.0.xsd b/src/java/org/apache/ivy/plugins/resolver/packager/packager-1.0.xsd
new file mode 100644
index 0000000..394cc87
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/resolver/packager/packager-1.0.xsd
@@ -0,0 +1,101 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+
+ <xsd:element name="packager-module">
+ <xsd:complexType>
+ <xsd:sequence>
+ <xsd:choice maxOccurs="unbounded">
+ <xsd:element name="property">
+ <xsd:complexType>
+ <xsd:attribute name="name" type="xsd:string" use="required"/>
+ <xsd:attribute name="value" type="xsd:string" use="required"/>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="resource">
+ <xsd:complexType>
+ <xsd:sequence minOccurs="0" maxOccurs="unbounded">
+ <xsd:any processContents="lax"/> <!-- XXX refine me -->
+ </xsd:sequence>
+ <xsd:attribute name="url" type="xsd:anyURI" use="required"/>
+ <xsd:attribute name="sha1" type="sha1Type" use="required"/>
+ <xsd:attribute name="dest" type="xsd:string"/>
+ <xsd:attribute name="tofile" type="xsd:string"/>
+ <xsd:attribute name="type" type="archiveType"/>
+ <xsd:attribute name="filename" type="xsd:string"/>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="m2resource">
+ <xsd:complexType>
+ <xsd:sequence minOccurs="0" maxOccurs="unbounded">
+ <xsd:element name="artifact">
+ <xsd:complexType>
+ <xsd:attribute name="ext" type="xsd:string"/>
+ <xsd:attribute name="classifier" type="xsd:string"/>
+ <xsd:attribute name="sha1" type="sha1Type" use="required"/>
+ <xsd:attribute name="dest" type="xsd:string"/>
+ <xsd:attribute name="tofile" type="xsd:string"/>
+ <xsd:attribute name="type" type="archiveType"/>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:sequence>
+ <xsd:attribute name="groupId" type="xsd:string"/>
+ <xsd:attribute name="artifactId" type="xsd:string"/>
+ <xsd:attribute name="version" type="xsd:string"/>
+ <xsd:attribute name="repo" type="xsd:anyURI"/>
+ </xsd:complexType>
+ </xsd:element>
+ <xsd:element name="build">
+ <xsd:complexType>
+ <xsd:sequence minOccurs="0" maxOccurs="unbounded">
+ <xsd:any processContents="lax"/> <!-- XXX refine me -->
+ </xsd:sequence>
+ </xsd:complexType>
+ </xsd:element>
+ </xsd:choice>
+ </xsd:sequence>
+ <xsd:attribute name="version" type="versionType" use="required"/>
+ </xsd:complexType>
+ </xsd:element>
+
+ <xsd:simpleType name="sha1Type">
+ <xsd:restriction base="xsd:string">
+ <xsd:pattern value="[0-9a-f]{40}"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+
+ <xsd:simpleType name="versionType">
+ <xsd:restriction base="xsd:string">
+ <xsd:enumeration value="1.0"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+
+ <xsd:simpleType name="archiveType">
+ <xsd:restriction base="xsd:string">
+ <xsd:enumeration value="zip"/>
+ <xsd:enumeration value="war"/>
+ <xsd:enumeration value="jar"/>
+ <xsd:enumeration value="tar"/>
+ <xsd:enumeration value="tgz"/>
+ <xsd:enumeration value="tar.gz"/>
+ <xsd:enumeration value="tar.bz2"/>
+ </xsd:restriction>
+ </xsd:simpleType>
+
+</xsd:schema>
diff --git a/src/java/org/apache/ivy/plugins/resolver/packager/packager.xsl b/src/java/org/apache/ivy/plugins/resolver/packager/packager.xsl
new file mode 100644
index 0000000..f42e7d3
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/resolver/packager/packager.xsl
@@ -0,0 +1,499 @@
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+
+ <xsl:output encoding="UTF-8" method="xml" indent="yes" media-type="text/xml"/>
+
+ <xsl:param name="resourceURL"/>
+ <xsl:param name="restricted"/>
+ <xsl:param name="quiet"/>
+
+ <xsl:variable name="maven2repo" select="'https://repo1.maven.org/maven2/'"/>
+
+ <xsl:template match="/packager-module">
+ <xsl:comment> GENERATED FILE - DO NOT EDIT </xsl:comment>
+ <project name="packager" default="build">
+ <xsl:apply-templates select="property"/>
+
+ <xsl:apply-templates select="resource | m2resource"/>
+
+ <!-- First, download and extract all resources -->
+ <target name="resources">
+ <xsl:attribute name="depends">
+ <xsl:for-each select="resource | m2resource/artifact">
+ <xsl:if test="position() > 1">
+ <xsl:value-of select="', '"/>
+ </xsl:if>
+ <xsl:value-of select="concat('resource.', generate-id())"/>
+ </xsl:for-each>
+ </xsl:attribute>
+ </target>
+
+ <!-- Second, put all artifacts into place under artifacts/ -->
+ <target name="build" depends="resources">
+ <mkdir dir="artifacts/jars"/>
+ <mkdir dir="artifacts/sources"/>
+ <mkdir dir="artifacts/javadocs"/>
+ <!-- ...add some other common artifact types here... -->
+ <xsl:apply-templates select="build/*"/>
+ </target>
+ </project>
+ </xsl:template>
+
+ <!-- Properties -->
+ <xsl:template match="/packager-module/property">
+ <xsl:copy-of select="."/>
+ </xsl:template>
+
+ <!-- The allowed build actions in restricted mode -->
+ <xsl:template match="/packager-module/build/copy" priority="1"><xsl:copy-of select="."/></xsl:template>
+ <xsl:template match="/packager-module/build/jar" priority="1"><xsl:copy-of select="."/></xsl:template>
+ <xsl:template match="/packager-module/build/mkdir" priority="1"><xsl:copy-of select="."/></xsl:template>
+ <xsl:template match="/packager-module/build/move" priority="1"><xsl:copy-of select="."/></xsl:template>
+ <xsl:template match="/packager-module/build/tar" priority="1"><xsl:copy-of select="."/></xsl:template>
+ <xsl:template match="/packager-module/build/unjar" priority="1"><xsl:copy-of select="."/></xsl:template>
+ <xsl:template match="/packager-module/build/untar" priority="1"><xsl:copy-of select="."/></xsl:template>
+ <xsl:template match="/packager-module/build/unwar" priority="1"><xsl:copy-of select="."/></xsl:template>
+ <xsl:template match="/packager-module/build/unzip" priority="1"><xsl:copy-of select="."/></xsl:template>
+ <xsl:template match="/packager-module/build/war" priority="1"><xsl:copy-of select="."/></xsl:template>
+ <xsl:template match="/packager-module/build/zip" priority="1"><xsl:copy-of select="."/></xsl:template>
+
+ <!-- Allow other build actions when restricted="false", otherwise generate error -->
+ <xsl:template match="/packager-module/build/*">
+ <xsl:choose>
+ <xsl:when test="$restricted = 'false'">
+ <xsl:copy-of select="."/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:message terminate="yes">build tag <<xsl:value-of select="name()"/>> not allowed in restricted mode</xsl:message>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <!-- Resource definitions -->
+ <xsl:template match="/packager-module/resource">
+
+ <!-- Convert URL into simple filename -->
+ <target name="setfilename.{generate-id()}">
+ <xsl:choose>
+ <xsl:when test="@filename">
+ <property name="filename.{generate-id()}" value="{@filename}"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <basename property="filename.{generate-id()}" file="{@url}"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </target>
+
+ <!-- Generate list of URLs to try -->
+ <xsl:variable name="urls">
+ <xsl:call-template name="concat">
+ <xsl:with-param name="nodes" select="@url | url/@href"/>
+ </xsl:call-template>
+ </xsl:variable>
+
+ <!-- Get resource -->
+ <xsl:call-template name="resource">
+ <xsl:with-param name="urls" select="$urls"/>
+ <xsl:with-param name="type" select="@type"/>
+ <xsl:with-param name="csum" select="@sha1"/>
+ <xsl:with-param name="dest" select="@dest"/>
+ <xsl:with-param name="tofile" select="@tofile"/>
+ <xsl:with-param name="filename" select="concat('${filename.', generate-id(), '}')"/>
+ <xsl:with-param name="depends" select="concat('setfilename.', generate-id())"/>
+ </xsl:call-template>
+ </xsl:template>
+
+ <!-- Maven2 resources -->
+ <xsl:template match="/packager-module/m2resource">
+
+ <!-- Convert groupId into URL directories, where dots become slashes -->
+ <xsl:variable name="groupdirs" select="concat('groupdirs.', generate-id())"/>
+ <target name="setgroupdirs.{generate-id()}">
+ <xsl:variable name="groupId">
+ <xsl:choose>
+ <xsl:when test="@groupId">
+ <xsl:value-of select="@groupId"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="'${ivy.packager.organisation}'"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <pathconvert property="{$groupdirs}" dirsep="/">
+ <path location="{$groupId}"/>
+ <mapper type="unpackage" to="*">
+ <xsl:attribute name="from">
+ <xsl:value-of select="'${basedir}${file.separator}*'"/>
+ </xsl:attribute>
+ </mapper>
+ </pathconvert>
+ </target>
+
+ <!-- Get maven2 artifactId (or use default) -->
+ <xsl:variable name="artifactId">
+ <xsl:choose>
+ <xsl:when test="@artifactId">
+ <xsl:value-of select="@artifactId"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="'${ivy.packager.module}'"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <!-- Get maven2 version (or use default) -->
+ <xsl:variable name="version">
+ <xsl:choose>
+ <xsl:when test="@version">
+ <xsl:value-of select="@version"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="'${ivy.packager.revision}'"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <!-- Get maven2 repository URL (or use default) -->
+ <xsl:variable name="repourl">
+ <xsl:choose>
+ <xsl:when test="@repo and substring(@repo, string-length(@repo) - 1) = '/'">
+ <xsl:value-of select="@repo"/>
+ </xsl:when>
+ <xsl:when test="@repo">
+ <xsl:value-of select="concat(@repo, '/')"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$maven2repo"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <!-- Compose directory in the maven2 repository -->
+ <xsl:variable name="m2dir" select="concat($repourl, '${', $groupdirs, '}/', $artifactId, '/', $version, '/')"/>
+
+ <!-- Iterate over artifacts -->
+ <xsl:for-each select="artifact">
+
+ <!-- Get classifier (or use default) -->
+ <xsl:variable name="classifier">
+ <xsl:choose>
+ <xsl:when test="@classifier">
+ <xsl:value-of select="concat('-', @classifier)"/>
+ </xsl:when>
+ <xsl:otherwise/>
+ </xsl:choose>
+ </xsl:variable>
+
+ <!-- Get classifier (or use default) -->
+ <xsl:variable name="suffix">
+ <xsl:choose>
+ <xsl:when test="@ext">
+ <xsl:value-of select="concat('.', @ext)"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="'.jar'"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <!-- Compose to get filename, then complete URL -->
+ <xsl:variable name="filename" select="concat($artifactId, '-', $version, $classifier, $suffix)"/>
+ <xsl:variable name="url" select="concat($m2dir, $filename)"/>
+
+ <!-- Get resource -->
+ <xsl:call-template name="resource">
+ <xsl:with-param name="urls" select="$url"/>
+ <xsl:with-param name="csum" select="@sha1"/>
+ <xsl:with-param name="dest" select="@dest"/>
+ <xsl:with-param name="tofile" select="@tofile"/>
+ <xsl:with-param name="type" select="@type"/>
+ <xsl:with-param name="filename" select="$filename"/>
+ <xsl:with-param name="depends" select="concat('setgroupdirs.', generate-id(..))"/>
+ </xsl:call-template>
+ </xsl:for-each>
+ </xsl:template>
+
+ <!-- Download and optionally unpack a resource -->
+ <xsl:template name="resource">
+ <xsl:param name="urls"/>
+ <xsl:param name="type"/>
+ <xsl:param name="csum"/>
+ <xsl:param name="dest"/>
+ <xsl:param name="tofile"/>
+ <xsl:param name="filename"/>
+ <xsl:param name="depends"/>
+
+ <!-- Figure out which directory to download into (at runtime) -->
+ <target name="genresdir.1.{generate-id()}" unless="ivy.packager.resourceCache">
+ <property name="resdir.{generate-id()}" value="tempdir.{generate-id()}"/>
+ </target>
+ <target name="genresdir.2.{generate-id()}" if="ivy.packager.resourceCache">
+ <property name="resdir.{generate-id()}">
+ <xsl:attribute name="value">${ivy.packager.resourceCache}</xsl:attribute>
+ </property>
+ <echo level="info">
+ <xsl:attribute name="message">using resource cache: ${ivy.packager.resourceCache}</xsl:attribute>
+ </echo>
+ </target>
+ <xsl:variable name="resdir" select="concat('${resdir.', generate-id(), '}')"/>
+ <xsl:variable name="downloadfile" select="concat($resdir, '${file.separator}', $filename)"/>
+
+ <!-- Create directory for the downloaded resource -->
+ <target name="checkdownload.0.{generate-id()}" depends="{$depends}, genresdir.1.{generate-id()}, genresdir.2.{generate-id()}">
+ <mkdir dir="{$resdir}"/>
+ <condition property="alreadydownloaded.{generate-id()}">
+ <and>
+ <available file="{$downloadfile}"/>
+ <checksum file="{$downloadfile}" algorithm="SHA" property="{$csum}"/>
+ </and>
+ </condition>
+ </target>
+
+ <!-- Prepend URL list with resourceURL if configured -->
+ <xsl:variable name="urls2">
+ <xsl:choose>
+ <xsl:when test="$resourceURL">
+ <xsl:value-of select="concat($resourceURL, $filename, ' ', $urls)"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$urls"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+
+ <!-- Try to download each of the URLs in order -->
+ <xsl:call-template name="downloads">
+ <xsl:with-param name="urls" select="$urls2"/>
+ <xsl:with-param name="filename" select="$filename"/>
+ <xsl:with-param name="destfile" select="$downloadfile"/>
+ <xsl:with-param name="csum" select="$csum"/>
+ </xsl:call-template>
+
+ <!-- Unpack or just copy the file to its destination -->
+ <target name="resource.{generate-id()}" depends="download.{generate-id()}">
+ <xsl:choose>
+ <xsl:when test="$tofile">
+ <copy file="{$downloadfile}" tofile="{$tofile}"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:variable name="realdest">
+ <xsl:choose>
+ <xsl:when test="$dest">
+ <xsl:value-of select="$dest"/>
+ </xsl:when>
+ <xsl:otherwise>archive</xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <xsl:choose>
+ <xsl:when test="$type = 'none'">
+ <copy file="{$downloadfile}" todir="{$realdest}"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:variable name="actualtype">
+ <xsl:choose>
+ <xsl:when test="$type">
+ <xsl:value-of select="$type"/>
+ </xsl:when>
+ <xsl:when test="@filename">
+ <xsl:call-template name="archiveType">
+ <xsl:with-param name="file" select="@filename"/>
+ </xsl:call-template>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="archiveType">
+ <xsl:with-param name="file" select="$urls"/>
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <xsl:call-template name="unpack">
+ <xsl:with-param name="file" select="$downloadfile"/>
+ <xsl:with-param name="dir" select="$realdest"/>
+ <xsl:with-param name="type" select="$actualtype"/>
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:otherwise>
+ </xsl:choose>
+ </target>
+ </xsl:template>
+
+ <!-- Download resource, trying one or more URLs in order -->
+ <xsl:template name="downloads">
+ <xsl:param name="urls"/>
+ <xsl:param name="filename"/>
+ <xsl:param name="destfile"/>
+ <xsl:param name="csum"/>
+ <xsl:param name="index" select="1"/>
+
+ <!-- Parse out first URL from whitespace-separated list -->
+ <xsl:variable name="nurls" select="normalize-space($urls)"/>
+ <xsl:variable name="isFirstURL" select="$index = 1"/>
+ <xsl:variable name="isLastURL" select="not(contains($nurls, ' '))"/>
+ <xsl:variable name="url">
+ <xsl:choose>
+ <xsl:when test="not($isLastURL)">
+ <xsl:value-of select="substring-before($nurls, ' ')"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="$nurls"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:variable>
+ <xsl:variable name="rurls" select="substring-after($nurls, ' ')"/>
+
+ <!-- Attempt download using this URL; allow download to fail if this is not the last URL -->
+ <target name="download.{$index}.{generate-id()}" depends="checkdownload.{$index - 1}.{generate-id()}"
+ unless="alreadydownloaded.{generate-id()}">
+ <get src="{$url}" dest="{$destfile}" verbose="{string($quiet = 'false')}" ignoreerrors="{string(not($isLastURL))}"/>
+ </target>
+
+ <!-- Check whether download attempt was successful -->
+ <target name="checkdownload.{$index}.{generate-id()}" depends="download.{$index}.{generate-id()}"
+ unless="alreadydownloaded.{generate-id()}">
+ <condition property="alreadydownloaded.{generate-id()}">
+ <and>
+ <available file="{$destfile}"/>
+ <checksum file="{$destfile}" algorithm="SHA" property="{$csum}"/>
+ </and>
+ </condition>
+ </target>
+
+ <!-- Do final check after last attempt, or recurse -->
+ <xsl:choose>
+ <xsl:when test="$isLastURL">
+ <target name="download.{generate-id()}" depends="checkdownload.{$index}.{generate-id()}"
+ unless="alreadydownloaded.{generate-id()}">
+ <fail message="Unable to download {$filename} from any configured URL">
+ <condition>
+ <not>
+ <available file="{$destfile}"/>
+ </not>
+ </condition>
+ </fail>
+ <fail message="SHA1 checksum verification for {$filename} failed!"/>
+ </target>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:call-template name="downloads">
+ <xsl:with-param name="urls" select="$rurls"/>
+ <xsl:with-param name="filename" select="$filename"/>
+ <xsl:with-param name="destfile" select="$destfile"/>
+ <xsl:with-param name="csum" select="$csum"/>
+ <xsl:with-param name="index" select="$index + 1"/>
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <!-- Unpack an archive -->
+ <xsl:template name="unpack">
+ <xsl:param name="file"/>
+ <xsl:param name="dir"/>
+ <xsl:param name="type"/>
+
+ <!-- Get nested includes, excludes, etc. -->
+ <xsl:variable name="includes" select="*[name() != 'url']"/>
+
+ <!-- Unpack -->
+ <mkdir dir="{$dir}"/>
+ <xsl:choose>
+
+ <!-- ZIP type files -->
+ <xsl:when test="$type = 'zip' or $type = 'war' or $type = 'jar'">
+ <unzip src="{$file}" dest="{$dir}">
+ <xsl:if test="$includes">
+ <patternset>
+ <xsl:copy-of select="$includes"/>
+ </patternset>
+ </xsl:if>
+ </unzip>
+ </xsl:when>
+
+ <!-- TAR files, optionally compressed -->
+ <xsl:when test="starts-with($type, 'tar')">
+ <untar src="{$file}" dest="{$dir}">
+ <xsl:choose>
+ <xsl:when test="$type = 'tar.gz'">
+ <xsl:attribute name="compression">gzip</xsl:attribute>
+ </xsl:when>
+ <xsl:when test="$type = 'tar.bz2'">
+ <xsl:attribute name="compression">bzip2</xsl:attribute>
+ </xsl:when>
+ </xsl:choose>
+ <xsl:if test="$includes">
+ <patternset>
+ <xsl:copy-of select="$includes"/>
+ </patternset>
+ </xsl:if>
+ </untar>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:message terminate="yes">
+ <xsl:value-of select="concat('ERROR: unknown archive type "', $type, '"
')"/>
+ <xsl:value-of select="'Please set the "type" attribute to one of: zip, tar, tar.gz, or tar.bz2.
'"/>
+ </xsl:message>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <!-- Determine type of archive automatically based on filename -->
+ <xsl:template name="archiveType">
+ <xsl:param name="file"/>
+ <xsl:choose>
+ <xsl:when test="substring($file, string-length($file) - 3) = '.jar'">jar</xsl:when>
+ <xsl:when test="substring($file, string-length($file) - 3) = '.war'">war</xsl:when>
+ <xsl:when test="substring($file, string-length($file) - 3) = '.zip'">zip</xsl:when>
+ <xsl:when test="substring($file, string-length($file) - 3) = '.tar'">tar</xsl:when>
+ <xsl:when test="substring($file, string-length($file) - 3) = '.tgz'">tar.gz</xsl:when>
+ <xsl:when test="substring($file, string-length($file) - 6) = '.tar.gz'">tar.gz</xsl:when>
+ <xsl:when test="substring($file, string-length($file) - 7) = '.tar.bz2'">tar.bz2</xsl:when>
+ <xsl:otherwise>
+ <xsl:message terminate="yes">
+ <xsl:value-of select="concat('ERROR: cannot determine type of archive: ', $file, '
')"/>
+ <xsl:value-of select="'Please set the "type" attribute to one of: zip, tar, tar.gz, or tar.bz2.
'"/>
+ </xsl:message>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <!-- Concatenate nodes separated by spaces -->
+ <xsl:template name="concat">
+ <xsl:param name="nodes"/>
+ <xsl:choose>
+ <xsl:when test="count($nodes) <= 1">
+ <xsl:value-of select="string($nodes)"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="concat(string($nodes[1]), ' ')"/>
+ <xsl:call-template name="concat">
+ <xsl:with-param name="nodes" select="$nodes[position() > 1]"/>
+ </xsl:call-template>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+
+ <!-- Ignore anything unexpected -->
+ <xsl:template match="*">
+ <xsl:message terminate="no">ignoring unexpected XML node <<xsl:value-of select="name()"/>></xsl:message>
+ </xsl:template>
+ <xsl:template match="@*|node()"/>
+
+</xsl:transform>
diff --git a/src/java/org/apache/ivy/plugins/resolver/util/ApacheHttpURLLister.java b/src/java/org/apache/ivy/plugins/resolver/util/ApacheHttpURLLister.java
new file mode 100644
index 0000000..90d68b0
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/resolver/util/ApacheHttpURLLister.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.resolver.util;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.List;
+
+import org.apache.ivy.util.url.ApacheURLLister;
+
+public class ApacheHttpURLLister implements URLLister {
+ private ApacheURLLister lister = new ApacheURLLister();
+
+ public boolean accept(String pattern) {
+ return pattern.startsWith("http");
+ }
+
+ public List listAll(URL url) throws IOException {
+ return lister.listAll(url);
+ }
+
+ public String toString() {
+ return "apache http lister";
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/resolver/util/FileURLLister.java b/src/java/org/apache/ivy/plugins/resolver/util/FileURLLister.java
new file mode 100644
index 0000000..26cd04d
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/resolver/util/FileURLLister.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.resolver.util;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class FileURLLister implements URLLister {
+ private File basedir;
+
+ public FileURLLister() {
+ this(null);
+ }
+
+ public FileURLLister(File baseDir) {
+ this.basedir = baseDir;
+ }
+
+ public boolean accept(String pattern) {
+ return pattern.startsWith("file");
+ }
+
+ public List listAll(URL url) throws IOException {
+ String path;
+ try {
+ path = new File(new URI(url.toExternalForm())).getPath();
+ } catch (URISyntaxException e) {
+ // unexpected try to get the best of it
+ path = url.getPath();
+ }
+ File file = basedir == null ? new File(path) : new File(basedir, path);
+ if (file.exists() && file.isDirectory()) {
+ String[] files = file.list();
+ List ret = new ArrayList(files.length);
+ URL context = url.getPath().endsWith("/") ? url : new URL(url.toExternalForm() + "/");
+ for (int i = 0; i < files.length; i++) {
+ ret.add(new URL(context, files[i]));
+ }
+ return ret;
+ } else {
+ return Collections.EMPTY_LIST;
+ }
+ }
+
+ public String toString() {
+ return "file lister";
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/resolver/util/HasLatestStrategy.java b/src/java/org/apache/ivy/plugins/resolver/util/HasLatestStrategy.java
new file mode 100644
index 0000000..ea4b27d
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/resolver/util/HasLatestStrategy.java
@@ -0,0 +1,28 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.resolver.util;
+
+import org.apache.ivy.plugins.latest.LatestStrategy;
+
+public interface HasLatestStrategy {
+ public LatestStrategy getLatestStrategy();
+
+ public void setLatestStrategy(LatestStrategy latestStrategy);
+
+ public String getLatest();
+}
diff --git a/src/java/org/apache/ivy/plugins/resolver/util/MDResolvedResource.java b/src/java/org/apache/ivy/plugins/resolver/util/MDResolvedResource.java
new file mode 100644
index 0000000..d14fe10
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/resolver/util/MDResolvedResource.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.resolver.util;
+
+import org.apache.ivy.core.resolve.ResolvedModuleRevision;
+import org.apache.ivy.plugins.repository.Resource;
+
+public class MDResolvedResource extends ResolvedResource {
+ private ResolvedModuleRevision rmr;
+
+ public MDResolvedResource(Resource res, String rev, ResolvedModuleRevision rmr) {
+ super(res, rev);
+ this.rmr = rmr;
+ }
+
+ public ResolvedModuleRevision getResolvedModuleRevision() {
+ return rmr;
+ }
+
+}
diff --git a/src/java/org/apache/ivy/plugins/resolver/util/ResolvedResource.java b/src/java/org/apache/ivy/plugins/resolver/util/ResolvedResource.java
new file mode 100644
index 0000000..e655112
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/resolver/util/ResolvedResource.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.resolver.util;
+
+import org.apache.ivy.plugins.latest.ArtifactInfo;
+import org.apache.ivy.plugins.repository.Resource;
+
+public class ResolvedResource implements ArtifactInfo {
+ private Resource res;
+
+ private String rev;
+
+ public ResolvedResource(Resource res, String rev) {
+ this.res = res;
+ this.rev = rev;
+ }
+
+ public String getRevision() {
+ return rev;
+ }
+
+ public Resource getResource() {
+ return res;
+ }
+
+ public String toString() {
+ return res + " (" + rev + ")";
+ }
+
+ public long getLastModified() {
+ return getResource().getLastModified();
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/resolver/util/ResolverHelper.java b/src/java/org/apache/ivy/plugins/resolver/util/ResolverHelper.java
new file mode 100644
index 0000000..91411a9
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/resolver/util/ResolverHelper.java
@@ -0,0 +1,332 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.resolver.util;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.ivy.core.IvyPatternHelper;
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.plugins.repository.Repository;
+import org.apache.ivy.plugins.repository.Resource;
+import org.apache.ivy.util.Message;
+
+public final class ResolverHelper {
+ private ResolverHelper() {
+ }
+
+ // lists all the values a token can take in a pattern, as listed by a given url lister
+ public static String[] listTokenValues(Repository rep, String pattern, String token) {
+ String fileSep = rep.getFileSeparator();
+ pattern = rep.standardize(pattern);
+ String tokenString = IvyPatternHelper.getTokenString(token);
+ int index = pattern.indexOf(tokenString);
+ if (index == -1) {
+ Message.verbose("unable to list " + token + " in " + pattern
+ + ": token not found in pattern");
+ return null;
+ }
+ if (((pattern.length() <= index + tokenString.length()) || fileSep.equals(pattern
+ .substring(index + tokenString.length(), index + tokenString.length() + 1)))
+ && (index == 0 || fileSep.equals(pattern.substring(index - 1, index)))) {
+ // the searched token is a whole name
+ String root = pattern.substring(0, index);
+ return listAll(rep, root);
+ } else {
+ int slashIndex = pattern.substring(0, index).lastIndexOf(fileSep);
+ String root = slashIndex == -1 ? "" : pattern.substring(0, slashIndex);
+
+ try {
+ Message.debug("\tusing " + rep + " to list all in " + root);
+ String[] all = listAll(rep, root);
+ if (all != null) {
+ Message.debug("\t\tfound " + all.length + " urls");
+ List<String> ret = new ArrayList<String>(all.length);
+ int endNameIndex = pattern.indexOf(fileSep, slashIndex + 1);
+ String namePattern;
+ if (endNameIndex != -1) {
+ namePattern = pattern.substring(slashIndex + 1, endNameIndex);
+ } else {
+ namePattern = pattern.substring(slashIndex + 1);
+ }
+ namePattern = namePattern.replaceAll("\\.", "\\\\.");
+ namePattern = IvyPatternHelper.substituteToken(namePattern, token, "(.+)");
+ Pattern p = Pattern.compile(namePattern);
+ for (String path : all) {
+ Matcher m = p.matcher(path);
+ if (m.matches()) {
+ String value = m.group(1);
+ ret.add(value);
+ }
+ }
+ Message.debug("\t\t" + ret.size() + " matched " + pattern);
+ return (String[]) ret.toArray(new String[ret.size()]);
+ } else {
+ return null;
+ }
+ } catch (Exception e) {
+ Message.warn("problem while listing resources in " + root + " with " + rep, e);
+ return null;
+ }
+ }
+ }
+
+ public static String[] listAll(Repository rep, String parent) {
+ try {
+ String fileSep = rep.getFileSeparator();
+ Message.debug("\tusing " + rep + " to list all in " + parent);
+ List all = rep.list(parent);
+ if (all != null) {
+ Message.debug("\t\tfound " + all.size() + " resources");
+ List names = new ArrayList(all.size());
+ for (Iterator iter = all.iterator(); iter.hasNext();) {
+ String path = (String) iter.next();
+ if (path.endsWith(fileSep)) {
+ path = path.substring(0, path.length() - 1);
+ }
+ int slashIndex = path.lastIndexOf(fileSep);
+ names.add(path.substring(slashIndex + 1));
+ }
+ return (String[]) names.toArray(new String[names.size()]);
+ } else {
+ Message.debug("\t\tno resources found");
+ return null;
+ }
+ } catch (IOException e) {
+ Message.verbose("problem while listing resources in " + parent + " with " + rep, e);
+ return null;
+ } catch (Exception e) {
+ Message.warn("problem while listing resources in " + parent + " with " + rep, e);
+ return null;
+ }
+ }
+
+ public static ResolvedResource[] findAll(Repository rep, ModuleRevisionId mrid, String pattern,
+ Artifact artifact) {
+ // substitute all but revision
+ String partiallyResolvedPattern = IvyPatternHelper.substitute(
+ pattern,
+ ModuleRevisionId.newInstance(mrid,
+ IvyPatternHelper.getTokenString(IvyPatternHelper.REVISION_KEY)), artifact);
+ Message.debug("\tlisting all in " + partiallyResolvedPattern);
+
+ String[] revs = listTokenValues(rep, partiallyResolvedPattern,
+ IvyPatternHelper.REVISION_KEY);
+ if (revs != null) {
+ Message.debug("\tfound revs: " + Arrays.asList(revs));
+ List ret = new ArrayList(revs.length);
+ for (int i = 0; i < revs.length; i++) {
+ String rres = IvyPatternHelper.substituteToken(partiallyResolvedPattern,
+ IvyPatternHelper.REVISION_KEY, revs[i]);
+ try {
+ Resource res = rep.getResource(rres);
+ if (res != null) {
+ // we do not test if the resource actually exist here, it would cause
+ // a lot of checks which are not always necessary depending on the usage
+ // which is done of the returned ResolvedResource array
+ ret.add(new ResolvedResource(res, revs[i]));
+ }
+ } catch (IOException e) {
+ Message.warn("impossible to get resource from name listed by repository: "
+ + rres, e);
+ }
+ }
+ if (revs.length != ret.size()) {
+ Message.debug("\tfound resolved res: " + ret);
+ }
+ return (ResolvedResource[]) ret.toArray(new ResolvedResource[ret.size()]);
+ } else if (partiallyResolvedPattern.indexOf("[" + IvyPatternHelper.REVISION_KEY + "]") == -1) {
+ // the partially resolved pattern is completely resolved, check the resource
+ try {
+ Resource res = rep.getResource(partiallyResolvedPattern);
+ if (res.exists()) {
+ Message.debug("\tonly one resource found without real listing: "
+ + "using and defining it as working@" + rep.getName() + " revision: "
+ + res.getName());
+ return new ResolvedResource[] {new ResolvedResource(res, "working@"
+ + rep.getName())};
+ }
+ } catch (IOException e) {
+ Message.debug("\timpossible to get resource from name listed by repository: "
+ + partiallyResolvedPattern, e);
+ }
+ Message.debug("\tno revision found");
+ }
+ return null;
+ }
+
+ // public static ResolvedResource[] findAll(Repository rep, ModuleRevisionId mrid, String
+ // pattern, Artifact artifact, VersionMatcher versionMatcher, ResourceMDParser mdParser) {
+ // // substitute all but revision
+ // String partiallyResolvedPattern = IvyPatternHelper.substitute(pattern, new
+ // ModuleRevisionId(mrid.getModuleId(),
+ // IvyPatternHelper.getTokenString(IvyPatternHelper.REVISION_KEY), mrid.getExtraAttributes()),
+ // artifact);
+ // Message.debug("\tlisting all in "+partiallyResolvedPattern);
+ //
+ // String[] revs = listTokenValues(rep, partiallyResolvedPattern,
+ // IvyPatternHelper.REVISION_KEY);
+ // if (revs != null) {
+ // Message.debug("\tfound revs: "+Arrays.asList(revs));
+ // List ret = new ArrayList(revs.length);
+ // String rres = null;
+ // for (int i = 0; i < revs.length; i++) {
+ // ModuleRevisionId foundMrid = new ModuleRevisionId(mrid.getModuleId(), revs[i],
+ // mrid.getExtraAttributes());
+ // if (versionMatcher.accept(mrid, foundMrid)) {
+ // rres = IvyPatternHelper.substituteToken(partiallyResolvedPattern,
+ // IvyPatternHelper.REVISION_KEY, revs[i]);
+ // try {
+ // ResolvedResource resolvedResource;
+ // if (versionMatcher.needModuleDescriptor(mrid, foundMrid)) {
+ // resolvedResource = mdParser.parse(rep.getResource(rres), revs[i]);
+ // if (!versionMatcher.accept(mrid,
+ // ((MDResolvedResource)resolvedResource).getResolvedModuleRevision().getDescriptor())) {
+ // continue;
+ // }
+ // } else {
+ // resolvedResource = new ResolvedResource(rep.getResource(rres), revs[i]);
+ // }
+ // ret.add(resolvedResource);
+ // } catch (IOException e) {
+ // Message.warn("impossible to get resource from name listed by repository: "+rres+":
+ // "+e.getMessage());
+ // }
+ // }
+ // }
+ // if (revs.length != ret.size()) {
+ // Message.debug("\tfound resolved res: "+ret);
+ // }
+ // return (ResolvedResource[])ret.toArray(new ResolvedResource[ret.size()]);
+ // } else {
+ // // maybe the partially resolved pattern is completely resolved ?
+ // try {
+ // Resource res = rep.getResource(partiallyResolvedPattern);
+ // if (res.exists()) {
+ // Message.debug("\tonly one resource found without real listing: using and defining it as
+ // working@"+rep.getName()+" revision: "+res.getName());
+ // return new ResolvedResource[] {new ResolvedResource(res, "working@"+rep.getName())};
+ // }
+ // } catch (IOException e) {
+ // Message.debug("\timpossible to get resource from name listed by repository:
+ // "+partiallyResolvedPattern+": "+e.getMessage());
+ // }
+ // Message.debug("\tno revision found");
+ // }
+ // return null;
+ // }
+
+ // lists all the values a token can take in a pattern, as listed by a given url lister
+ public static String[] listTokenValues(URLLister lister, String pattern, String token) {
+ pattern = standardize(pattern);
+ if (lister.accept(pattern)) {
+ String tokenString = IvyPatternHelper.getTokenString(token);
+ int index = pattern.indexOf(tokenString);
+ if (index == -1) {
+ Message.verbose("unable to list " + token + " in " + pattern
+ + ": token not found in pattern");
+ return null;
+ }
+ if (((pattern.length() <= index + tokenString.length()) || "/".equals(pattern
+ .substring(index + tokenString.length(), index + tokenString.length() + 1)))
+ && (index == 0 || "/".equals(pattern.substring(index - 1, index)))) {
+ // the searched token is a whole name
+ String root = pattern.substring(0, index);
+ try {
+ return listAll(lister, new URL(root));
+ } catch (MalformedURLException e) {
+ Message.warn("malformed url from pattern root: " + root + ": " + e.getMessage());
+ return null;
+ }
+ } else {
+ int slashIndex = pattern.substring(0, index).lastIndexOf('/');
+ String root = slashIndex == -1 ? "" : pattern.substring(0, slashIndex);
+
+ try {
+ Message.debug("\tusing " + lister + " to list all in " + root);
+ List all = lister.listAll(new URL(root));
+ Message.debug("\t\tfound " + all.size() + " urls");
+ List ret = new ArrayList(all.size());
+ int endNameIndex = pattern.indexOf('/', slashIndex + 1);
+ String namePattern;
+ if (endNameIndex != -1) {
+ namePattern = pattern.substring(slashIndex + 1, endNameIndex);
+ } else {
+ namePattern = pattern.substring(slashIndex + 1);
+ }
+ String acceptNamePattern = ".*"
+ + IvyPatternHelper.substituteToken(namePattern, token, "([^/]+)")
+ + ".*";
+ Pattern p = Pattern.compile(acceptNamePattern.toString());
+ for (Iterator iter = all.iterator(); iter.hasNext();) {
+ URL url = (URL) iter.next();
+ String path = standardize(url.getPath());
+ Matcher m = p.matcher(path);
+ if (m.matches()) {
+ String value = m.group(1);
+ ret.add(value);
+ }
+ }
+ Message.debug("\t\t" + ret.size() + " matched " + pattern);
+ return (String[]) ret.toArray(new String[ret.size()]);
+ } catch (Exception e) {
+ Message.warn("problem while listing files in " + root, e);
+ return null;
+ }
+ }
+ }
+ return null;
+ }
+
+ private static String standardize(String path) {
+ return path.replace('\\', '/');
+ }
+
+ public static String[] listAll(URLLister lister, URL root) {
+ try {
+ if (lister.accept(root.toExternalForm())) {
+ Message.debug("\tusing " + lister + " to list all in " + root);
+ List all = lister.listAll(root);
+ Message.debug("\t\tfound " + all.size() + " urls");
+ List names = new ArrayList(all.size());
+ for (Iterator iter = all.iterator(); iter.hasNext();) {
+ URL dir = (URL) iter.next();
+ String path = dir.getPath();
+ if (path.endsWith("/")) {
+ path = path.substring(0, path.length() - 1);
+ }
+ int slashIndex = path.lastIndexOf('/');
+ names.add(path.substring(slashIndex + 1));
+ }
+ return (String[]) names.toArray(new String[names.size()]);
+ }
+ return null;
+ } catch (Exception e) {
+ Message.warn("problem while listing directories in " + root, e);
+ return null;
+ }
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/resolver/util/ResourceMDParser.java b/src/java/org/apache/ivy/plugins/resolver/util/ResourceMDParser.java
new file mode 100644
index 0000000..211f8c7
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/resolver/util/ResourceMDParser.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.resolver.util;
+
+import org.apache.ivy.plugins.repository.Resource;
+
+public interface ResourceMDParser {
+ /**
+ * Parses the module descriptor designed by the given resource.
+ *
+ * @param resource
+ * the resource at which the module descriptor is located
+ * @param rev
+ * the revision at which the module descriptor should be
+ * @return the parsed module descriptor as a {@link MDResolvedResource}, or <code>null</code> if
+ * parsing has failed or if the resource is not available.
+ */
+ MDResolvedResource parse(Resource resource, String rev);
+}
diff --git a/src/java/org/apache/ivy/plugins/resolver/util/URLLister.java b/src/java/org/apache/ivy/plugins/resolver/util/URLLister.java
new file mode 100644
index 0000000..6e7258a
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/resolver/util/URLLister.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.resolver.util;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.List;
+
+public interface URLLister {
+ /**
+ * Indicates if this lister is able to list urls with the given pattern. In general, only
+ * protocol is used.
+ *
+ * @param pattern
+ * @return
+ */
+ boolean accept(String pattern);
+
+ List listAll(URL url) throws IOException;
+}
diff --git a/src/java/org/apache/ivy/plugins/signer/SignatureGenerator.java b/src/java/org/apache/ivy/plugins/signer/SignatureGenerator.java
new file mode 100644
index 0000000..bf81e45
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/signer/SignatureGenerator.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.signer;
+
+import java.io.File;
+import java.io.IOException;
+
+public interface SignatureGenerator {
+
+ String getName();
+
+ void sign(File src, File dest) throws IOException;
+
+ String getExtension();
+
+}
diff --git a/src/java/org/apache/ivy/plugins/signer/bouncycastle/OpenPGPSignatureGenerator.java b/src/java/org/apache/ivy/plugins/signer/bouncycastle/OpenPGPSignatureGenerator.java
new file mode 100644
index 0000000..af7beae
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/signer/bouncycastle/OpenPGPSignatureGenerator.java
@@ -0,0 +1,185 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.signer.bouncycastle;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Security;
+import java.security.SignatureException;
+import java.util.Iterator;
+
+import org.apache.ivy.plugins.signer.SignatureGenerator;
+import org.bouncycastle.bcpg.ArmoredOutputStream;
+import org.bouncycastle.bcpg.BCPGOutputStream;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.openpgp.PGPException;
+import org.bouncycastle.openpgp.PGPPrivateKey;
+import org.bouncycastle.openpgp.PGPSecretKey;
+import org.bouncycastle.openpgp.PGPSecretKeyRing;
+import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
+import org.bouncycastle.openpgp.PGPSignature;
+import org.bouncycastle.openpgp.PGPSignatureGenerator;
+import org.bouncycastle.openpgp.PGPUtil;
+
+public class OpenPGPSignatureGenerator implements SignatureGenerator {
+
+ private static final long MASK = 0xFFFFFFFFL;
+
+ static {
+ Security.addProvider(new BouncyCastleProvider());
+ }
+
+ private String name;
+
+ private String secring;
+
+ private String password;
+
+ private String keyId;
+
+ private PGPSecretKey pgpSec;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public String getExtension() {
+ return "asc";
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+ public void setSecring(String secring) {
+ this.secring = secring;
+ }
+
+ public void setKeyId(String keyId) {
+ if (!"auto".equals(keyId)) {
+ this.keyId = keyId;
+ }
+ }
+
+ public void sign(File src, File dest) throws IOException {
+ OutputStream out = null;
+ InputStream in = null;
+ InputStream keyIn = null;
+
+ try {
+ if (secring == null) {
+ secring = System.getProperty("user.home") + "/.gnupg/secring.gpg";
+ }
+
+ if (pgpSec == null) {
+ keyIn = new FileInputStream(secring);
+ pgpSec = readSecretKey(keyIn);
+ }
+
+ PGPPrivateKey pgpPrivKey = pgpSec.extractPrivateKey(password.toCharArray(),
+ BouncyCastleProvider.PROVIDER_NAME);
+ PGPSignatureGenerator sGen = new PGPSignatureGenerator(pgpSec.getPublicKey()
+ .getAlgorithm(), PGPUtil.SHA1, BouncyCastleProvider.PROVIDER_NAME);
+ sGen.initSign(PGPSignature.BINARY_DOCUMENT, pgpPrivKey);
+
+ in = new FileInputStream(src);
+ out = new BCPGOutputStream(new ArmoredOutputStream(new FileOutputStream(dest)));
+
+ int ch = 0;
+ while ((ch = in.read()) >= 0) {
+ sGen.update((byte) ch);
+ }
+
+ sGen.generate().encode(out);
+ } catch (SignatureException e) {
+ IOException ioexc = new IOException();
+ ioexc.initCause(e);
+ throw ioexc;
+ } catch (PGPException e) {
+ IOException ioexc = new IOException();
+ ioexc.initCause(e);
+ throw ioexc;
+ } catch (NoSuchAlgorithmException e) {
+ IOException ioexc = new IOException();
+ ioexc.initCause(e);
+ throw ioexc;
+ } catch (NoSuchProviderException e) {
+ IOException ioexc = new IOException();
+ ioexc.initCause(e);
+ throw ioexc;
+ } finally {
+ if (out != null) {
+ try {
+ out.close();
+ } catch (IOException e) {
+ }
+ }
+ if (in != null) {
+ try {
+ in.close();
+ } catch (IOException e) {
+ }
+ }
+ if (keyIn != null) {
+ try {
+ keyIn.close();
+ } catch (IOException e) {
+ }
+ }
+ }
+ }
+
+ private PGPSecretKey readSecretKey(InputStream in) throws IOException, PGPException {
+ in = PGPUtil.getDecoderStream(in);
+ PGPSecretKeyRingCollection pgpSec = new PGPSecretKeyRingCollection(in);
+
+ PGPSecretKey key = null;
+ for (Iterator it = pgpSec.getKeyRings(); key == null && it.hasNext();) {
+ PGPSecretKeyRing kRing = (PGPSecretKeyRing) it.next();
+
+ for (Iterator it2 = kRing.getSecretKeys(); key == null && it2.hasNext();) {
+ PGPSecretKey k = (PGPSecretKey) it2.next();
+ if ((keyId == null) && k.isSigningKey()) {
+ key = k;
+ }
+ if ((keyId != null)
+ && (Long.valueOf(keyId, 16).longValue() == (k.getKeyID() & MASK))) {
+ key = k;
+ }
+ }
+ }
+
+ if (key == null) {
+ throw new IllegalArgumentException("Can't find encryption key"
+ + (keyId != null ? " '" + keyId + "' " : " ") + "in key ring.");
+ }
+
+ return key;
+ }
+
+}
diff --git a/src/java/org/apache/ivy/plugins/trigger/AbstractTrigger.java b/src/java/org/apache/ivy/plugins/trigger/AbstractTrigger.java
new file mode 100644
index 0000000..c4e02a6
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/trigger/AbstractTrigger.java
@@ -0,0 +1,83 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.trigger;
+
+import org.apache.ivy.core.IvyContext;
+import org.apache.ivy.core.event.IvyEvent;
+import org.apache.ivy.core.event.IvyEventFilter;
+import org.apache.ivy.core.event.IvyListener;
+import org.apache.ivy.plugins.matcher.PatternMatcher;
+import org.apache.ivy.util.filter.Filter;
+
+/**
+ * Base class for easy trigger implementation. This base class takes of the event filtering part,
+ * the only method to implement in subclasses is {@link IvyListener#progress(IvyEvent)} which should
+ * do whatever the trigger needs to do when the event occurs. This method will only be called when
+ * an event matching the trigger filter occurs.
+ *
+ * @since 1.4
+ */
+public abstract class AbstractTrigger implements Trigger {
+ private Filter filter;
+
+ private String event;
+
+ private String expression;
+
+ private String matcher = PatternMatcher.EXACT;
+
+ public Filter getEventFilter() {
+ if (filter == null) {
+ filter = createFilter();
+ }
+ return filter;
+ }
+
+ private Filter createFilter() {
+ return new IvyEventFilter(getEvent(), getFilter(), getPatternMatcher());
+ }
+
+ private PatternMatcher getPatternMatcher() {
+ return IvyContext.getContext().getSettings().getMatcher(matcher);
+ }
+
+ public String getEvent() {
+ return event;
+ }
+
+ public void setEvent(String event) {
+ this.event = event;
+ }
+
+ public String getFilter() {
+ return expression;
+ }
+
+ public void setFilter(String filterExpression) {
+ expression = filterExpression;
+ }
+
+ public String getMatcher() {
+ return matcher;
+ }
+
+ public void setMatcher(String matcher) {
+ this.matcher = matcher;
+ }
+
+}
diff --git a/src/java/org/apache/ivy/plugins/trigger/LogTrigger.java b/src/java/org/apache/ivy/plugins/trigger/LogTrigger.java
new file mode 100644
index 0000000..08d4d1a
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/trigger/LogTrigger.java
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.trigger;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+
+import org.apache.ivy.core.IvyPatternHelper;
+import org.apache.ivy.core.event.IvyEvent;
+import org.apache.ivy.core.resolve.ResolveProcessException;
+import org.apache.ivy.util.Message;
+
+/**
+ * A trigger performing logging.
+ * <p>
+ * The implementation is widely inspired by Ant Echo task.
+ * </p>
+ */
+public class LogTrigger extends AbstractTrigger {
+ private static final String LINE_SEPARATOR = System.getProperty("line.separator");
+
+ private String message = "";
+
+ private File file = null;
+
+ private boolean append = true;
+
+ /** encoding; set to null or empty means 'default' */
+ private String encoding = "";
+
+ public void progress(IvyEvent event) {
+ log(IvyPatternHelper.substituteVariables(message, event.getAttributes()));
+ }
+
+ /**
+ * Logs the given message.
+ *
+ * @param message
+ * the message to log
+ */
+ protected void log(String message) {
+ if (file == null) {
+ Message.info(message);
+ } else {
+ Writer out = null;
+ try {
+ // we add a line separator here for consistency with Message.info which creates a
+ // new line each time.
+ // we use the system dependent line separator to ease reading the log file
+ message += LINE_SEPARATOR;
+ String filename = file.getAbsolutePath();
+ if (encoding == null || encoding.length() == 0) {
+ out = new FileWriter(filename, append);
+ } else {
+ out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(filename,
+ append), encoding));
+ }
+ out.write(message, 0, message.length());
+ } catch (IOException e) {
+ throw new ResolveProcessException(e);
+ } finally {
+ if (out != null) {
+ try {
+ out.close();
+ } catch (IOException e) {
+ throw new ResolveProcessException(e);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Message to write.
+ *
+ * @param msg
+ * Sets the value for the message variable.
+ */
+ public void setMessage(String msg) {
+ this.message = msg;
+ }
+
+ /**
+ * File to write to.
+ *
+ * @param file
+ * the file to write to, if not set, echo to standard Ivy logging
+ */
+ public void setFile(File file) {
+ this.file = file;
+ }
+
+ /**
+ * If true, append to existing file.
+ *
+ * @param append
+ * if true, append to existing file, default is false.
+ */
+ public void setAppend(boolean append) {
+ this.append = append;
+ }
+
+ /**
+ * Declare the encoding to use when outputting to a file; Use "" for the platform's default
+ * encoding.
+ *
+ * @param encoding
+ * the character encoding to use.
+ */
+ public void setEncoding(String encoding) {
+ this.encoding = encoding;
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/trigger/Trigger.java b/src/java/org/apache/ivy/plugins/trigger/Trigger.java
new file mode 100644
index 0000000..b8987e9
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/trigger/Trigger.java
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.trigger;
+
+import org.apache.ivy.core.event.IvyListener;
+import org.apache.ivy.util.filter.Filter;
+
+public interface Trigger extends IvyListener {
+ Filter getEventFilter();
+}
diff --git a/src/java/org/apache/ivy/plugins/version/AbstractVersionMatcher.java b/src/java/org/apache/ivy/plugins/version/AbstractVersionMatcher.java
new file mode 100644
index 0000000..e3100cb
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/version/AbstractVersionMatcher.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.version;
+
+import java.util.Comparator;
+
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.settings.IvySettings;
+import org.apache.ivy.plugins.IvySettingsAware;
+import org.apache.ivy.util.Checks;
+
+public abstract class AbstractVersionMatcher implements VersionMatcher, IvySettingsAware {
+ private String name;
+
+ private IvySettings settings;
+
+ public AbstractVersionMatcher() {
+ }
+
+ public AbstractVersionMatcher(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public boolean needModuleDescriptor(ModuleRevisionId askedMrid, ModuleRevisionId foundMrid) {
+ return false;
+ }
+
+ public boolean accept(ModuleRevisionId askedMrid, ModuleDescriptor foundMD) {
+ return accept(askedMrid, foundMD.getResolvedModuleRevisionId());
+ }
+
+ /**
+ * This method should be overriden in most cases, because it uses the default contract to return
+ * 1 when it's not possible to know which revision is greater.
+ */
+ public int compare(ModuleRevisionId askedMrid, ModuleRevisionId foundMrid,
+ Comparator staticComparator) {
+ return 0;
+ }
+
+ public String toString() {
+ return getName();
+ }
+
+ public IvySettings getSettings() {
+ return settings;
+ }
+
+ public void setSettings(IvySettings settings) {
+ Checks.checkNotNull(settings, "settings");
+ this.settings = settings;
+ }
+
+}
diff --git a/src/java/org/apache/ivy/plugins/version/ChainVersionMatcher.java b/src/java/org/apache/ivy/plugins/version/ChainVersionMatcher.java
new file mode 100644
index 0000000..fae5ca7
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/version/ChainVersionMatcher.java
@@ -0,0 +1,155 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.version;
+
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.settings.IvySettings;
+import org.apache.ivy.plugins.IvySettingsAware;
+import org.apache.ivy.util.Checks;
+
+/**
+ * An implementation of {@link VersionMatcher} chaining several version matchers, and implemeting
+ * the {@link VersionMatcher} interface by returning results from the first matcher in the chain
+ * accepting the version.
+ */
+public class ChainVersionMatcher extends AbstractVersionMatcher {
+ /**
+ * The list of version matchers in the chain. This list will be queried in order, so the last
+ * matcher will be used only if no other matcher accept the revision before.
+ */
+ private List/* <VersionMatcher> */matchers = new LinkedList();
+
+ /**
+ * Unique Constructor.
+ */
+ public ChainVersionMatcher() {
+ super("chain");
+ }
+
+ /**
+ * Adds a {@link VersionMatcher} to the chain.
+ *
+ * @param matcher
+ * the version matcher to add. Must not be null
+ */
+ public void add(VersionMatcher matcher) {
+ Checks.checkNotNull(matcher, "matcher");
+ matchers.add(0, matcher);
+ if (getSettings() != null && matcher instanceof IvySettingsAware) {
+ ((IvySettingsAware) matcher).setSettings(getSettings());
+ }
+ }
+
+ /**
+ * Sets the settings this matcher will use, and set to the matcher in the chain which implements
+ * {@link IvySettingsAware}.
+ *
+ * @param settings
+ * the settings to use in the whole chain. Must not be null.
+ */
+ public void setSettings(IvySettings settings) {
+ super.setSettings(settings);
+ for (Iterator iter = matchers.iterator(); iter.hasNext();) {
+ VersionMatcher matcher = (VersionMatcher) iter.next();
+ if (matcher instanceof IvySettingsAware) {
+ ((IvySettingsAware) matcher).setSettings(settings);
+ }
+ }
+ }
+
+ /**
+ * Returns the list of matchers in the chain.
+ * <p>
+ * The list is returned as an unmodifiable view on the actual list of matchers, and will thus
+ * reflect futher changes made in the chain.
+ *
+ * @return the list of matchers in the chain. Is never null.
+ */
+ public List getMatchers() {
+ return Collections.unmodifiableList(matchers);
+ }
+
+ public boolean isDynamic(ModuleRevisionId askedMrid) {
+ Checks.checkNotNull(askedMrid, "askedMrid");
+ for (Iterator iter = matchers.iterator(); iter.hasNext();) {
+ VersionMatcher matcher = (VersionMatcher) iter.next();
+ if (matcher.isDynamic(askedMrid)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public int compare(ModuleRevisionId askedMrid, ModuleRevisionId foundMrid,
+ Comparator staticComparator) {
+ Checks.checkNotNull(askedMrid, "askedMrid");
+ Checks.checkNotNull(foundMrid, "foundMrid");
+ Checks.checkNotNull(staticComparator, "staticComparator");
+ for (Iterator iter = matchers.iterator(); iter.hasNext();) {
+ VersionMatcher matcher = (VersionMatcher) iter.next();
+ if (matcher.isDynamic(askedMrid)) {
+ return matcher.compare(askedMrid, foundMrid, staticComparator);
+ }
+ }
+ throw new IllegalArgumentException(
+ "impossible to compare revisions: askedMrid is not dynamic: " + askedMrid);
+ }
+
+ public boolean accept(ModuleRevisionId askedMrid, ModuleRevisionId foundMrid) {
+ Checks.checkNotNull(askedMrid, "askedMrid");
+ Checks.checkNotNull(foundMrid, "foundMrid");
+ for (Iterator iter = matchers.iterator(); iter.hasNext();) {
+ VersionMatcher matcher = (VersionMatcher) iter.next();
+ if (!iter.hasNext() || matcher.isDynamic(askedMrid)) {
+ return matcher.accept(askedMrid, foundMrid);
+ }
+ }
+ return false;
+ }
+
+ public boolean needModuleDescriptor(ModuleRevisionId askedMrid, ModuleRevisionId foundMrid) {
+ Checks.checkNotNull(askedMrid, "askedMrid");
+ Checks.checkNotNull(foundMrid, "foundMrid");
+ for (Iterator iter = matchers.iterator(); iter.hasNext();) {
+ VersionMatcher matcher = (VersionMatcher) iter.next();
+ if (!iter.hasNext() || matcher.isDynamic(askedMrid)) {
+ return matcher.needModuleDescriptor(askedMrid, foundMrid);
+ }
+ }
+ return false;
+ }
+
+ public boolean accept(ModuleRevisionId askedMrid, ModuleDescriptor foundMD) {
+ Checks.checkNotNull(askedMrid, "askedMrid");
+ Checks.checkNotNull(foundMD, "foundMD");
+ for (Iterator iter = matchers.iterator(); iter.hasNext();) {
+ VersionMatcher matcher = (VersionMatcher) iter.next();
+ if (!iter.hasNext() || matcher.isDynamic(askedMrid)) {
+ return matcher.accept(askedMrid, foundMD);
+ }
+ }
+ return false;
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/version/ExactVersionMatcher.java b/src/java/org/apache/ivy/plugins/version/ExactVersionMatcher.java
new file mode 100644
index 0000000..6b861e3
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/version/ExactVersionMatcher.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.version;
+
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+
+public class ExactVersionMatcher extends AbstractVersionMatcher {
+
+ public ExactVersionMatcher() {
+ super("exact");
+ }
+
+ public boolean isDynamic(ModuleRevisionId askedMrid) {
+ return false;
+ }
+
+ public boolean accept(ModuleRevisionId askedMrid, ModuleRevisionId foundMrid) {
+ return askedMrid.getRevision().equals(foundMrid.getRevision());
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/version/LatestVersionMatcher.java b/src/java/org/apache/ivy/plugins/version/LatestVersionMatcher.java
new file mode 100644
index 0000000..ba5b153
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/version/LatestVersionMatcher.java
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.version;
+
+import java.util.Comparator;
+import java.util.List;
+
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.module.status.Status;
+import org.apache.ivy.core.module.status.StatusManager;
+
+public class LatestVersionMatcher extends AbstractVersionMatcher {
+ public LatestVersionMatcher() {
+ super("latest");
+ }
+
+ public boolean isDynamic(ModuleRevisionId askedMrid) {
+ return askedMrid.getRevision().startsWith("latest.");
+ }
+
+ public boolean accept(ModuleRevisionId askedMrid, ModuleRevisionId foundMrid) {
+ return true;
+ }
+
+ public boolean needModuleDescriptor(ModuleRevisionId askedMrid, ModuleRevisionId foundMrid) {
+ List statuses = StatusManager.getCurrent().getStatuses();
+ Status lowest = (Status) statuses.get(statuses.size() - 1);
+ String latestLowest = "latest." + lowest.getName();
+ return !latestLowest.equals(askedMrid.getRevision());
+ }
+
+ public boolean accept(ModuleRevisionId askedMrid, ModuleDescriptor foundMD) {
+ String askedStatus = askedMrid.getRevision().substring("latest.".length());
+ return StatusManager.getCurrent().getPriority(askedStatus) >= StatusManager.getCurrent()
+ .getPriority(foundMD.getStatus());
+ }
+
+ /**
+ * If we don't need a module descriptor we can consider the dynamic revision to be greater. If
+ * we need a module descriptor then we can't know which one is greater and return 0.
+ */
+ public int compare(ModuleRevisionId askedMrid, ModuleRevisionId foundMrid,
+ Comparator staticComparator) {
+ return needModuleDescriptor(askedMrid, foundMrid) ? 0 : 1;
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/version/Match.java b/src/java/org/apache/ivy/plugins/version/Match.java
new file mode 100644
index 0000000..85465cc
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/version/Match.java
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.version;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+import org.apache.ivy.core.IvyContext;
+import org.apache.ivy.core.IvyPatternHelper;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.plugins.matcher.Matcher;
+import org.apache.ivy.plugins.matcher.PatternMatcher;
+
+/**
+ *
+ */
+public class Match {
+ private String revision;
+
+ private String pattern;
+
+ private String args;
+
+ private String matcher;
+
+ public String getArgs() {
+ return args;
+ }
+
+ public void setArgs(String args) {
+ this.args = args;
+ }
+
+ public String getMatcher() {
+ return matcher;
+ }
+
+ public void setMatcher(String matcher) {
+ this.matcher = matcher;
+ }
+
+ public String getPattern() {
+ return pattern;
+ }
+
+ public void setPattern(String pattern) {
+ this.pattern = pattern;
+ }
+
+ public String getRevision() {
+ return revision;
+ }
+
+ public void setRevision(String revision) {
+ this.revision = revision;
+ }
+
+ public Matcher getPatternMatcher(ModuleRevisionId askedMrid) {
+ String revision = askedMrid.getRevision();
+
+ String[] args = split(getArgs());
+ String[] argValues = getRevisionArgs(revision);
+
+ if (args.length != argValues.length) {
+ return new NoMatchMatcher();
+ }
+
+ Map variables = new HashMap();
+ for (int i = 0; i < args.length; i++) {
+ variables.put(args[i], argValues[i]);
+ }
+
+ String pattern = getPattern();
+ pattern = IvyPatternHelper.substituteVariables(pattern, variables);
+
+ PatternMatcher pMatcher = IvyContext.getContext().getSettings().getMatcher(matcher);
+ return pMatcher.getMatcher(pattern);
+ }
+
+ private String[] getRevisionArgs(String revision) {
+ int bracketStartIndex = revision.indexOf('(');
+ if (bracketStartIndex == -1) {
+ return new String[0];
+ }
+
+ int bracketEndIndex = revision.indexOf(')');
+ if (bracketEndIndex <= (bracketStartIndex + 1)) {
+ return new String[0];
+ }
+
+ String args = revision.substring(bracketStartIndex + 1, bracketEndIndex);
+ return split(args);
+ }
+
+ private static String[] split(String string) {
+ if (string == null) {
+ return new String[0];
+ }
+
+ StringTokenizer tokenizer = new StringTokenizer(string, ", ");
+ List tokens = new ArrayList();
+ while (tokenizer.hasMoreTokens()) {
+ tokens.add(tokenizer.nextToken());
+ }
+
+ return (String[]) tokens.toArray(new String[tokens.size()]);
+ }
+
+ private static class NoMatchMatcher implements Matcher {
+ public boolean isExact() {
+ return false;
+ }
+
+ public boolean matches(String str) {
+ return false;
+ }
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/version/PatternVersionMatcher.java b/src/java/org/apache/ivy/plugins/version/PatternVersionMatcher.java
new file mode 100644
index 0000000..66f51a2
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/version/PatternVersionMatcher.java
@@ -0,0 +1,99 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.version;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.plugins.matcher.Matcher;
+
+/**
+ *
+ */
+public class PatternVersionMatcher extends AbstractVersionMatcher {
+
+ private List matches = new ArrayList();
+
+ private Map revisionMatches = new HashMap(); // revision -> list of Match instances
+
+ private boolean init = false;
+
+ public void addMatch(Match match) {
+ matches.add(match);
+ }
+
+ private void init() {
+ if (!init) {
+ for (Iterator it = matches.iterator(); it.hasNext();) {
+ Match match = (Match) it.next();
+ List revMatches = (List) revisionMatches.get(match.getRevision());
+ if (revMatches == null) {
+ revMatches = new ArrayList();
+ revisionMatches.put(match.getRevision(), revMatches);
+ }
+ revMatches.add(match);
+ }
+ init = true;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean accept(ModuleRevisionId askedMrid, ModuleRevisionId foundMrid) {
+ init();
+ boolean accept = false;
+
+ String revision = askedMrid.getRevision();
+ int bracketIndex = revision.indexOf('(');
+ if (bracketIndex > 0) {
+ revision = revision.substring(0, bracketIndex);
+ }
+
+ List revMatches = (List) revisionMatches.get(revision);
+
+ if (revMatches != null) {
+ Iterator it = revMatches.iterator();
+ while (!accept && it.hasNext()) {
+ Match match = (Match) it.next();
+ Matcher matcher = match.getPatternMatcher(askedMrid);
+ accept = matcher.matches(foundMrid.getRevision());
+ }
+ }
+
+ return accept;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isDynamic(ModuleRevisionId askedMrid) {
+ init();
+ String revision = askedMrid.getRevision();
+ int bracketIndex = revision.indexOf('(');
+ if (bracketIndex > 0) {
+ revision = revision.substring(0, bracketIndex);
+ }
+ return revisionMatches.containsKey(revision);
+ }
+
+}
diff --git a/src/java/org/apache/ivy/plugins/version/SubVersionMatcher.java b/src/java/org/apache/ivy/plugins/version/SubVersionMatcher.java
new file mode 100644
index 0000000..b8e04cb
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/version/SubVersionMatcher.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.version;
+
+import java.util.Comparator;
+
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+
+public class SubVersionMatcher extends AbstractVersionMatcher {
+ public SubVersionMatcher() {
+ super("sub-version");
+ }
+
+ public boolean isDynamic(ModuleRevisionId askedMrid) {
+ return askedMrid.getRevision().endsWith("+");
+ }
+
+ public boolean accept(ModuleRevisionId askedMrid, ModuleRevisionId foundMrid) {
+ String prefix = askedMrid.getRevision().substring(0, askedMrid.getRevision().length() - 1);
+ return foundMrid.getRevision().startsWith(prefix);
+ }
+
+ public int compare(ModuleRevisionId askedMrid, ModuleRevisionId foundMrid,
+ Comparator staticComparator) {
+ if (foundMrid.getRevision().startsWith(
+ askedMrid.getRevision().substring(0, askedMrid.getRevision().length() - 1))) {
+ return 1;
+ }
+ return staticComparator.compare(askedMrid, foundMrid);
+ }
+}
diff --git a/src/java/org/apache/ivy/plugins/version/VersionMatcher.java b/src/java/org/apache/ivy/plugins/version/VersionMatcher.java
new file mode 100644
index 0000000..ffbc976
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/version/VersionMatcher.java
@@ -0,0 +1,110 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.version;
+
+import java.util.Comparator;
+
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+
+/**
+ * This interface defines a version matcher, i.e. a class able to tell if the revision asked by a
+ * module for a dependency is dynamic (i.e. need to find all revisions to find the good one among
+ * them) and if a found revision matches the asked one.
+ * <p>
+ * Two ways of matching are possible:
+ * <ul>
+ * <li>based on the module revision only (known as ModuleRevisionId)</li>
+ * <li>based on the parsed module descriptor</li>
+ * </ul>
+ * The second being much more time consuming than the first, the version matcher should tell if it
+ * needs such parsing or not using the
+ * {@link #needModuleDescriptor(ModuleRevisionId, ModuleRevisionId)} method. Anyway, the first way
+ * is always used, and if a revision is not accepted using the first method, the module descriptor
+ * won't be parsed. Therefore if a version matcher uses only module descriptors to accept a revision
+ * or not it should always return true to
+ * {@link #needModuleDescriptor(ModuleRevisionId, ModuleRevisionId)} and
+ * {@link #accept(ModuleRevisionId, ModuleDescriptor)}.
+ */
+public interface VersionMatcher {
+ /**
+ * Indicates if the given asked ModuleRevisionId should be considered as dynamic for the current
+ * VersionMatcher or not.
+ *
+ * @param askedMrid
+ * the dependency module revision id as asked by a module
+ * @return true if this revision is considered as a dynamic one, false otherwise
+ */
+ public boolean isDynamic(ModuleRevisionId askedMrid);
+
+ /**
+ * Indicates if this version matcher considers that the module revision found matches the asked
+ * one.
+ *
+ * @param askedMrid
+ * @param foundMrid
+ * @return
+ */
+ public boolean accept(ModuleRevisionId askedMrid, ModuleRevisionId foundMrid);
+
+ /**
+ * Indicates if this VersionMatcher needs module descriptors to determine if a module revision
+ * matches the asked one. Note that returning true in this method may imply big performance
+ * issues.
+ *
+ * @return
+ */
+ public boolean needModuleDescriptor(ModuleRevisionId askedMrid, ModuleRevisionId foundMrid);
+
+ /**
+ * Indicates if this version matcher considers that the module found matches the asked one. This
+ * method can be called even needModuleDescriptor(ModuleRevisionId askedMrid, ModuleRevisionId
+ * foundMrid) returns false, so it is required to implement it in any case, a usual default
+ * implementation being: return accept(askedMrid, foundMD.getResolvedModuleRevisionId());
+ *
+ * @param askedMrid
+ * @param foundMD
+ * @return
+ */
+ public boolean accept(ModuleRevisionId askedMrid, ModuleDescriptor foundMD);
+
+ /**
+ * Compares a dynamic revision (askedMrid) with a static one (foundMrid) to indicate which one
+ * should be considered the greater. If there is not enough information to know which one is the
+ * greater, the dynamic one should be considered greater and this method should return 0. This
+ * method should never be called with a askdeMrid for which isDynamic returns false.
+ *
+ * @param askedMrid
+ * the dynamic revision to compare
+ * @param foundMrid
+ * the static revision to compare
+ * @param staticComparator
+ * a comparator which can be used to compare static revisions
+ * @return 0 if it's not possible to know which one is greater, greater than 0 if askedMrid
+ * should be considered greater, lower than 0 if it can't be consider greater
+ */
+ public int compare(ModuleRevisionId askedMrid, ModuleRevisionId foundMrid,
+ Comparator staticComparator);
+
+ /**
+ * Returns the version matcher name identifying this version matcher
+ *
+ * @return the version matcher name identifying this version matcher
+ */
+ public String getName();
+}
diff --git a/src/java/org/apache/ivy/plugins/version/VersionRangeMatcher.java b/src/java/org/apache/ivy/plugins/version/VersionRangeMatcher.java
new file mode 100644
index 0000000..7c59a3a
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/version/VersionRangeMatcher.java
@@ -0,0 +1,246 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.plugins.version;
+
+import java.util.Comparator;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.plugins.latest.ArtifactInfo;
+import org.apache.ivy.plugins.latest.LatestStrategy;
+
+/**
+ * Matches version ranges: [1.0,2.0] matches all versions greater or equal to 1.0 and lower or equal
+ * to 2.0 [1.0,2.0[ matches all versions greater or equal to 1.0 and lower than 2.0 ]1.0,2.0]
+ * matches all versions greater than 1.0 and lower or equal to 2.0 ]1.0,2.0[ matches all versions
+ * greater than 1.0 and lower than 2.0 [1.0,) matches all versions greater or equal to 1.0 ]1.0,)
+ * matches all versions greater than 1.0 (,2.0] matches all versions lower or equal to 2.0 (,2.0[
+ * matches all versions lower than 2.0 This class uses a latest strategy to compare revisions. If
+ * none is set, it uses the default one of the ivy instance set through setIvy(). If neither a
+ * latest strategy nor a ivy instance is set, an IllegalStateException will be thrown when calling
+ * accept(). Note that it can't work with latest time strategy, cause no time is known for the
+ * limits of the range. Therefore only purely revision based LatestStrategy can be used.
+ */
+public class VersionRangeMatcher extends AbstractVersionMatcher {
+ // todo: check these constants
+ private static final String OPEN_INC = "[";
+
+ private static final String OPEN_EXC = "]";
+
+ private static final String OPEN_EXC_MAVEN = "(";
+
+ private static final String CLOSE_INC = "]";
+
+ private static final String CLOSE_EXC = "[";
+
+ private static final String CLOSE_EXC_MAVEN = ")";
+
+ private static final String LOWER_INFINITE = "(";
+
+ private static final String UPPER_INFINITE = ")";
+
+ private static final String SEPARATOR = ",";
+
+ // following patterns are built upon constants above and should not be modified
+ private static final String OPEN_INC_PATTERN = "\\" + OPEN_INC;
+
+ private static final String OPEN_EXC_PATTERN = "\\" + OPEN_EXC + "\\" + OPEN_EXC_MAVEN;
+
+ private static final String CLOSE_INC_PATTERN = "\\" + CLOSE_INC;
+
+ private static final String CLOSE_EXC_PATTERN = "\\" + CLOSE_EXC + "\\" + CLOSE_EXC_MAVEN;
+
+ private static final String LI_PATTERN = "\\" + LOWER_INFINITE;
+
+ private static final String UI_PATTERN = "\\" + UPPER_INFINITE;
+
+ private static final String SEP_PATTERN = "\\s*\\" + SEPARATOR + "\\s*";
+
+ private static final String OPEN_PATTERN = "[" + OPEN_INC_PATTERN + OPEN_EXC_PATTERN + "]";
+
+ private static final String CLOSE_PATTERN = "[" + CLOSE_INC_PATTERN + CLOSE_EXC_PATTERN + "]";
+
+ private static final String ANY_NON_SPECIAL_PATTERN = "[^\\s" + SEPARATOR + OPEN_INC_PATTERN
+ + OPEN_EXC_PATTERN + CLOSE_INC_PATTERN + CLOSE_EXC_PATTERN + LI_PATTERN + UI_PATTERN
+ + "]";
+
+ private static final String FINITE_PATTERN = OPEN_PATTERN + "\\s*(" + ANY_NON_SPECIAL_PATTERN
+ + "+)" + SEP_PATTERN + "(" + ANY_NON_SPECIAL_PATTERN + "+)\\s*" + CLOSE_PATTERN;
+
+ private static final String LOWER_INFINITE_PATTERN = LI_PATTERN + SEP_PATTERN + "("
+ + ANY_NON_SPECIAL_PATTERN + "+)\\s*" + CLOSE_PATTERN;
+
+ private static final String UPPER_INFINITE_PATTERN = OPEN_PATTERN + "\\s*("
+ + ANY_NON_SPECIAL_PATTERN + "+)" + SEP_PATTERN + UI_PATTERN;
+
+ private static final Pattern FINITE_RANGE = Pattern.compile(FINITE_PATTERN);
+
+ private static final Pattern LOWER_INFINITE_RANGE = Pattern.compile(LOWER_INFINITE_PATTERN);
+
+ private static final Pattern UPPER_INFINITE_RANGE = Pattern.compile(UPPER_INFINITE_PATTERN);
+
+ private static final Pattern ALL_RANGE = Pattern.compile(FINITE_PATTERN + "|"
+ + LOWER_INFINITE_PATTERN + "|" + UPPER_INFINITE_PATTERN);
+
+ private final class MRIDArtifactInfo implements ArtifactInfo {
+ private ModuleRevisionId mrid;
+
+ public MRIDArtifactInfo(ModuleRevisionId id) {
+ mrid = id;
+ }
+
+ public long getLastModified() {
+ return 0;
+ }
+
+ public String getRevision() {
+ return mrid.getRevision();
+ }
+ }
+
+ private final Comparator comparator = new Comparator() {
+ public int compare(Object o1, Object o2) {
+ if (o1.equals(o2)) {
+ return 0;
+ }
+ ArtifactInfo art1 = new MRIDArtifactInfo((ModuleRevisionId) o1);
+ ArtifactInfo art2 = new MRIDArtifactInfo((ModuleRevisionId) o2);
+ ArtifactInfo art = getLatestStrategy()
+ .findLatest(new ArtifactInfo[] {art1, art2}, null);
+ return art == art1 ? -1 : 1;
+ }
+ };
+
+ private LatestStrategy latestStrategy;
+
+ private String latestStrategyName = "default";
+
+ public VersionRangeMatcher() {
+ super("version-range");
+ }
+
+ public VersionRangeMatcher(String name) {
+ super(name);
+ }
+
+ public VersionRangeMatcher(String name, LatestStrategy strategy) {
+ super(name);
+ this.latestStrategy = strategy;
+ }
+
+ public boolean isDynamic(ModuleRevisionId askedMrid) {
+ String revision = askedMrid.getRevision();
+ return ALL_RANGE.matcher(revision).matches();
+ }
+
+ public boolean accept(ModuleRevisionId askedMrid, ModuleRevisionId foundMrid) {
+ String revision = askedMrid.getRevision();
+ Matcher m;
+ m = FINITE_RANGE.matcher(revision);
+ if (m.matches()) {
+ String lower = m.group(1);
+ String upper = m.group(2);
+ return isUpper(askedMrid, lower, foundMrid, revision.startsWith(OPEN_INC))
+ && isLower(askedMrid, upper, foundMrid, revision.endsWith(CLOSE_INC));
+ }
+ m = LOWER_INFINITE_RANGE.matcher(revision);
+ if (m.matches()) {
+ String upper = m.group(1);
+ return isLower(askedMrid, upper, foundMrid, revision.endsWith(CLOSE_INC));
+ }
+ m = UPPER_INFINITE_RANGE.matcher(revision);
+ if (m.matches()) {
+ String lower = m.group(1);
+ return isUpper(askedMrid, lower, foundMrid, revision.startsWith(OPEN_INC));
+ }
+ return false;
+ }
+
+ private boolean isLower(ModuleRevisionId askedMrid, String revision,
+ ModuleRevisionId foundMrid, boolean inclusive) {
+ ModuleRevisionId mRevId = ModuleRevisionId.newInstance(askedMrid, revision);
+ int result = comparator.compare(mRevId, foundMrid);
+ return result <= (inclusive ? 0 : -1);
+ }
+
+ private boolean isUpper(ModuleRevisionId askedMrid, String revision,
+ ModuleRevisionId foundMrid, boolean inclusive) {
+ ModuleRevisionId mRevId = ModuleRevisionId.newInstance(askedMrid, revision);
+ int result = comparator.compare(mRevId, foundMrid);
+ return result >= (inclusive ? 0 : 1);
+ }
+
+ public int compare(ModuleRevisionId askedMrid, ModuleRevisionId foundMrid,
+ Comparator staticComparator) {
+ String revision = askedMrid.getRevision();
+ Matcher m;
+ m = UPPER_INFINITE_RANGE.matcher(revision);
+ if (m.matches()) {
+ // no upper limit, the dynamic revision can always be considered greater
+ return 1;
+ }
+ String upper;
+ m = FINITE_RANGE.matcher(revision);
+ if (m.matches()) {
+ upper = m.group(2);
+ } else {
+ m = LOWER_INFINITE_RANGE.matcher(revision);
+ if (m.matches()) {
+ upper = m.group(1);
+ } else {
+ throw new IllegalArgumentException(
+ "impossible to compare: askedMrid is not a dynamic revision: " + askedMrid);
+ }
+ }
+ int c = staticComparator.compare(ModuleRevisionId.newInstance(askedMrid, upper), foundMrid);
+ // if the comparison consider them equal, we must return -1, because we can't consider the
+ // dynamic revision to be greater. Otherwise we can safeely return the result of the static
+ // comparison
+ return c == 0 ? -1 : c;
+ }
+
+ public LatestStrategy getLatestStrategy() {
+ if (latestStrategy == null) {
+ if (getSettings() == null) {
+ throw new IllegalStateException(
+ "no ivy instance nor latest strategy configured in version range matcher "
+ + this);
+ }
+ if (latestStrategyName == null) {
+ throw new IllegalStateException(
+ "null latest strategy defined in version range matcher " + this);
+ }
+ latestStrategy = getSettings().getLatestStrategy(latestStrategyName);
+ if (latestStrategy == null) {
+ throw new IllegalStateException("unknown latest strategy '" + latestStrategyName
+ + "' configured in version range matcher " + this);
+ }
+ }
+ return latestStrategy;
+ }
+
+ public void setLatestStrategy(LatestStrategy latestStrategy) {
+ this.latestStrategy = latestStrategy;
+ }
+
+ public void setLatest(String latestStrategyName) {
+ this.latestStrategyName = latestStrategyName;
+ }
+
+}
diff --git a/src/java/org/apache/ivy/tools/analyser/DependencyAnalyser.java b/src/java/org/apache/ivy/tools/analyser/DependencyAnalyser.java
new file mode 100644
index 0000000..1b45040
--- /dev/null
+++ b/src/java/org/apache/ivy/tools/analyser/DependencyAnalyser.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.tools.analyser;
+
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+
+public interface DependencyAnalyser {
+ public ModuleDescriptor[] analyze(JarModule[] modules);
+}
diff --git a/src/java/org/apache/ivy/tools/analyser/JarJarDependencyAnalyser.java b/src/java/org/apache/ivy/tools/analyser/JarJarDependencyAnalyser.java
new file mode 100644
index 0000000..81736b1
--- /dev/null
+++ b/src/java/org/apache/ivy/tools/analyser/JarJarDependencyAnalyser.java
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.tools.analyser;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.ivy.core.module.descriptor.DefaultDependencyDescriptor;
+import org.apache.ivy.core.module.descriptor.DefaultModuleDescriptor;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.util.Message;
+
+public class JarJarDependencyAnalyser implements DependencyAnalyser {
+ private File jarjarjarLocation;
+
+ public JarJarDependencyAnalyser(File jarjarjarLocation) {
+ this.jarjarjarLocation = jarjarjarLocation;
+ }
+
+ public ModuleDescriptor[] analyze(JarModule[] modules) {
+
+ StringBuffer jarjarCmd = new StringBuffer("java -jar \"").append(
+ jarjarjarLocation.getAbsolutePath()).append("\" --find --level=jar ");
+ Map jarModulesMap = new HashMap();
+ Map mds = new HashMap();
+
+ for (int i = 0; i < modules.length; i++) {
+ jarModulesMap.put(modules[i].getJar().getAbsolutePath(), modules[i]);
+ DefaultModuleDescriptor md = DefaultModuleDescriptor.newBasicInstance(
+ modules[i].getMrid(), new Date(modules[i].getJar().lastModified()));
+ mds.put(modules[i].getMrid(), md);
+ jarjarCmd.append("\"").append(modules[i].getJar().getAbsolutePath()).append("\"");
+ if (i + 1 < modules.length) {
+ jarjarCmd.append(File.pathSeparator);
+ }
+ }
+
+ Message.verbose("jarjar command: " + jarjarCmd);
+
+ try {
+ Process p = Runtime.getRuntime().exec(jarjarCmd.toString());
+ BufferedReader r = new BufferedReader(new InputStreamReader(p.getInputStream()));
+ String line;
+ while ((line = r.readLine()) != null) {
+ String[] deps = line.split(" -> ");
+ JarModule module = (JarModule) jarModulesMap.get(deps[0]);
+ JarModule dependency = (JarModule) jarModulesMap.get(deps[1]);
+
+ if (module.getMrid().getModuleId().equals(dependency.getMrid().getModuleId())) {
+ continue;
+ }
+ Message.verbose(module.getMrid() + " depends on " + dependency.getMrid());
+
+ DefaultModuleDescriptor md = (DefaultModuleDescriptor) mds.get(module.getMrid());
+
+ DefaultDependencyDescriptor dd = new DefaultDependencyDescriptor(md,
+ dependency.getMrid(), false, false, true);
+ dd.addDependencyConfiguration(ModuleDescriptor.DEFAULT_CONFIGURATION,
+ ModuleDescriptor.DEFAULT_CONFIGURATION);
+ md.addDependency(dd);
+ }
+ } catch (IOException e) {
+ Message.debug(e);
+ }
+ return (ModuleDescriptor[]) mds.values().toArray(new ModuleDescriptor[mds.values().size()]);
+ }
+
+ public static void main(String[] args) {
+ JarJarDependencyAnalyser a = new JarJarDependencyAnalyser(new File(
+ "D:/temp/test2/jarjar-0.7.jar"));
+ a.analyze(new JarModuleFinder(
+ "D:/temp/test2/ivyrep/[organisation]/[module]/[revision]/[artifact].[ext]")
+ .findJarModules());
+ }
+}
diff --git a/src/java/org/apache/ivy/tools/analyser/JarModule.java b/src/java/org/apache/ivy/tools/analyser/JarModule.java
new file mode 100644
index 0000000..35605fe
--- /dev/null
+++ b/src/java/org/apache/ivy/tools/analyser/JarModule.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.tools.analyser;
+
+import java.io.File;
+
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+
+public class JarModule {
+ private ModuleRevisionId mrid;
+
+ private File jar;
+
+ public JarModule(ModuleRevisionId mrid, File jar) {
+ this.mrid = mrid;
+ this.jar = jar;
+ }
+
+ public File getJar() {
+ return jar;
+ }
+
+ public ModuleRevisionId getMrid() {
+ return mrid;
+ }
+
+ public String toString() {
+ return jar + " " + mrid;
+ }
+
+}
diff --git a/src/java/org/apache/ivy/tools/analyser/JarModuleFinder.java b/src/java/org/apache/ivy/tools/analyser/JarModuleFinder.java
new file mode 100644
index 0000000..8cbd262
--- /dev/null
+++ b/src/java/org/apache/ivy/tools/analyser/JarModuleFinder.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.tools.analyser;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.ivy.core.IvyPatternHelper;
+import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.plugins.resolver.util.FileURLLister;
+import org.apache.ivy.plugins.resolver.util.ResolverHelper;
+import org.apache.ivy.plugins.resolver.util.URLLister;
+import org.apache.ivy.util.Message;
+
+public class JarModuleFinder {
+ private String pattern;
+
+ private String filePattern;
+
+ public JarModuleFinder(String pattern) {
+ this.pattern = "file:///" + pattern;
+ this.filePattern = pattern;
+ }
+
+ public JarModule[] findJarModules() {
+ List ret = new ArrayList();
+ URLLister lister = new FileURLLister();
+ try {
+ String[] orgs = ResolverHelper.listTokenValues(lister, pattern, "organisation");
+ for (int i = 0; i < orgs.length; i++) {
+ String orgPattern = IvyPatternHelper.substituteToken(pattern,
+ IvyPatternHelper.ORGANISATION_KEY, orgs[i]);
+ String[] modules = ResolverHelper.listTokenValues(lister, orgPattern, "module");
+ for (int j = 0; j < modules.length; j++) {
+ String modPattern = IvyPatternHelper.substituteToken(orgPattern,
+ IvyPatternHelper.MODULE_KEY, modules[j]);
+ String[] revs = ResolverHelper.listTokenValues(lister, modPattern, "revision");
+ for (int k = 0; k < revs.length; k++) {
+ File jar = new File(IvyPatternHelper.substitute(filePattern, orgs[i],
+ modules[j], revs[k], modules[j], "jar", "jar"));
+ if (jar.exists()) {
+ ret.add(new JarModule(ModuleRevisionId.newInstance(orgs[i], modules[j],
+ revs[k]), jar));
+ }
+ }
+ }
+ }
+
+ } catch (Exception e) {
+ Message.debug(e);
+ // TODO: handle exception
+ }
+ return (JarModule[]) ret.toArray(new JarModule[ret.size()]);
+ }
+
+ public static void main(String[] args) {
+ JarModule[] mods = new JarModuleFinder(
+ "D:/temp/test2/ivyrep/[organisation]/[module]/[revision]/[artifact].[ext]")
+ .findJarModules();
+ for (int i = 0; i < mods.length; i++) {
+ System.out.println(mods[i]);
+ }
+ }
+}
diff --git a/src/java/org/apache/ivy/tools/analyser/RepositoryAnalyser.java b/src/java/org/apache/ivy/tools/analyser/RepositoryAnalyser.java
new file mode 100644
index 0000000..aa34a45
--- /dev/null
+++ b/src/java/org/apache/ivy/tools/analyser/RepositoryAnalyser.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.tools.analyser;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.ivy.core.IvyPatternHelper;
+import org.apache.ivy.core.module.descriptor.DefaultArtifact;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.plugins.parser.xml.XmlModuleDescriptorWriter;
+import org.apache.ivy.util.Message;
+
+public class RepositoryAnalyser {
+ public void analyse(String pattern, DependencyAnalyser depAnalyser) {
+ JarModuleFinder finder = new JarModuleFinder(pattern);
+ ModuleDescriptor[] mds = depAnalyser.analyze(finder.findJarModules());
+ Message.info("found " + mds.length + " modules");
+ for (int i = 0; i < mds.length; i++) {
+ File ivyFile = new File(IvyPatternHelper.substitute(
+ pattern,
+ DefaultArtifact.newIvyArtifact(mds[i].getModuleRevisionId(),
+ mds[i].getPublicationDate())));
+ try {
+ Message.info("generating " + ivyFile);
+ XmlModuleDescriptorWriter.write(mds[i], ivyFile);
+ } catch (IOException e) {
+ Message.debug(e);
+ }
+ }
+ }
+
+ public static void main(String[] args) {
+ if (args.length != 2) {
+ System.out
+ .println("usage: ivyanalyser path/to/jarjar.jar absolute-ivy-repository-pattern");
+ return;
+ }
+ String jarjarLocation = args[0];
+ String pattern = args[1];
+
+ JarJarDependencyAnalyser a = new JarJarDependencyAnalyser(new File(jarjarLocation));
+ new RepositoryAnalyser().analyse(pattern, a);
+ }
+}
diff --git a/src/java/org/apache/ivy/util/AbstractMessageLogger.java b/src/java/org/apache/ivy/util/AbstractMessageLogger.java
new file mode 100644
index 0000000..118db66
--- /dev/null
+++ b/src/java/org/apache/ivy/util/AbstractMessageLogger.java
@@ -0,0 +1,199 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.util;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * An abstract base class to ease {@link MessageLogger} implementation.
+ */
+public abstract class AbstractMessageLogger implements MessageLogger {
+ private List problems = new ArrayList();
+
+ private List warns = new ArrayList();
+
+ private List errors = new ArrayList();
+
+ private boolean showProgress = true;
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.ivy.util.MessageLogger#debug(java.lang.String)
+ */
+ public void debug(String msg) {
+ log(msg, Message.MSG_DEBUG);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.ivy.util.MessageLogger#verbose(java.lang.String)
+ */
+ public void verbose(String msg) {
+ log(msg, Message.MSG_VERBOSE);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.ivy.util.MessageLogger#deprecated(java.lang.String)
+ */
+ public void deprecated(String msg) {
+ log("DEPRECATED: " + msg, Message.MSG_WARN);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.ivy.util.MessageLogger#info(java.lang.String)
+ */
+ public void info(String msg) {
+ log(msg, Message.MSG_INFO);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.ivy.util.MessageLogger#info(java.lang.String)
+ */
+ public void rawinfo(String msg) {
+ rawlog(msg, Message.MSG_INFO);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.ivy.util.MessageLogger#warn(java.lang.String)
+ */
+ public void warn(String msg) {
+ log("WARN: " + msg, Message.MSG_VERBOSE);
+ problems.add("WARN: " + msg);
+ getWarns().add(msg);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.ivy.util.MessageLogger#error(java.lang.String)
+ */
+ public void error(String msg) {
+ // log in verbose mode because message is appended as a problem, and will be
+ // logged at the end at error level
+ log("ERROR: " + msg, Message.MSG_VERBOSE);
+ problems.add("\tERROR: " + msg);
+ getErrors().add(msg);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.ivy.util.MessageLogger#getProblems()
+ */
+ public List getProblems() {
+ return problems;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.ivy.util.MessageLogger#sumupProblems()
+ */
+ public void sumupProblems() {
+ MessageLoggerHelper.sumupProblems(this);
+ clearProblems();
+ }
+
+ public void clearProblems() {
+ problems.clear();
+ warns.clear();
+ errors.clear();
+ }
+
+ public List getErrors() {
+ return errors;
+ }
+
+ public List getWarns() {
+ return warns;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.ivy.util.MessageLogger#progress()
+ */
+ public void progress() {
+ if (showProgress) {
+ doProgress();
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.ivy.util.MessageLogger#endProgress()
+ */
+ public void endProgress() {
+ endProgress("");
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.ivy.util.MessageLogger#endProgress(java.lang.String)
+ */
+ public void endProgress(String msg) {
+ if (showProgress) {
+ doEndProgress(msg);
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.ivy.util.MessageLogger#isShowProgress()
+ */
+ public boolean isShowProgress() {
+ return showProgress;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.apache.ivy.util.MessageLogger#setShowProgress(boolean)
+ */
+ public void setShowProgress(boolean progress) {
+ showProgress = progress;
+ }
+
+ /**
+ * Indicates a progression for a long running task
+ */
+ protected abstract void doProgress();
+
+ /**
+ * Indicates the end of a long running task
+ *
+ * @param msg
+ * the message associated with long running task end.
+ */
+ protected abstract void doEndProgress(String msg);
+
+}
diff --git a/src/java/org/apache/ivy/util/Checks.java b/src/java/org/apache/ivy/util/Checks.java
new file mode 100644
index 0000000..6b9e927
--- /dev/null
+++ b/src/java/org/apache/ivy/util/Checks.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.util;
+
+import java.io.File;
+
+/**
+ * Utility class used to perform some checks.
+ */
+public final class Checks {
+ private Checks() {
+ }
+
+ /**
+ * Checks that an object is not null, and throw an exception if the object is null.
+ *
+ * @param o
+ * the object to check
+ * @param objectName
+ * the name of the object to check. This name will be used in the exception message.
+ * @throws IllegalArgumentException
+ * if the object is null
+ */
+ public static void checkNotNull(Object o, String objectName) {
+ if (o == null) {
+ throw new IllegalArgumentException(objectName + " must not be null");
+ }
+ }
+
+ public static File checkAbsolute(File f, String fileName) {
+ checkNotNull(f, fileName);
+ if (!f.isAbsolute()) {
+ throw new IllegalArgumentException(fileName + " must be absolute: " + f.getPath());
+ }
+ return FileUtil.normalize(f.getPath());
+ }
+
+ public static File checkAbsolute(String path, String fileName) {
+ checkNotNull(path, fileName);
+ File f = new File(path);
+ if (!f.isAbsolute()) {
+ throw new IllegalArgumentException(fileName + " must be absolute: " + path);
+ }
+ return FileUtil.normalize(f.getPath());
+ }
+}
diff --git a/src/java/org/apache/ivy/util/ChecksumHelper.java b/src/java/org/apache/ivy/util/ChecksumHelper.java
new file mode 100644
index 0000000..56aa936
--- /dev/null
+++ b/src/java/org/apache/ivy/util/ChecksumHelper.java
@@ -0,0 +1,168 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.util;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+public final class ChecksumHelper {
+
+ private static final int BUFFER_SIZE = 2048;
+
+ private static Map algorithms = new HashMap();
+ static {
+ algorithms.put("md5", "MD5");
+ algorithms.put("sha1", "SHA-1");
+ }
+
+ /**
+ * Checks the checksum of the given file against the given checksumFile, and throws an
+ * IOException if the checksum is not compliant
+ *
+ * @param dest
+ * the file to test
+ * @param checksumFile
+ * the file containing the expected checksum
+ * @param algorithm
+ * the checksum algorithm to use
+ * @throws IOException
+ * if an IO problem occur whle reading files or if the checksum is not compliant
+ */
+ public static void check(File dest, File checksumFile, String algorithm) throws IOException {
+ String csFileContent = FileUtil
+ .readEntirely(new BufferedReader(new FileReader(checksumFile))).trim()
+ .toLowerCase(Locale.US);
+ String expected;
+ if (csFileContent.indexOf(' ') > -1
+ && (csFileContent.startsWith("md") || csFileContent.startsWith("sha"))) {
+ int lastSpaceIndex = csFileContent.lastIndexOf(' ');
+ expected = csFileContent.substring(lastSpaceIndex + 1);
+ } else {
+ int spaceIndex = csFileContent.indexOf(' ');
+ if (spaceIndex != -1) {
+ expected = csFileContent.substring(0, spaceIndex);
+
+ // IVY-1155: support some strange formats like this one:
+ // https://repo1.maven.org/maven2/org/apache/pdfbox/fontbox/0.8.0-incubator/fontbox-0.8.0-incubator.jar.md5
+ if (expected.endsWith(":")) {
+ StringBuffer result = new StringBuffer();
+ char[] chars = csFileContent.substring(spaceIndex + 1).toCharArray();
+ for (int i = 0; i < chars.length; i++) {
+ if (!Character.isWhitespace(chars[i])) {
+ result.append(chars[i]);
+ }
+ }
+ expected = result.toString();
+ }
+ } else {
+ expected = csFileContent;
+ }
+ }
+
+ String computed = computeAsString(dest, algorithm).trim().toLowerCase(Locale.US);
+ if (!expected.equals(computed)) {
+ throw new IOException("invalid " + algorithm + ": expected=" + expected + " computed="
+ + computed);
+ }
+ }
+
+ public static String computeAsString(File f, String algorithm) throws IOException {
+ return byteArrayToHexString(compute(f, algorithm));
+ }
+
+ private static byte[] compute(File f, String algorithm) throws IOException {
+ InputStream is = new FileInputStream(f);
+
+ try {
+ MessageDigest md = getMessageDigest(algorithm);
+ md.reset();
+
+ byte[] buf = new byte[BUFFER_SIZE];
+ int len = 0;
+ while ((len = is.read(buf)) != -1) {
+ md.update(buf, 0, len);
+ }
+ return md.digest();
+ } finally {
+ is.close();
+ }
+ }
+
+ public static boolean isKnownAlgorithm(String algorithm) {
+ return algorithms.containsKey(algorithm);
+ }
+
+ private static MessageDigest getMessageDigest(String algorithm) {
+ String mdAlgorithm = (String) algorithms.get(algorithm);
+ if (mdAlgorithm == null) {
+ throw new IllegalArgumentException("unknown algorithm " + algorithm);
+ }
+ try {
+ return MessageDigest.getInstance(mdAlgorithm);
+ } catch (NoSuchAlgorithmException e) {
+ throw new IllegalArgumentException("unknown algorithm " + algorithm);
+ }
+ }
+
+ // byte to hex string converter
+ private static final char[] CHARS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a',
+ 'b', 'c', 'd', 'e', 'f'};
+
+ /**
+ * Convert a byte[] array to readable string format. This makes the "hex" readable!
+ *
+ * @return result String buffer in String format
+ * @param in
+ * byte[] buffer to convert to string format
+ */
+ public static String byteArrayToHexString(byte[] in) {
+ byte ch = 0x00;
+
+ if (in == null || in.length <= 0) {
+ return null;
+ }
+
+ StringBuffer out = new StringBuffer(in.length * 2);
+
+ // CheckStyle:MagicNumber OFF
+ for (int i = 0; i < in.length; i++) {
+ ch = (byte) (in[i] & 0xF0); // Strip off high nibble
+ ch = (byte) (ch >>> 4); // shift the bits down
+ ch = (byte) (ch & 0x0F); // must do this is high order bit is on!
+
+ out.append(CHARS[ch]); // convert the nibble to a String Character
+ ch = (byte) (in[i] & 0x0F); // Strip off low nibble
+ out.append(CHARS[ch]); // convert the nibble to a String Character
+ }
+ // CheckStyle:MagicNumber ON
+
+ return out.toString();
+ }
+
+ private ChecksumHelper() {
+ }
+}
diff --git a/src/java/org/apache/ivy/util/CollectionUtils.java b/src/java/org/apache/ivy/util/CollectionUtils.java
new file mode 100644
index 0000000..d82e414
--- /dev/null
+++ b/src/java/org/apache/ivy/util/CollectionUtils.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.util;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+public class CollectionUtils {
+
+ public static List toList(Iterator iterator) {
+ List list = new ArrayList();
+ while (iterator.hasNext()) {
+ list.add(iterator.next());
+ }
+ return list;
+ }
+}
diff --git a/src/java/org/apache/ivy/util/ConfigurationUtils.java b/src/java/org/apache/ivy/util/ConfigurationUtils.java
new file mode 100644
index 0000000..1b5a637
--- /dev/null
+++ b/src/java/org/apache/ivy/util/ConfigurationUtils.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.util;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import org.apache.ivy.core.module.descriptor.Configuration;
+import org.apache.ivy.core.module.descriptor.Configuration.Visibility;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+
+/**
+ * Class containing several utility methods for working with configurations.
+ */
+public final class ConfigurationUtils {
+
+ /**
+ * Private constructor to avoid instantiation of this class.
+ */
+ private ConfigurationUtils() {
+ }
+
+ /**
+ * Replace the wildcards in the given configuration array, by the name of the given
+ * ModuleDescriptor
+ *
+ * The supported wildcards are:
+ * <ul>
+ * <li><b><tt>*</tt> :</b> all configurations</li>
+ * <li><b><tt>*(public)</tt> :</b> all public configurations</li>
+ * <li><b><tt>*(private)</tt> :</b> all private configurations</li>
+ * </ul>
+ * If the given array of configurations is <code>null</code>, all configurations from the given
+ * module descriptor are returned, including if this array is empty.
+ *
+ * @param confs
+ * the configurations, can contain wildcards
+ * @param md
+ * the configurations where the wildcards are replaced
+ * @return
+ */
+ public static String[] replaceWildcards(String[] confs, ModuleDescriptor md) {
+ if (confs == null) {
+ return md.getConfigurationsNames();
+ }
+
+ Set result = new LinkedHashSet();
+ Set excluded = new LinkedHashSet();
+ for (int i = 0; i < confs.length; i++) {
+ if ("*".equals(confs[i])) {
+ result.addAll(Arrays.asList(md.getConfigurationsNames()));
+ } else if ("*(public)".equals(confs[i])) {
+ Configuration[] all = md.getConfigurations();
+ for (int j = 0; j < all.length; j++) {
+ if (all[j].getVisibility().equals(Visibility.PUBLIC)) {
+ result.add(all[j].getName());
+ }
+ }
+ } else if ("*(private)".equals(confs[i])) {
+ Configuration[] all = md.getConfigurations();
+ for (int j = 0; j < all.length; j++) {
+ if (all[j].getVisibility().equals(Visibility.PRIVATE)) {
+ result.add(all[j].getName());
+ }
+ }
+ } else if (confs[i].startsWith("!")) {
+ excluded.add(confs[i].substring(1));
+ } else {
+ result.add(confs[i]);
+ }
+ }
+ for (Iterator iter = excluded.iterator(); iter.hasNext();) {
+ result.remove(iter.next());
+ }
+
+ return (String[]) result.toArray(new String[result.size()]);
+ }
+
+}
diff --git a/src/java/org/apache/ivy/util/Configurator.java b/src/java/org/apache/ivy/util/Configurator.java
new file mode 100644
index 0000000..9513070
--- /dev/null
+++ b/src/java/org/apache/ivy/util/Configurator.java
@@ -0,0 +1,764 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.util;
+
+import java.io.File;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Stack;
+
+import org.apache.ivy.core.IvyPatternHelper;
+
+/**
+ * Ant 1.6.1 like Configurator
+ * <p>
+ * This configurator is used to configure elements (initialised with setRoot) using the behaviour
+ * defined by ant for its tasks.
+ * <p>
+ * Example (based on <a href="http://ant.apache.org/manual/develop.html#writingowntask">Ant
+ * Example</a>):
+ *
+ * <pre>
+ * Configurator conf = new Configurator();
+ * conf.typeDef("buildpath", "Sample$BuildPath");
+ * conf.typeDef("xinterface", "Sample$XInterface");
+ * Sample.MyFileSelector mfs = new Sample.MyFileSelector();
+ * conf.setRoot(mfs);
+ * conf.startCreateChild("buildpath");
+ * conf.setAttribute("path", ".");
+ * conf.setAttribute("url", "abc");
+ * conf.startCreateChild("xinterface");
+ * conf.setAttribute("count", "4");
+ * conf.endCreateChild(); // xinterface
+ * conf.endCreateChild(); // buildpath
+ * </pre>
+ */
+public class Configurator {
+ public static class Macro {
+ private MacroDef macrodef;
+
+ private Map attValues = new HashMap();
+
+ private Map macroRecords = new HashMap();
+
+ public Macro(MacroDef def) {
+ macrodef = def;
+ }
+
+ public void defineAttribute(String attributeName, String value) {
+ if (macrodef.getAttribute(attributeName) == null) {
+ throw new IllegalArgumentException("undeclared attribute " + attributeName
+ + " on macro " + macrodef.getName());
+ }
+ attValues.put(attributeName, value);
+ }
+
+ public MacroRecord recordCreateChild(String name) {
+ MacroRecord macroRecord = new MacroRecord(name);
+ List records = (List) macroRecords.get(name);
+ if (records == null) {
+ records = new ArrayList();
+ macroRecords.put(name, records);
+ }
+ records.add(macroRecord);
+ return macroRecord;
+ }
+
+ public Object play(Configurator conf) {
+ return macrodef.play(conf, attValues, macroRecords);
+ }
+
+ }
+
+ public static class Attribute {
+ private String name;
+
+ private String defaultValue;
+
+ public String getDefault() {
+ return defaultValue;
+ }
+
+ public void setDefault(String default1) {
+ defaultValue = default1;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+ }
+
+ public static class Element {
+ private String name;
+
+ private boolean optional = false;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public boolean isOptional() {
+ return optional;
+ }
+
+ public void setOptional(boolean optional) {
+ this.optional = optional;
+ }
+ }
+
+ public static class MacroRecord {
+ private String name;
+
+ private Map attributes = new LinkedHashMap();
+
+ private List children = new ArrayList();
+
+ private Object object;
+
+ public MacroRecord(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void recordAttribute(String name, String value) {
+ attributes.put(name, value);
+ }
+
+ public MacroRecord recordChild(String name) {
+ MacroRecord child = new MacroRecord(name);
+ children.add(child);
+ return child;
+ }
+
+ public MacroRecord recordChild(String name, Object object) {
+ MacroRecord child = recordChild(name);
+ child.object = object;
+ return child;
+ }
+
+ public Map getAttributes() {
+ return attributes;
+ }
+
+ public List getChildren() {
+ return children;
+ }
+
+ public Object getObject() {
+ return object;
+ }
+ }
+
+ public static class MacroDef {
+ private String name;
+
+ private Map attributes = new HashMap();
+
+ private Map elements = new HashMap();
+
+ private MacroRecord macroRecord;
+
+ public MacroDef(String macroName) {
+ name = macroName;
+ }
+
+ public Attribute getAttribute(String attributeName) {
+ return (Attribute) attributes.get(attributeName);
+ }
+
+ public Object play(Configurator conf, Map attValues, Map macroRecords) {
+ for (Iterator iter = attributes.values().iterator(); iter.hasNext();) {
+ Attribute att = (Attribute) iter.next();
+ String val = (String) attValues.get(att.getName());
+ if (val == null) {
+ if (att.getDefault() == null) {
+ throw new IllegalArgumentException("attribute " + att.getName()
+ + " is required in " + getName());
+ } else {
+ attValues.put(att.getName(), att.getDefault());
+ }
+ }
+ }
+ return play(conf, macroRecord, attValues, macroRecords);
+ }
+
+ private Object play(Configurator conf, MacroRecord macroRecord, Map attValues,
+ Map childrenRecords) {
+ if (macroRecord.getObject() != null) {
+ // this is a recorded reference, we can add the referenced object directly
+ conf.addChild(macroRecord.getName(), macroRecord.getObject());
+ conf.endCreateChild();
+ return macroRecord.getObject();
+ }
+ conf.startCreateChild(macroRecord.getName());
+ Map attributes = macroRecord.getAttributes();
+ for (Iterator iter = attributes.keySet().iterator(); iter.hasNext();) {
+ String attName = (String) iter.next();
+ String attValue = replaceParam((String) attributes.get(attName), attValues);
+ conf.setAttribute(attName, attValue);
+ }
+ for (Iterator iter = macroRecord.getChildren().iterator(); iter.hasNext();) {
+ MacroRecord child = (MacroRecord) iter.next();
+ Element elt = (Element) elements.get(child.getName());
+ if (elt != null) {
+ List elements = (List) childrenRecords.get(child.getName());
+ if (elements != null) {
+ for (Iterator iterator = elements.iterator(); iterator.hasNext();) {
+ MacroRecord element = (MacroRecord) iterator.next();
+ for (Iterator it2 = element.getChildren().iterator(); it2.hasNext();) {
+ MacroRecord r = (MacroRecord) it2.next();
+ play(conf, r, attValues, Collections.EMPTY_MAP);
+ }
+ }
+ } else if (!elt.isOptional()) {
+ throw new IllegalArgumentException(
+ "non optional element is not specified: " + elt.getName()
+ + " in macro " + getName());
+ }
+ continue;
+ }
+ play(conf, child, attValues, childrenRecords);
+ }
+ return conf.endCreateChild();
+ }
+
+ private String replaceParam(String string, Map attValues) {
+ return IvyPatternHelper.substituteParams(string, attValues);
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void addConfiguredAttribute(Attribute att) {
+ attributes.put(att.getName(), att);
+ }
+
+ public void addConfiguredElement(Element elt) {
+ elements.put(elt.getName(), elt);
+ }
+
+ public Macro createMacro() {
+ return new Macro(this);
+ }
+
+ public void addAttribute(String attName, String attDefaultValue) {
+ Attribute att = new Attribute();
+ att.setName(attName);
+ att.setDefault(attDefaultValue);
+ addConfiguredAttribute(att);
+ }
+
+ public void addElement(String elementName, boolean optional) {
+ Element elt = new Element();
+ elt.setName(elementName);
+ elt.setOptional(optional);
+ addConfiguredElement(elt);
+ }
+
+ public MacroRecord recordCreateChild(String name) {
+ macroRecord = new MacroRecord(name);
+ return macroRecord;
+ }
+ }
+
+ private static class ObjectDescriptor {
+ private Object obj;
+
+ private String objName;
+
+ private Map createMethods = new HashMap();
+
+ private Map addMethods = new HashMap();
+
+ private Map addConfiguredMethods = new HashMap();
+
+ private Map setMethods = new HashMap();
+
+ private Map typeAddMethods = new HashMap();
+
+ private Map typeAddConfiguredMethods = new HashMap();
+
+ public ObjectDescriptor(Object object, String objName) {
+ obj = object;
+ this.objName = objName;
+ Method[] methods = object.getClass().getMethods();
+ for (int i = 0; i < methods.length; i++) {
+ Method m = methods[i];
+ if (m.getName().startsWith("create") && m.getParameterTypes().length == 0
+ && !Void.TYPE.equals(m.getReturnType())) {
+ String name = StringUtils
+ .uncapitalize(m.getName().substring("create".length()));
+ if (name.length() == 0) {
+ continue;
+ }
+ addCreateMethod(name, m);
+ } else if (m.getName().startsWith("addConfigured")
+ && m.getParameterTypes().length == 1 && Void.TYPE.equals(m.getReturnType())) {
+ String name = StringUtils.uncapitalize(m.getName().substring(
+ "addConfigured".length()));
+ if (name.length() == 0) {
+ addAddConfiguredMethod(m);
+ }
+ addAddConfiguredMethod(name, m);
+ } else if (m.getName().startsWith("add")
+ && !m.getName().startsWith("addConfigured")
+ && m.getParameterTypes().length == 1 && Void.TYPE.equals(m.getReturnType())) {
+ String name = StringUtils.uncapitalize(m.getName().substring("add".length()));
+ if (name.length() == 0) {
+ addAddMethod(m);
+ }
+ addAddMethod(name, m);
+ } else if (m.getName().startsWith("set") && m.getParameterTypes().length == 1
+ && Void.TYPE.equals(m.getReturnType())) {
+ String name = StringUtils.uncapitalize(m.getName().substring("set".length()));
+ if (name.length() == 0) {
+ continue;
+ }
+ addSetMethod(name, m);
+ }
+ }
+ }
+
+ public void addCreateMethod(String name, Method m) {
+ createMethods.put(name, m);
+ }
+
+ public void addAddMethod(String name, Method m) {
+ addMethods.put(name, m);
+ }
+
+ public void addAddConfiguredMethod(String name, Method m) {
+ addConfiguredMethods.put(name, m);
+ }
+
+ private void addAddMethod(Method m) {
+ typeAddMethods.put(m.getParameterTypes()[0], m);
+ }
+
+ private void addAddConfiguredMethod(Method m) {
+ typeAddConfiguredMethods.put(m.getParameterTypes()[0], m);
+ }
+
+ public void addSetMethod(String name, Method m) {
+ Method current = (Method) setMethods.get(name);
+ if (current != null && current.getParameterTypes()[0] == String.class) {
+ // setter methods with String attribute take precedence
+ return;
+ }
+ setMethods.put(name, m);
+ }
+
+ public Object getObject() {
+ return obj;
+ }
+
+ public Method getCreateMethod(String name) {
+ return (Method) createMethods.get(name);
+ }
+
+ public Method getAddMethod(String name) {
+ return (Method) addMethods.get(name);
+ }
+
+ public Method getAddConfiguredMethod(String name) {
+ return (Method) addConfiguredMethods.get(name);
+ }
+
+ public Method getAddMethod(Class type) {
+ return getTypeMatchingMethod(type, typeAddMethods);
+ }
+
+ public Method getAddConfiguredMethod(Class type) {
+ return getTypeMatchingMethod(type, typeAddConfiguredMethods);
+ }
+
+ private Method getTypeMatchingMethod(Class type, Map typeMethods) {
+ Method m = (Method) typeMethods.get(type);
+ if (m != null) {
+ return m;
+ }
+ for (Iterator iter = typeMethods.keySet().iterator(); iter.hasNext();) {
+ Class clss = (Class) iter.next();
+ if (clss.isAssignableFrom(type)) {
+ return (Method) typeMethods.get(clss);
+ }
+ }
+ return null;
+ }
+
+ public Method getSetMethod(String name) {
+ return (Method) setMethods.get(name);
+ }
+
+ public String getObjectName() {
+ return objName;
+ }
+ }
+
+ private FileResolver fileResolver = FileResolver.DEFAULT;
+
+ private Map typedefs = new HashMap();
+
+ private Map macrodefs = new HashMap();
+
+ // stack in which the top is current configured object descriptor
+ private Stack objectStack = new Stack();
+
+ private static final List TRUE_VALUES = Arrays.asList(new String[] {"true", "yes", "on"});
+
+ public void typeDef(String name, String className) throws ClassNotFoundException {
+ typeDef(name, Class.forName(className));
+ }
+
+ public void typeDef(String name, Class clazz) {
+ typedefs.put(name, clazz);
+ }
+
+ public void setRoot(Object root) {
+ if (root == null) {
+ throw new NullPointerException();
+ }
+ objectStack.clear();
+ setCurrent(root, null);
+ }
+
+ public void clear() {
+ objectStack.clear();
+ }
+
+ private void setCurrent(Object object, String name) {
+ objectStack.push(new ObjectDescriptor(object, name));
+ }
+
+ public Object startCreateChild(String name) {
+ if (objectStack.isEmpty()) {
+ throw new IllegalStateException("set root before creating child");
+ }
+ ObjectDescriptor parentOD = (ObjectDescriptor) objectStack.peek();
+ Object parent = parentOD.getObject();
+ if (parent instanceof MacroDef) {
+ if (!"attribute".equals(name) && !"element".equals(name)) {
+ MacroRecord record = ((MacroDef) parent).recordCreateChild(name);
+ setCurrent(record, name);
+ return record;
+ }
+ }
+ if (parent instanceof Macro) {
+ MacroRecord record = ((Macro) parent).recordCreateChild(name);
+ setCurrent(record, name);
+ return record;
+ }
+ if (parent instanceof MacroRecord) {
+ MacroRecord record = ((MacroRecord) parent).recordChild(name);
+ setCurrent(record, name);
+ return record;
+ }
+ Object child = null;
+ MacroDef macrodef = (MacroDef) macrodefs.get(name);
+ if (macrodef != null) {
+ Macro macro = macrodef.createMacro();
+ setCurrent(macro, name);
+ return macro;
+ }
+ Class childClass = (Class) typedefs.get(name);
+ Method addChild = null;
+ try {
+ if (childClass != null) {
+ return addChild(parentOD, childClass, name, null);
+ } else {
+ addChild = parentOD.getCreateMethod(name);
+ if (addChild != null) {
+ child = addChild.invoke(parent, new Object[0]);
+ setCurrent(child, name);
+ return child;
+ }
+ addChild = parentOD.getAddMethod(name);
+ if (addChild != null) {
+ childClass = addChild.getParameterTypes()[0];
+ child = childClass.newInstance();
+ addChild.invoke(parent, new Object[] {child});
+ setCurrent(child, name);
+ return child;
+ }
+ addChild = parentOD.getAddConfiguredMethod(name);
+ if (addChild != null) {
+ childClass = addChild.getParameterTypes()[0];
+ if (Map.class == childClass) {
+ child = new HashMap();
+ } else {
+ child = childClass.newInstance();
+ }
+ setCurrent(child, name);
+ return child;
+ }
+ }
+ } catch (InstantiationException ex) {
+ throw new IllegalArgumentException("no default constructor on " + childClass
+ + " for adding " + name + " on " + parent.getClass());
+ } catch (Exception ex) {
+ IllegalArgumentException iae = new IllegalArgumentException("bad method found for "
+ + name + " on " + parent.getClass());
+ iae.initCause(ex);
+ throw iae;
+ }
+ throw new IllegalArgumentException("no appropriate method found for adding " + name
+ + " on " + parent.getClass());
+ }
+
+ public void addChild(String name, Object child) {
+ if (objectStack.isEmpty()) {
+ throw new IllegalStateException("set root before creating child");
+ }
+ ObjectDescriptor parentOD = (ObjectDescriptor) objectStack.peek();
+ try {
+ addChild(parentOD, child.getClass(), name, child);
+ } catch (InstantiationException ex) {
+ throw new IllegalArgumentException("no default constructor on " + child.getClass()
+ + " for adding " + name + " on " + parentOD.getObject().getClass());
+ } catch (Exception ex) {
+ IllegalArgumentException iae = new IllegalArgumentException("bad method found for "
+ + name + " on " + parentOD.getObject().getClass());
+ iae.initCause(ex);
+ throw iae;
+ }
+ }
+
+ private Object addChild(ObjectDescriptor parentOD, Class childClass, String name, Object child)
+ throws InstantiationException, IllegalAccessException, InvocationTargetException {
+ Object parent = parentOD.getObject();
+ if (parent instanceof MacroRecord) {
+ MacroRecord record = (MacroRecord) parent;
+ MacroRecord recordChild = record.recordChild(name, child);
+ setCurrent(recordChild, name);
+ return recordChild;
+ }
+ Method addChild = parentOD.getAddMethod(childClass);
+ if (addChild != null) {
+ if (child == null) {
+ child = childClass.newInstance();
+ }
+ addChild.invoke(parent, new Object[] {child});
+ setCurrent(child, name);
+ return child;
+ }
+ addChild = parentOD.getAddConfiguredMethod(childClass);
+ if (addChild != null) {
+ if (child == null) {
+ if (Map.class == childClass) {
+ child = new HashMap();
+ } else {
+ child = childClass.newInstance();
+ }
+ }
+ setCurrent(child, name);
+ return child;
+ }
+ throw new IllegalArgumentException("no appropriate method found for adding " + name
+ + " on " + parent.getClass());
+ }
+
+ public boolean isTopLevelMacroRecord() {
+ if (objectStack.isEmpty()) {
+ return false;
+ }
+ ObjectDescriptor od = (ObjectDescriptor) objectStack.peek();
+ return (od.getObject() instanceof MacroDef);
+ }
+
+ public void setAttribute(String attributeName, String value) {
+ if (objectStack.isEmpty()) {
+ throw new IllegalStateException("set root before setting attribute");
+ }
+ ObjectDescriptor od = (ObjectDescriptor) objectStack.peek();
+ if (od.getObject() instanceof Macro) {
+ ((Macro) od.getObject()).defineAttribute(attributeName, value);
+ return;
+ }
+ if (od.getObject() instanceof MacroRecord) {
+ ((MacroRecord) od.getObject()).recordAttribute(attributeName, value);
+ return;
+ }
+ Method m = od.getSetMethod(attributeName);
+ if (m == null) {
+ if (od.getObject() instanceof Map) {
+ ((Map) od.getObject()).put(attributeName, value);
+ return;
+ }
+ throw new IllegalArgumentException("no set method found for " + attributeName + " on "
+ + od.getObject().getClass());
+ }
+ Object convertedValue = null;
+ Class paramClass = m.getParameterTypes()[0];
+ try {
+ if (paramClass.equals(String.class)) {
+ convertedValue = value;
+ } else if (paramClass.equals(Boolean.class) || paramClass.equals(boolean.class)) {
+ convertedValue = Boolean.valueOf(TRUE_VALUES.contains(value));
+ } else if (paramClass.equals(Character.class) || paramClass.equals(char.class)) {
+ convertedValue = new Character(value.length() > 0 ? value.charAt(0) : ' ');
+ } else if (paramClass.equals(Short.class) || paramClass.equals(short.class)) {
+ convertedValue = Short.valueOf(value);
+ } else if (paramClass.equals(Integer.class) || paramClass.equals(int.class)) {
+ convertedValue = Integer.valueOf(value);
+ } else if (paramClass.equals(Long.class) || paramClass.equals(long.class)) {
+ convertedValue = Long.valueOf(value);
+ } else if (paramClass.equals(Class.class)) {
+ convertedValue = Class.forName(value);
+ } else if (paramClass.equals(File.class)) {
+ convertedValue = fileResolver.resolveFile(value, od.getObjectName() + "."
+ + attributeName);
+ } else {
+ convertedValue = paramClass.getConstructor(new Class[] {String.class}).newInstance(
+ new Object[] {value});
+ }
+ } catch (Exception ex) {
+ IllegalArgumentException iae = new IllegalArgumentException("impossible to convert "
+ + value + " to " + paramClass + " for setting " + attributeName + " on "
+ + od.getObject().getClass() + ": " + ex.getMessage());
+ iae.initCause(ex);
+ throw iae;
+ }
+ try {
+ m.invoke(od.getObject(), new Object[] {convertedValue});
+ } catch (Exception ex) {
+ IllegalArgumentException iae = new IllegalArgumentException("impossible to set "
+ + attributeName + " to " + convertedValue + " on " + od.getObject().getClass());
+ iae.initCause(ex);
+ throw iae;
+ }
+ }
+
+ public void addText(String text) {
+ if (objectStack.isEmpty()) {
+ throw new IllegalStateException("set root before adding text");
+ }
+ ObjectDescriptor od = (ObjectDescriptor) objectStack.peek();
+ try {
+ od.getObject().getClass().getMethod("addText", new Class[] {String.class})
+ .invoke(od.getObject(), new Object[] {text});
+ } catch (Exception ex) {
+ IllegalArgumentException iae = new IllegalArgumentException(
+ "impossible to add text on " + od.getObject().getClass());
+ iae.initCause(ex);
+ throw iae;
+ }
+ }
+
+ /**
+ * @return the finished child
+ */
+ public Object endCreateChild() {
+ if (objectStack.isEmpty()) {
+ throw new IllegalStateException("set root before ending child");
+ }
+ ObjectDescriptor od = (ObjectDescriptor) objectStack.pop();
+ if (objectStack.isEmpty()) {
+ objectStack.push(od); // back to previous state
+ throw new IllegalStateException("cannot end root");
+ }
+ if (od.getObject() instanceof Macro) {
+ return ((Macro) od.getObject()).play(this);
+ }
+ ObjectDescriptor parentOD = (ObjectDescriptor) objectStack.peek();
+ String name = od.getObjectName();
+ Class childClass = (Class) typedefs.get(name);
+ Method m = null;
+ if (childClass != null) {
+ m = parentOD.getAddConfiguredMethod(childClass);
+ } else {
+ m = parentOD.getAddConfiguredMethod(name);
+ }
+ try {
+ if (m != null) {
+ m.invoke(parentOD.getObject(), new Object[] {od.getObject()});
+ }
+ return od.getObject();
+ } catch (Exception ex) {
+ IllegalArgumentException iae = new IllegalArgumentException(
+ "impossible to add configured child for " + name + " on "
+ + parentOD.getObject().getClass() + ": "
+ + StringUtils.getErrorMessage(ex));
+ iae.initCause(ex);
+ throw iae;
+ }
+ }
+
+ public Object getCurrent() {
+ return objectStack.isEmpty() ? null : ((ObjectDescriptor) objectStack.peek()).getObject();
+ }
+
+ public int getDepth() {
+ return objectStack.size();
+ }
+
+ public MacroDef startMacroDef(String macroName) {
+ MacroDef macroDef = new MacroDef(macroName);
+ setCurrent(macroDef, macroName);
+ return macroDef;
+ }
+
+ public void addMacroAttribute(String attName, String attDefaultValue) {
+ ((MacroDef) getCurrent()).addAttribute(attName, attDefaultValue);
+ }
+
+ public void addMacroElement(String elementName, boolean optional) {
+ ((MacroDef) getCurrent()).addElement(elementName, optional);
+ }
+
+ public void endMacroDef() {
+ addConfiguredMacrodef(((MacroDef) getCurrent()));
+ objectStack.pop();
+ }
+
+ public void addConfiguredMacrodef(MacroDef macrodef) {
+ macrodefs.put(macrodef.getName(), macrodef);
+ }
+
+ public Class getTypeDef(String name) {
+ return (Class) typedefs.get(name);
+ }
+
+ public FileResolver getFileResolver() {
+ return fileResolver;
+ }
+
+ public void setFileResolver(FileResolver fileResolver) {
+ Checks.checkNotNull(fileResolver, "fileResolver");
+ this.fileResolver = fileResolver;
+ }
+}
diff --git a/src/java/org/apache/ivy/util/ContextualSAXHandler.java b/src/java/org/apache/ivy/util/ContextualSAXHandler.java
new file mode 100644
index 0000000..daf85c0
--- /dev/null
+++ b/src/java/org/apache/ivy/util/ContextualSAXHandler.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.util;
+
+import java.util.Iterator;
+import java.util.Stack;
+
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+public class ContextualSAXHandler extends DefaultHandler {
+
+ private Stack contextStack = new Stack();
+
+ private StringBuffer buffer = new StringBuffer();
+
+ public void characters(char[] ch, int start, int length) throws SAXException {
+ buffer.append(ch, start, length);
+ }
+
+ public void startElement(String uri, String localName, String qName, Attributes attributes)
+ throws SAXException {
+ contextStack.push(qName);
+ buffer.setLength(0);
+ }
+
+ public void endElement(String uri, String localName, String qName) throws SAXException {
+ contextStack.pop();
+ buffer.setLength(0);
+ }
+
+ protected String getContext() {
+ StringBuffer buf = new StringBuffer();
+ for (Iterator iter = contextStack.iterator(); iter.hasNext();) {
+ String ctx = (String) iter.next();
+ buf.append(ctx).append("/");
+ }
+ if (buf.length() > 0) {
+ buf.setLength(buf.length() - 1);
+ }
+ return buf.toString();
+ }
+
+ protected String getText() {
+ return buffer.toString();
+ }
+
+}
diff --git a/src/java/org/apache/ivy/util/CopyProgressEvent.java b/src/java/org/apache/ivy/util/CopyProgressEvent.java
new file mode 100644
index 0000000..c71e116
--- /dev/null
+++ b/src/java/org/apache/ivy/util/CopyProgressEvent.java
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.util;
+
+/**
+ * Event reporting a stream copy progression
+ */
+public class CopyProgressEvent {
+ private long totalReadBytes;
+
+ private byte[] buffer;
+
+ private int readBytes;
+
+ public CopyProgressEvent() {
+ }
+
+ public CopyProgressEvent(byte[] buffer, int read, long total) {
+ update(buffer, read, total);
+ }
+
+ public CopyProgressEvent(byte[] buffer, long total) {
+ update(buffer, 0, total);
+ }
+
+ protected CopyProgressEvent update(byte[] buffer, int read, long total) {
+ this.buffer = buffer;
+ this.readBytes = read;
+ this.totalReadBytes = total;
+ return this;
+ }
+
+ public long getTotalReadBytes() {
+ return totalReadBytes;
+ }
+
+ public byte[] getBuffer() {
+ return buffer;
+ }
+
+ public int getReadBytes() {
+ return readBytes;
+ }
+
+}
diff --git a/src/java/org/apache/ivy/util/CopyProgressListener.java b/src/java/org/apache/ivy/util/CopyProgressListener.java
new file mode 100644
index 0000000..2dbaa2e
--- /dev/null
+++ b/src/java/org/apache/ivy/util/CopyProgressListener.java
@@ -0,0 +1,29 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.util;
+
+/**
+ * Listen to copy progression
+ */
+public interface CopyProgressListener {
+ void start(CopyProgressEvent evt);
+
+ void progress(CopyProgressEvent evt);
+
+ void end(CopyProgressEvent evt);
+}
diff --git a/src/java/org/apache/ivy/util/Credentials.java b/src/java/org/apache/ivy/util/Credentials.java
new file mode 100644
index 0000000..682e35a
--- /dev/null
+++ b/src/java/org/apache/ivy/util/Credentials.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.util;
+
+/**
+ *
+ */
+public class Credentials {
+ private String realm;
+
+ private String host;
+
+ private String userName;
+
+ private String passwd;
+
+ public Credentials(String realm, String host, String userName, String passwd) {
+ this.realm = realm;
+ this.host = host;
+ this.userName = userName;
+ this.passwd = passwd;
+ }
+
+ public String getHost() {
+ return host;
+ }
+
+ public String getPasswd() {
+ return passwd;
+ }
+
+ public String getRealm() {
+ return realm;
+ }
+
+ public String getUserName() {
+ return userName;
+ }
+
+ public static String buildKey(String realm, String host) {
+ if (realm == null || "".equals(realm.trim())) {
+ return host;
+ } else {
+ return realm + "@" + host;
+ }
+ }
+
+ /**
+ * Return a string that can be used for debug purpose. It contains only stars for each password
+ * character.
+ */
+ public String toString() {
+ return getKey() + " " + getUserName() + "/" + getPasswdAsStars();
+ }
+
+ private String getPasswdAsStars() {
+ if (passwd == null) {
+ return null;
+ }
+ StringBuffer sb = new StringBuffer();
+ for (int i = passwd.length(); i > 0; i--) {
+ sb.append('*');
+ }
+ return sb.toString();
+ }
+
+ public boolean equals(Object o) {
+ if (o == null) {
+ return false;
+ }
+
+ if (o instanceof Credentials) {
+ Credentials c = (Credentials) o;
+ return getKey().equals(c.getKey());
+ }
+
+ return false;
+ }
+
+ public int hashCode() {
+ return getKey().hashCode();
+ }
+
+ public String getKey() {
+ return buildKey(realm, host);
+ }
+}
diff --git a/src/java/org/apache/ivy/util/CredentialsUtil.java b/src/java/org/apache/ivy/util/CredentialsUtil.java
new file mode 100644
index 0000000..68a0ba8
--- /dev/null
+++ b/src/java/org/apache/ivy/util/CredentialsUtil.java
@@ -0,0 +1,167 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.util;
+
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Properties;
+
+import javax.swing.ImageIcon;
+import javax.swing.JCheckBox;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JPasswordField;
+import javax.swing.JTextField;
+
+import org.apache.ivy.Ivy;
+
+public final class CredentialsUtil {
+
+ private static final class CredentialPanel extends JPanel {
+ private static final int FIELD_LENGTH = 20;
+
+ private JTextField userNameField = new JTextField(FIELD_LENGTH);
+
+ private JTextField passwordField = new JPasswordField(FIELD_LENGTH);
+
+ private JCheckBox rememberDataCB = new JCheckBox("remember my information");
+
+ CredentialPanel(Credentials credentials, File passfile) {
+ GridBagLayout layout = new GridBagLayout();
+ setLayout(layout);
+ GridBagConstraints c = new GridBagConstraints();
+ c.insets = new Insets(2, 2, 2, 2);
+
+ c.gridx = 1;
+ c.gridheight = 1;
+ c.gridwidth = 2;
+ String prompt = credentials.getRealm() != null ? "Enter username and password for \""
+ + credentials.getRealm() + "\" at " + credentials.getHost()
+ : "Enter username and password for " + credentials.getHost();
+ add(new JLabel(prompt), c);
+
+ c.gridy = 1;
+ c.gridwidth = 1;
+
+ add(new JLabel("username: "), c);
+ c.gridx = 2;
+ add(userNameField, c);
+ c.gridx = 1;
+ c.gridy++;
+
+ if (credentials.getUserName() != null) {
+ userNameField.setText(credentials.getUserName());
+ }
+
+ if (credentials.getPasswd() == null) {
+ add(new JLabel("passwd: "), c);
+ c.gridx = 2;
+ add(passwordField, c);
+ c.gridx = 1;
+ c.gridy++;
+ } else {
+ passwordField.setText(credentials.getPasswd());
+ }
+
+ if (passfile != null) {
+ c.gridwidth = 2;
+ add(rememberDataCB, c);
+ c.gridy++;
+ }
+ c.gridwidth = 2;
+ add(new JLabel(), c); // spacer
+
+ }
+ }
+
+ public static Credentials promptCredentials(Credentials c, File passfile) {
+ c = loadPassfile(c, passfile);
+ if (c.getUserName() != null && c.getPasswd() != null) {
+ return c;
+ }
+ CredentialPanel credentialPanel = new CredentialPanel(c, passfile);
+ if (JOptionPane.showOptionDialog(null, credentialPanel, c.getHost() + " credentials",
+ JOptionPane.OK_CANCEL_OPTION, 0, new ImageIcon(Ivy.class.getResource("logo.png")),
+ null, new Integer(JOptionPane.OK_OPTION)) == JOptionPane.OK_OPTION) {
+ String username = credentialPanel.userNameField.getText();
+ String passwd = credentialPanel.passwordField.getText();
+ if (credentialPanel.rememberDataCB.isSelected()) {
+ Properties props = new EncrytedProperties();
+ props.setProperty("username", username);
+ props.setProperty("passwd", passwd);
+ FileOutputStream fos = null;
+ try {
+ fos = new FileOutputStream(passfile);
+ props.store(fos, "");
+ } catch (Exception e) {
+ Message.warn("error occurred while saving password file " + passfile, e);
+ } finally {
+ if (fos != null) {
+ try {
+ fos.close();
+ } catch (Exception e) {
+ // ignored
+ }
+ }
+ }
+ }
+ c = new Credentials(c.getRealm(), c.getHost(), username, passwd);
+ }
+ return c;
+ }
+
+ public static Credentials loadPassfile(Credentials c, File passfile) {
+ if (passfile != null && passfile.exists()) {
+ Properties props = new EncrytedProperties();
+ FileInputStream fis = null;
+ try {
+ fis = new FileInputStream(passfile);
+ props.load(fis);
+ String username = c.getUserName();
+ String passwd = c.getPasswd();
+ if (username == null) {
+ username = props.getProperty("username");
+ }
+ if (passwd == null) {
+ passwd = props.getProperty("passwd");
+ }
+ return new Credentials(c.getRealm(), c.getHost(), username, passwd);
+ } catch (IOException e) {
+ Message.warn("error occurred while loading password file " + passfile, e);
+ } finally {
+ if (fis != null) {
+ try {
+ fis.close();
+ } catch (IOException e) {
+ // ignored
+ }
+ }
+ }
+ }
+ return c;
+ }
+
+ private CredentialsUtil() {
+ }
+}
diff --git a/src/java/org/apache/ivy/util/DateUtil.java b/src/java/org/apache/ivy/util/DateUtil.java
new file mode 100644
index 0000000..fe66e1d
--- /dev/null
+++ b/src/java/org/apache/ivy/util/DateUtil.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.util;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+public class DateUtil {
+
+ private DateUtil() {
+ // Utility class
+ }
+
+ public static final String DATE_FORMAT_PATTERN = "yyyyMMddHHmmss";
+
+ public static String format(Date date) {
+ SimpleDateFormat format = new SimpleDateFormat(DATE_FORMAT_PATTERN);
+ return format.format(date);
+ }
+
+ public static Date parse(String date) throws ParseException {
+ SimpleDateFormat format = new SimpleDateFormat(DATE_FORMAT_PATTERN);
+ return format.parse(date);
+ }
+
+}
diff --git a/src/java/org/apache/ivy/util/DefaultMessageLogger.java b/src/java/org/apache/ivy/util/DefaultMessageLogger.java
new file mode 100644
index 0000000..7a88a36
--- /dev/null
+++ b/src/java/org/apache/ivy/util/DefaultMessageLogger.java
@@ -0,0 +1,51 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.util;
+
+public class DefaultMessageLogger extends AbstractMessageLogger {
+ private int level = Message.MSG_INFO;
+
+ /**
+ * @param level
+ */
+ public DefaultMessageLogger(int level) {
+ this.level = level;
+ }
+
+ public void log(String msg, int level) {
+ if (level <= this.level) {
+ System.out.println(msg);
+ }
+ }
+
+ public void rawlog(String msg, int level) {
+ log(msg, level);
+ }
+
+ public void doProgress() {
+ System.out.print(".");
+ }
+
+ public void doEndProgress(String msg) {
+ System.out.println(msg);
+ }
+
+ public int getLevel() {
+ return level;
+ }
+}
diff --git a/src/java/org/apache/ivy/util/EncrytedProperties.java b/src/java/org/apache/ivy/util/EncrytedProperties.java
new file mode 100644
index 0000000..8d38fef
--- /dev/null
+++ b/src/java/org/apache/ivy/util/EncrytedProperties.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.util;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Properties;
+
+/**
+ * An implementation of Properties which stores the values encrypted. The use is transparent from
+ * the user point of view (use as any Properties instance), except that get, put and putAll do not
+ * handle encryption/decryption. This means that get returns the encrypted value, while put and
+ * putAll puts given values without encrypting them. It this thus recommended to void using them,
+ * use setProperty and getProperty instead.
+ */
+public class EncrytedProperties extends Properties {
+
+ public EncrytedProperties() {
+ super();
+ }
+
+ public synchronized Object setProperty(String key, String value) {
+ return StringUtils.decrypt((String) super.setProperty(key, StringUtils.encrypt(value)));
+ }
+
+ public String getProperty(String key) {
+ return StringUtils.decrypt(super.getProperty(key));
+ }
+
+ public String getProperty(String key, String defaultValue) {
+ return StringUtils.decrypt(super.getProperty(key, StringUtils.encrypt(defaultValue)));
+ }
+
+ public boolean containsValue(Object value) {
+ return super.containsValue(StringUtils.encrypt((String) value));
+ }
+
+ public synchronized boolean contains(Object value) {
+ return super.contains(StringUtils.encrypt((String) value));
+ }
+
+ public Collection values() {
+ List ret = new ArrayList(super.values());
+ for (int i = 0; i < ret.size(); i++) {
+ ret.set(i, StringUtils.decrypt((String) ret.get(i)));
+ }
+ return ret;
+ }
+}
diff --git a/src/java/org/apache/ivy/util/FileResolver.java b/src/java/org/apache/ivy/util/FileResolver.java
new file mode 100644
index 0000000..2827519
--- /dev/null
+++ b/src/java/org/apache/ivy/util/FileResolver.java
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.util;
+
+import java.io.File;
+
+public interface FileResolver {
+ public static final FileResolver DEFAULT = new FileResolver() {
+ public File resolveFile(String path, String filename) {
+ return new File(path);
+ }
+ };
+
+ /**
+ * Return the canonical form of a path, or raise an {@link IllegalArgumentException} if the path
+ * is not valid for this {@link FileResolver}.
+ * <p>
+ *
+ * @param path
+ * The path of the file to resolve. Must not be <code>null</code>.
+ * @param fileName
+ * The name of the file to resolve. This is used only for exception message if any.
+ * Must not be <code>null</code>.
+ *
+ * @return the resolved File.
+ *
+ */
+ File resolveFile(String path, String filename);
+}
diff --git a/src/java/org/apache/ivy/util/FileUtil.java b/src/java/org/apache/ivy/util/FileUtil.java
new file mode 100644
index 0000000..7a7be31
--- /dev/null
+++ b/src/java/org/apache/ivy/util/FileUtil.java
@@ -0,0 +1,731 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.util;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Stack;
+import java.util.StringTokenizer;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Pack200;
+import java.util.jar.Pack200.Unpacker;
+import java.util.regex.Pattern;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.ZipInputStream;
+
+import org.apache.ivy.util.url.URLHandlerRegistry;
+
+/**
+ * Utility class used to deal with file related operations, like copy, full reading, symlink, ...
+ */
+public final class FileUtil {
+
+ private FileUtil() {
+ // Utility class
+ }
+
+ // according to tests by users, 64kB seems to be a good value for the buffer used during copy
+ // further improvements could be obtained using NIO API
+ private static final int BUFFER_SIZE = 64 * 1024;
+
+ private static final byte[] EMPTY_BUFFER = new byte[0];
+
+ private static final Pattern ALLOWED_PATH_PATTERN = Pattern.compile("[\\w-./\\\\:~ %\\(\\)]+");
+
+ public static void symlinkInMass(Map/* <File, File> */destToSrcMap, boolean overwrite)
+ throws IOException {
+
+ // This pattern could be more forgiving if somebody wanted it to be...
+ // ...but this should satisfy 99+% of all needs, without letting unsafe operations be done.
+ // If you paths is not supported, you then skip this mass option.
+ // NOTE: A space inside the path is allowed (I can't control other programmers who like them
+ // in their working directory names)...
+ // but trailing spaces on file names will be checked otherwise and refused.
+ try {
+ StringBuffer sb = new StringBuffer();
+
+ Iterator keyItr = destToSrcMap.entrySet().iterator();
+ while (keyItr.hasNext()) {
+ Entry/* <File, File> */entry = (Entry) keyItr.next();
+ File destFile = (File) entry.getKey();
+ File srcFile = (File) entry.getValue();
+ if (!ALLOWED_PATH_PATTERN.matcher(srcFile.getAbsolutePath()).matches()) {
+ throw new IOException("Unsafe file to 'mass' symlink: '"
+ + srcFile.getAbsolutePath() + "'");
+ }
+ if (!ALLOWED_PATH_PATTERN.matcher(destFile.getAbsolutePath()).matches()) {
+ throw new IOException("Unsafe file to 'mass' symlink to: '"
+ + destFile.getAbsolutePath() + "'");
+ }
+
+ // Add to our buffer of commands
+ sb.append("ln -s -f \"" + srcFile.getAbsolutePath() + "\" \""
+ + destFile.getAbsolutePath() + "\";");
+ if (keyItr.hasNext()) {
+ sb.append("\n");
+ }
+ }
+
+ String commands = sb.toString();
+ // Run the buffer of commands we have built.
+ Runtime runtime = Runtime.getRuntime();
+ Message.verbose("executing \"sh\" of:\n\t" + commands.replaceAll("\n", "\n\t"));
+ Process process = runtime.exec("sh");
+ OutputStream os = process.getOutputStream();
+ os.write(commands.getBytes("UTF-8"));
+ os.flush();
+ os.close();
+
+ if (process.waitFor() != 0) {
+ InputStream errorStream = process.getErrorStream();
+ InputStreamReader isr = new InputStreamReader(errorStream);
+ BufferedReader br = new BufferedReader(isr);
+
+ StringBuffer error = new StringBuffer();
+ String line;
+ while ((line = br.readLine()) != null) {
+ error.append(line);
+ error.append('\n');
+ }
+
+ throw new IOException("error running ln commands with 'sh':\n" + error);
+ }
+ } catch (InterruptedException x) {
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ public static void symlink(File src, File dest, CopyProgressListener l, boolean overwrite)
+ throws IOException {
+ if (!prepareCopy(src, dest, overwrite)) {
+ return;
+ }
+ try {
+ Runtime runtime = Runtime.getRuntime();
+ Message.verbose("executing 'ln -s -f " + src.getAbsolutePath() + " " + dest.getPath()
+ + "'");
+ Process process = runtime.exec(new String[] {"ln", "-s", "-f", src.getAbsolutePath(),
+ dest.getPath()});
+
+ if (process.waitFor() != 0) {
+ InputStream errorStream = process.getErrorStream();
+ InputStreamReader isr = new InputStreamReader(errorStream);
+ BufferedReader br = new BufferedReader(isr);
+
+ StringBuffer error = new StringBuffer();
+ String line;
+ while ((line = br.readLine()) != null) {
+ error.append(line);
+ error.append('\n');
+ }
+
+ throw new IOException("error symlinking " + src + " to " + dest + ":\n" + error);
+ }
+
+ // check if the creation of the symbolic link was successful
+ if (!dest.exists()) {
+ throw new IOException("error symlinking: " + dest + " doesn't exists");
+ }
+
+ // check if the result is a true symbolic link
+ if (dest.getAbsolutePath().equals(dest.getCanonicalPath())) {
+ dest.delete(); // just make sure we do delete the invalid symlink!
+ throw new IOException("error symlinking: " + dest + " isn't a symlink");
+ }
+ } catch (IOException e) {
+ Message.verbose("symlink failed; falling back to copy", e);
+ copy(src, dest, l, overwrite);
+ } catch (InterruptedException x) {
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ public static boolean copy(File src, File dest, CopyProgressListener l) throws IOException {
+ return copy(src, dest, l, false);
+ }
+
+ public static boolean prepareCopy(File src, File dest, boolean overwrite) throws IOException {
+ if (src.isDirectory()) {
+ if (dest.exists()) {
+ if (!dest.isDirectory()) {
+ throw new IOException("impossible to copy: destination is not a directory: "
+ + dest);
+ }
+ } else {
+ dest.mkdirs();
+ }
+ return true;
+ }
+ // else it is a file copy
+ if (dest.exists()) {
+ if (!dest.isFile()) {
+ throw new IOException("impossible to copy: destination is not a file: " + dest);
+ }
+ if (overwrite) {
+ if (!dest.canWrite()) {
+ dest.delete();
+ } // if dest is writable, the copy will overwrite it without requiring a delete
+ } else {
+ Message.verbose(dest + " already exists, nothing done");
+ return false;
+ }
+ }
+ if (dest.getParentFile() != null) {
+ dest.getParentFile().mkdirs();
+ }
+ return true;
+ }
+
+ public static boolean copy(File src, File dest, CopyProgressListener l, boolean overwrite)
+ throws IOException {
+ if (!prepareCopy(src, dest, overwrite)) {
+ return false;
+ }
+ if (src.isDirectory()) {
+ return deepCopy(src, dest, l, overwrite);
+ }
+ // else it is a file copy
+ copy(new FileInputStream(src), dest, l);
+ long srcLen = src.length();
+ long destLen = dest.length();
+ if (srcLen != destLen) {
+ dest.delete();
+ throw new IOException("size of source file " + src.toString() + "(" + srcLen
+ + ") differs from size of dest file " + dest.toString() + "(" + destLen
+ + ") - please retry");
+ }
+ dest.setLastModified(src.lastModified());
+ return true;
+ }
+
+ public static boolean deepCopy(File src, File dest, CopyProgressListener l, boolean overwrite)
+ throws IOException {
+ // the list of files which already exist in the destination folder
+ List/* <File> */existingChild = Collections.EMPTY_LIST;
+ if (dest.exists()) {
+ if (!dest.isDirectory()) {
+ // not expected type, remove
+ dest.delete();
+ // and create a folder
+ dest.mkdirs();
+ dest.setLastModified(src.lastModified());
+ } else {
+ // existing folder, gather existing children
+ File[] children = dest.listFiles();
+ if (children != null) {
+ existingChild = Arrays.asList(children);
+ }
+ }
+ } else {
+ dest.mkdirs();
+ dest.setLastModified(src.lastModified());
+ }
+ // copy files one by one
+ File[] toCopy = src.listFiles();
+ if (toCopy != null) {
+ for (int i = 0; i < toCopy.length; i++) {
+ // compute the destination file
+ File childDest = new File(dest, toCopy[i].getName());
+ // if file existing, 'mark' it as taken care of
+ existingChild.remove(childDest);
+ if (toCopy[i].isDirectory()) {
+ deepCopy(toCopy[i], childDest, l, overwrite);
+ } else {
+ copy(toCopy[i], childDest, l, overwrite);
+ }
+ }
+ }
+ // some file exist in the destination but not in the source: delete them
+ for (int i = 0; i < existingChild.size(); i++) {
+ forceDelete((File) existingChild.get(i));
+ }
+ return true;
+ }
+
+ public static void copy(URL src, File dest, CopyProgressListener l) throws IOException {
+ URLHandlerRegistry.getDefault().download(src, dest, l);
+ }
+
+ public static void copy(File src, URL dest, CopyProgressListener l) throws IOException {
+ URLHandlerRegistry.getDefault().upload(src, dest, l);
+ }
+
+ public static void copy(InputStream src, File dest, CopyProgressListener l) throws IOException {
+ if (dest.getParentFile() != null) {
+ dest.getParentFile().mkdirs();
+ }
+ copy(src, new FileOutputStream(dest), l);
+ }
+
+ public static void copy(InputStream src, OutputStream dest, CopyProgressListener l)
+ throws IOException {
+ copy(src, dest, l, true);
+ }
+
+ public static void copy(InputStream src, OutputStream dest, CopyProgressListener l,
+ boolean autoClose) throws IOException {
+ CopyProgressEvent evt = null;
+ if (l != null) {
+ evt = new CopyProgressEvent();
+ }
+ try {
+ byte[] buffer = new byte[BUFFER_SIZE];
+ int c;
+ long total = 0;
+
+ if (l != null) {
+ l.start(evt);
+ }
+ while ((c = src.read(buffer)) != -1) {
+ if (Thread.currentThread().isInterrupted()) {
+ throw new IOException("transfer interrupted");
+ }
+ dest.write(buffer, 0, c);
+ total += c;
+ if (l != null) {
+ l.progress(evt.update(buffer, c, total));
+ }
+ }
+
+ if (l != null) {
+ evt.update(EMPTY_BUFFER, 0, total);
+ }
+
+ try {
+ dest.flush();
+ } catch (IOException ex) {
+ // ignore
+ }
+
+ // close the streams
+ if (autoClose) {
+ src.close();
+ dest.close();
+ }
+ } finally {
+ if (autoClose) {
+ try {
+ src.close();
+ } catch (IOException ex) {
+ // ignore
+ }
+ try {
+ dest.close();
+ } catch (IOException ex) {
+ // ignore
+ }
+ }
+ }
+
+ if (l != null) {
+ l.end(evt);
+ }
+ }
+
+ /**
+ * Reads the whole BufferedReader line by line, using \n as line separator for each line.
+ * <p>
+ * Note that this method will add a final \n to the last line even though there is no new line
+ * character at the end of last line in the original reader.
+ * </p>
+ * <p>
+ * The BufferedReader is closed when this method returns.
+ * </p>
+ *
+ * @param in
+ * the {@link BufferedReader} to read from
+ * @return a String with the whole content read from the {@link BufferedReader}
+ * @throws IOException
+ * if an IO problems occur during reading
+ */
+ public static String readEntirely(BufferedReader in) throws IOException {
+ try {
+ StringBuffer buf = new StringBuffer();
+
+ String line = in.readLine();
+ while (line != null) {
+ buf.append(line + "\n");
+ line = in.readLine();
+ }
+ return buf.toString();
+ } finally {
+ in.close();
+ }
+ }
+
+ /**
+ * Reads the entire content of the file and returns it as a String.
+ *
+ * @param f
+ * the file to read from
+ * @return a String with the file content
+ * @throws IOException
+ * if an IO problems occurs during reading
+ */
+ public static String readEntirely(File f) throws IOException {
+ return readEntirely(new FileInputStream(f));
+ }
+
+ /**
+ * Reads the entire content of the {@link InputStream} and returns it as a String.
+ * <p>
+ * The input stream is closed when this method returns.
+ * </p>
+ *
+ * @param is
+ * the {@link InputStream} to read from
+ * @return a String with the input stream content
+ * @throws IOException
+ * if an IO problems occurs during reading
+ */
+ public static String readEntirely(InputStream is) throws IOException {
+ try {
+ StringBuffer sb = new StringBuffer();
+ byte[] buffer = new byte[BUFFER_SIZE];
+ int c;
+
+ while ((c = is.read(buffer)) != -1) {
+ sb.append(new String(buffer, 0, c));
+ }
+ return sb.toString();
+ } finally {
+ is.close();
+ }
+ }
+
+ public static String concat(String dir, String file) {
+ return dir + "/" + file;
+ }
+
+ /**
+ * Recursively delete file
+ *
+ * @param file
+ * the file to delete
+ * @return true if the deletion completed successfully (ie if the file does not exist on the
+ * filesystem after this call), false if a deletion was not performed successfully.
+ */
+ public static boolean forceDelete(File file) {
+ if (!file.exists()) {
+ return true;
+ }
+ if (file.isDirectory()) {
+ File[] files = file.listFiles();
+ if (files != null) {
+ for (int i = 0; i < files.length; i++) {
+ if (!forceDelete(files[i])) {
+ return false;
+ }
+ }
+ }
+ }
+ return file.delete();
+ }
+
+ /**
+ * Returns a list of Files composed of all directories being parent of file and child of root +
+ * file and root themselves. Example: getPathFiles(new File("test"), new
+ * File("test/dir1/dir2/file.txt")) => {new File("test/dir1"), new File("test/dir1/dir2"), new
+ * File("test/dir1/dir2/file.txt") } Note that if root is not an ancester of file, or if root is
+ * null, all directories from the file system root will be returned.
+ */
+ public static List getPathFiles(File root, File file) {
+ List ret = new ArrayList();
+ while (file != null && !file.getAbsolutePath().equals(root.getAbsolutePath())) {
+ ret.add(file);
+ file = file.getParentFile();
+ }
+ if (root != null) {
+ ret.add(root);
+ }
+ Collections.reverse(ret);
+ return ret;
+ }
+
+ /**
+ * Returns a collection of all Files being contained in the given directory, recursively,
+ * including directories.
+ *
+ * @param dir
+ * The directory from which all files, including files in subdirectory) are
+ * extracted.
+ * @param ignore
+ * a Collection of filenames which must be excluded from listing
+ * @return A collectoin containing all the files of the given directory and it's subdirectories.
+ */
+ public static Collection listAll(File dir, Collection ignore) {
+ return listAll(dir, new ArrayList(), ignore);
+ }
+
+ private static Collection listAll(File file, Collection list, Collection ignore) {
+ if (ignore.contains(file.getName())) {
+ return list;
+ }
+
+ if (file.exists()) {
+ list.add(file);
+ }
+ if (file.isDirectory()) {
+ File[] files = file.listFiles();
+ for (int i = 0; i < files.length; i++) {
+ listAll(files[i], list, ignore);
+ }
+ }
+ return list;
+ }
+
+ public static File resolveFile(File file, String filename) {
+ File result = new File(filename);
+ if (!result.isAbsolute()) {
+ result = new File(file, filename);
+ }
+
+ return normalize(result.getPath());
+ }
+
+ // ////////////////////////////////////////////
+ // The following code comes from Ant FileUtils
+ // ////////////////////////////////////////////
+
+ /**
+ * "Normalize" the given absolute path.
+ *
+ * <p>
+ * This includes:
+ * <ul>
+ * <li>Uppercase the drive letter if there is one.</li>
+ * <li>Remove redundant slashes after the drive spec.</li>
+ * <li>Resolve all ./, .\, ../ and ..\ sequences.</li>
+ * <li>DOS style paths that start with a drive letter will have \ as the separator.</li>
+ * </ul>
+ * Unlike {@link File#getCanonicalPath()} this method specifically does not resolve symbolic
+ * links.
+ *
+ * @param path
+ * the path to be normalized.
+ * @return the normalized version of the path.
+ *
+ * @throws java.lang.NullPointerException
+ * if path is null.
+ */
+ public static File normalize(final String path) {
+ Stack s = new Stack();
+ String[] dissect = dissect(path);
+ s.push(dissect[0]);
+
+ StringTokenizer tok = new StringTokenizer(dissect[1], File.separator);
+ while (tok.hasMoreTokens()) {
+ String thisToken = tok.nextToken();
+ if (".".equals(thisToken)) {
+ continue;
+ }
+ if ("..".equals(thisToken)) {
+ if (s.size() < 2) {
+ // Cannot resolve it, so skip it.
+ return new File(path);
+ }
+ s.pop();
+ } else { // plain component
+ s.push(thisToken);
+ }
+ }
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < s.size(); i++) {
+ if (i > 1) {
+ // not before the filesystem root and not after it, since root
+ // already contains one
+ sb.append(File.separatorChar);
+ }
+ sb.append(s.elementAt(i));
+ }
+ return new File(sb.toString());
+ }
+
+ /**
+ * Dissect the specified absolute path.
+ *
+ * @param path
+ * the path to dissect.
+ * @return String[] {root, remaining path}.
+ * @throws java.lang.NullPointerException
+ * if path is null.
+ * @since Ant 1.7
+ */
+ private static String[] dissect(String path) {
+ char sep = File.separatorChar;
+ path = path.replace('/', sep).replace('\\', sep);
+
+ // // make sure we are dealing with an absolute path
+ // if (!isAbsolutePath(path)) {
+ // throw new BuildException(path + " is not an absolute path");
+ // }
+ String root = null;
+ int colon = path.indexOf(':');
+ if (colon > 0) { // && (ON_DOS || ON_NETWARE)) {
+
+ int next = colon + 1;
+ root = path.substring(0, next);
+ char[] ca = path.toCharArray();
+ root += sep;
+ // remove the initial separator; the root has it.
+ next = (ca[next] == sep) ? next + 1 : next;
+
+ StringBuffer sbPath = new StringBuffer();
+ // Eliminate consecutive slashes after the drive spec:
+ for (int i = next; i < ca.length; i++) {
+ if (ca[i] != sep || ca[i - 1] != sep) {
+ sbPath.append(ca[i]);
+ }
+ }
+ path = sbPath.toString();
+ } else if (path.length() > 1 && path.charAt(1) == sep) {
+ // UNC drive
+ int nextsep = path.indexOf(sep, 2);
+ nextsep = path.indexOf(sep, nextsep + 1);
+ root = (nextsep > 2) ? path.substring(0, nextsep + 1) : path;
+ path = path.substring(root.length());
+ } else {
+ root = File.separator;
+ path = path.substring(1);
+ }
+ return new String[] {root, path};
+ }
+
+ /**
+ * Get the length of the file, or the sum of the children lengths if it is a directory
+ *
+ * @param file
+ * @return
+ */
+ public static long getFileLength(File file) {
+ long l = 0;
+ if (file.isDirectory()) {
+ File[] files = file.listFiles();
+ if (files != null) {
+ for (int i = 0; i < files.length; i++) {
+ l += getFileLength(files[i]);
+ }
+ }
+ } else {
+ l = file.length();
+ }
+ return l;
+ }
+
+ public static InputStream unwrapPack200(InputStream packed) throws IOException {
+ BufferedInputStream buffered = new BufferedInputStream(packed);
+ buffered.mark(4);
+ byte[] magic = new byte[4];
+ buffered.read(magic, 0, 4);
+ buffered.reset();
+
+ InputStream in = buffered;
+
+ if (magic[0] == (byte) 0x1F && magic[1] == (byte) 0x8B && magic[2] == (byte) 0x08) {
+ // this is a gziped pack200
+ in = new GZIPInputStream(in);
+ }
+
+ Unpacker unpacker = Pack200.newUnpacker();
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ JarOutputStream jar = new JarOutputStream(baos);
+ unpacker.unpack(new UncloseInputStream(in), jar);
+ jar.close();
+ return new ByteArrayInputStream(baos.toByteArray());
+ }
+
+ /**
+ * Wrap an input stream and do not close the stream on call to close(). Used to avoid closing a
+ * {@link ZipInputStream} used with {@link Unpacker#unpack(File, JarOutputStream)}
+ */
+ private static final class UncloseInputStream extends InputStream {
+
+ private InputStream wrapped;
+
+ public UncloseInputStream(InputStream wrapped) {
+ this.wrapped = wrapped;
+ }
+
+ public void close() throws IOException {
+ // do not close
+ }
+
+ public int read() throws IOException {
+ return wrapped.read();
+ }
+
+ public int hashCode() {
+ return wrapped.hashCode();
+ }
+
+ public int read(byte[] b) throws IOException {
+ return wrapped.read(b);
+ }
+
+ public boolean equals(Object obj) {
+ return wrapped.equals(obj);
+ }
+
+ public int read(byte[] b, int off, int len) throws IOException {
+ return wrapped.read(b, off, len);
+ }
+
+ public long skip(long n) throws IOException {
+ return wrapped.skip(n);
+ }
+
+ public String toString() {
+ return wrapped.toString();
+ }
+
+ public int available() throws IOException {
+ return wrapped.available();
+ }
+
+ public void mark(int readlimit) {
+ wrapped.mark(readlimit);
+ }
+
+ public void reset() throws IOException {
+ wrapped.reset();
+ }
+
+ public boolean markSupported() {
+ return wrapped.markSupported();
+ }
+
+ }
+}
diff --git a/src/java/org/apache/ivy/util/HexEncoder.java b/src/java/org/apache/ivy/util/HexEncoder.java
new file mode 100644
index 0000000..436f1b1
--- /dev/null
+++ b/src/java/org/apache/ivy/util/HexEncoder.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.util;
+
+/**
+ * Simple encoder of byte arrays into String array using only the hexadecimal alphabet
+ */
+public class HexEncoder {
+
+ private static final char[] ALPHABET = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a',
+ 'b', 'c', 'd', 'e', 'f'};
+
+ public static String encode(byte[] packet) {
+ StringBuffer chars = new StringBuffer(16);
+ for (int i = 0; i < packet.length; i++) {
+ int highBits = (packet[i] & 0xF0) >> 4;
+ int lowBits = packet[i] & 0x0F;
+ chars.append(ALPHABET[highBits]);
+ chars.append(ALPHABET[lowBits]);
+ }
+ return chars.toString();
+ }
+
+}
diff --git a/src/java/org/apache/ivy/util/HostUtil.java b/src/java/org/apache/ivy/util/HostUtil.java
new file mode 100644
index 0000000..ae4a41b
--- /dev/null
+++ b/src/java/org/apache/ivy/util/HostUtil.java
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.util;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+/**
+ * This class contains basic helper methods for the Host.
+ *
+ */
+public final class HostUtil {
+
+ private static String localHostName = null;
+
+ /**
+ * This default constructor is to hide this class from initialization through other classes.
+ */
+ private HostUtil() {
+ }
+
+ /**
+ * This method returns the name of the current Host, if this name cannot be determined,
+ * localhost will be returned.
+ *
+ * @return The name of the current "local" Host.
+ */
+ public static String getLocalHostName() {
+ if (localHostName == null) {
+ try {
+ localHostName = InetAddress.getLocalHost().getHostName();
+ } catch (UnknownHostException e) {
+ localHostName = "localhost";
+ }
+ }
+ return localHostName;
+ }
+}
diff --git a/src/java/org/apache/ivy/util/MemoryUtil.java b/src/java/org/apache/ivy/util/MemoryUtil.java
new file mode 100644
index 0000000..fef4482
--- /dev/null
+++ b/src/java/org/apache/ivy/util/MemoryUtil.java
@@ -0,0 +1,90 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.util;
+
+/**
+ * Memory related utilities.
+ */
+public final class MemoryUtil {
+ private static final int SAMPLING_SIZE = 100;
+
+ private static final int SLEEP_TIME = 100;
+
+ private MemoryUtil() {
+ }
+
+ /**
+ * Returns the approximate size of a default instance of the given class.
+ *
+ * @param clazz
+ * the class to evaluate.
+ * @return the estimated size of instance, in bytes.
+ */
+ public static long sizeOf(Class clazz) {
+ long size = 0;
+ Object[] objects = new Object[SAMPLING_SIZE];
+ try {
+ clazz.newInstance();
+ long startingMemoryUse = getUsedMemory();
+ for (int i = 0; i < objects.length; i++) {
+ objects[i] = clazz.newInstance();
+ }
+ long endingMemoryUse = getUsedMemory();
+ float approxSize = (endingMemoryUse - startingMemoryUse) / (float) objects.length;
+ size = Math.round(approxSize);
+ } catch (Exception e) {
+ Message.warn("Couldn't instantiate " + clazz, e);
+ }
+ return size;
+ }
+
+ /**
+ * Returns the currently used memory, after calling garbage collector and waiting enough to get
+ * maximal chance it is actually called. But since {@link Runtime#gc()} is only advisory,
+ * results returned by this method should be treated as rough approximation only.
+ *
+ * @return the currently used memory, in bytes.
+ */
+ public static long getUsedMemory() {
+ gc();
+ long totalMemory = Runtime.getRuntime().totalMemory();
+ gc();
+ long freeMemory = Runtime.getRuntime().freeMemory();
+ long usedMemory = totalMemory - freeMemory;
+ return usedMemory;
+ }
+
+ private static void gc() {
+ try {
+ System.gc();
+ Thread.sleep(SLEEP_TIME);
+ System.runFinalization();
+ Thread.sleep(SLEEP_TIME);
+ System.gc();
+ Thread.sleep(SLEEP_TIME);
+ System.runFinalization();
+ Thread.sleep(SLEEP_TIME);
+ } catch (Exception e) {
+ Message.debug(e);
+ }
+ }
+
+ public static void main(String[] args) throws ClassNotFoundException {
+ System.out.println(sizeOf(Class.forName(args[0])));
+ }
+}
\ No newline at end of file
diff --git a/src/java/org/apache/ivy/util/Message.java b/src/java/org/apache/ivy/util/Message.java
new file mode 100644
index 0000000..1e6b67f
--- /dev/null
+++ b/src/java/org/apache/ivy/util/Message.java
@@ -0,0 +1,218 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.util;
+
+import java.util.List;
+
+import org.apache.ivy.Ivy;
+import org.apache.ivy.core.IvyContext;
+
+/**
+ * Logging utility class.
+ * <p>
+ * This class provides static methods for easy access to the current logger in {@link IvyContext}.
+ * </p>
+ * <p>
+ * To configure logging, you should use the methods provided by the {@link MessageLoggerEngine}
+ * associated with the {@link Ivy} engine.
+ * </p>
+ */
+public final class Message {
+ // messages level copied from ant project, to avoid dependency on ant
+ /** Message priority of "error". */
+ public static final int MSG_ERR = 0;
+
+ /** Message priority of "warning". */
+ public static final int MSG_WARN = 1;
+
+ /** Message priority of "information". */
+ public static final int MSG_INFO = 2;
+
+ /** Message priority of "verbose". */
+ public static final int MSG_VERBOSE = 3;
+
+ /** Message priority of "debug". */
+ public static final int MSG_DEBUG = 4;
+
+ private static boolean showedInfo = false;
+
+ private static MessageLogger defaultLogger = new DefaultMessageLogger(Message.MSG_INFO);
+
+ /**
+ * Returns the current default logger.
+ *
+ * @return the current default logger; is never <code>null</code>.
+ */
+ public static MessageLogger getDefaultLogger() {
+ return defaultLogger;
+ }
+
+ /**
+ * Change the default logger used when no other logger is currently configured
+ *
+ * @param logger
+ * the new default logger, must not be <code>null</code>
+ */
+ public static void setDefaultLogger(MessageLogger logger) {
+ Checks.checkNotNull(logger, "logger");
+ defaultLogger = logger;
+ }
+
+ private static MessageLogger getLogger() {
+ return IvyContext.getContext().getMessageLogger();
+ }
+
+ public static void showInfo() {
+ if (!showedInfo) {
+ info(":: Apache Ivy " + Ivy.getIvyVersion() + " - " + Ivy.getIvyDate() + " :: "
+ + Ivy.getIvyHomeURL() + " ::");
+ showedInfo = true;
+ }
+ }
+
+ public static void debug(String msg) {
+ getLogger().debug(msg);
+ }
+
+ public static void verbose(String msg) {
+ getLogger().verbose(msg);
+ }
+
+ public static void info(String msg) {
+ getLogger().info(msg);
+ }
+
+ public static void rawinfo(String msg) {
+ getLogger().rawinfo(msg);
+ }
+
+ public static void deprecated(String msg) {
+ getLogger().deprecated(msg);
+ }
+
+ public static void warn(String msg) {
+ getLogger().warn(msg);
+ }
+
+ public static void error(String msg) {
+ getLogger().error(msg);
+ }
+
+ public static void log(int logLevel, String msg) {
+ switch (logLevel) {
+ case MSG_DEBUG:
+ debug(msg);
+ break;
+ case MSG_VERBOSE:
+ verbose(msg);
+ break;
+ case MSG_INFO:
+ info(msg);
+ break;
+ case MSG_WARN:
+ warn(msg);
+ break;
+ case MSG_ERR:
+ error(msg);
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown log level " + logLevel);
+ }
+ }
+
+ public static List<String> getProblems() {
+ return getLogger().getProblems();
+ }
+
+ public static void sumupProblems() {
+ getLogger().sumupProblems();
+ }
+
+ public static void progress() {
+ getLogger().progress();
+ }
+
+ public static void endProgress() {
+ getLogger().endProgress();
+ }
+
+ public static void endProgress(String msg) {
+ getLogger().endProgress(msg);
+ }
+
+ public static boolean isShowProgress() {
+ return getLogger().isShowProgress();
+ }
+
+ public static void setShowProgress(boolean progress) {
+ getLogger().setShowProgress(progress);
+ }
+
+ private Message() {
+ }
+
+ public static void debug(String message, Throwable t) {
+ if (t == null) {
+ debug(message);
+ } else {
+ debug(message + " (" + t.getClass().getName() + ": " + t.getMessage() + ")");
+ debug(t);
+ }
+ }
+
+ public static void verbose(String message, Throwable t) {
+ if (t == null) {
+ verbose(message);
+ } else {
+ verbose(message + " (" + t.getClass().getName() + ": " + t.getMessage() + ")");
+ debug(t);
+ }
+ }
+
+ public static void info(String message, Throwable t) {
+ if (t == null) {
+ info(message);
+ } else {
+ info(message + " (" + t.getClass().getName() + ": " + t.getMessage() + ")");
+ debug(t);
+ }
+ }
+
+ public static void warn(String message, Throwable t) {
+ if (t == null) {
+ warn(message);
+ } else {
+ warn(message + " (" + t.getClass().getName() + ": " + t.getMessage() + ")");
+ debug(t);
+ }
+ }
+
+ public static void error(String message, Throwable t) {
+ if (t == null) {
+ error(message);
+ } else {
+ error(message + " (" + t.getClass().getName() + ": " + t.getMessage() + ")");
+ debug(t);
+ }
+ }
+
+ public static void debug(Throwable t) {
+ debug(StringUtils.getStackTrace(t));
+ }
+
+}
diff --git a/src/java/org/apache/ivy/util/MessageLogger.java b/src/java/org/apache/ivy/util/MessageLogger.java
new file mode 100644
index 0000000..0c09efe
--- /dev/null
+++ b/src/java/org/apache/ivy/util/MessageLogger.java
@@ -0,0 +1,108 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.util;
+
+import java.util.List;
+
+/**
+ * A MessageLogger is used to log messages.
+ * <p>
+ * Where the messages are logged is depending on the implementation.
+ * </p>
+ * <p>
+ * This interface provides both level specific methods ({@link #info(String)}, {@link #warn(String)}
+ * , ...) and generic methods ({@link #log(String, int)}, {@link #rawlog(String, int)}). Note that
+ * calling level specific methods is usually not equivalent to calling the generic method with the
+ * corresponding level. Indeed, for warn and error level, the implementation will actually log the
+ * message at a lower level (usually {@link Message#MSG_VERBOSE}) and log the message at the actual
+ * level only when {@link #sumupProblems()} is called.
+ * </p>
+ *
+ * @see Message
+ */
+public interface MessageLogger {
+ /**
+ * Logs a message at the given level.
+ * <p>
+ * <code>level</code> constants are defined in the {@link Message} class.
+ * </p>
+ *
+ * @param msg
+ * the message to log
+ * @param level
+ * the level at which the message should be logged.
+ * @see Message#MSG_DEBUG
+ * @see Message#MSG_VERBOSE
+ * @see Message#MSG_INFO
+ * @see Message#MSG_WARN
+ * @see Message#MSG_ERROR
+ */
+ public abstract void log(String msg, int level);
+
+ /**
+ * Same as {@link #log(String, int)}, but without adding any contextual information to the
+ * message.
+ *
+ * @param msg
+ * the message to log
+ * @param level
+ * the level at which the message should be logged.
+ */
+ public abstract void rawlog(String msg, int level);
+
+ public abstract void debug(String msg);
+
+ public abstract void verbose(String msg);
+
+ public abstract void deprecated(String msg);
+
+ public abstract void info(String msg);
+
+ public abstract void rawinfo(String msg);
+
+ public abstract void warn(String msg);
+
+ public abstract void error(String msg);
+
+ public abstract List<String> getProblems();
+
+ public abstract List<String> getWarns();
+
+ public abstract List<String> getErrors();
+
+ /**
+ * Clears the list of problems, warns and errors.
+ */
+ public abstract void clearProblems();
+
+ /**
+ * Sumup all problems encountered so far, and clear them.
+ */
+ public abstract void sumupProblems();
+
+ public abstract void progress();
+
+ public abstract void endProgress();
+
+ public abstract void endProgress(String msg);
+
+ public abstract boolean isShowProgress();
+
+ public abstract void setShowProgress(boolean progress);
+
+}
\ No newline at end of file
diff --git a/src/java/org/apache/ivy/util/MessageLoggerEngine.java b/src/java/org/apache/ivy/util/MessageLoggerEngine.java
new file mode 100644
index 0000000..21bc72f
--- /dev/null
+++ b/src/java/org/apache/ivy/util/MessageLoggerEngine.java
@@ -0,0 +1,207 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.util;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Stack;
+
+/**
+ * A {@link MessageLogger} implementation delegating the work to the current top logger on a stack.
+ * <p>
+ * When the logger stack is empty, it delegates the work to a default logger, which by default is
+ * the {@link Message#getDefaultLogger()}.
+ * </p>
+ * <p>
+ * {@link #pushLogger(MessageLogger)} should be called to delegate to a new logger, and
+ * {@link #popLogger()} should be called when the context of this logger is finished.
+ * </p>
+ */
+public class MessageLoggerEngine implements MessageLogger {
+ private final ThreadLocal/* <Stack<MessageLogger>> */loggerStacks = new ThreadLocal();
+
+ private MessageLogger defaultLogger = null;
+
+ private List problems = new ArrayList();
+
+ private List warns = new ArrayList();
+
+ private List errors = new ArrayList();
+
+ private Stack getLoggerStack() {
+ Stack stack = (Stack) loggerStacks.get();
+ if (stack == null) {
+ stack = new Stack();
+ loggerStacks.set(stack);
+ }
+ return stack;
+ }
+
+ public MessageLoggerEngine() {
+ }
+
+ /**
+ * Sets the logger used when the stack is empty.
+ *
+ * @param defaultLogger
+ * the logger to use when the stack is empty.
+ */
+ public void setDefaultLogger(MessageLogger defaultLogger) {
+ this.defaultLogger = defaultLogger;
+ }
+
+ /**
+ * Push a logger on the stack.
+ *
+ * @param logger
+ * the logger to push. Must not be <code>null</code>.
+ */
+ public void pushLogger(MessageLogger logger) {
+ Checks.checkNotNull(logger, "logger");
+ getLoggerStack().push(logger);
+ }
+
+ /**
+ * Pops a logger from the logger stack.
+ * <p>
+ * Does nothing if the logger stack is empty
+ * </p>
+ */
+ public void popLogger() {
+ if (!getLoggerStack().isEmpty()) {
+ getLoggerStack().pop();
+ }
+ }
+
+ /**
+ * Returns the current logger, or the default one if there is no logger in the stack
+ *
+ * @return the current logger, or the default one if there is no logger in the stack
+ */
+ public MessageLogger peekLogger() {
+ if (getLoggerStack().isEmpty()) {
+ return getDefaultLogger();
+ }
+ return (MessageLogger) getLoggerStack().peek();
+ }
+
+ private MessageLogger getDefaultLogger() {
+ // we don't store the logger returned by Message.getDefaultLogger() to always stay in sync
+ // as long as our default logger has not been set explicitly with setDefaultLogger()
+ return defaultLogger == null ? Message.getDefaultLogger() : defaultLogger;
+ }
+
+ // consolidated methods
+ public void warn(String msg) {
+ peekLogger().warn(msg);
+ problems.add("WARN: " + msg);
+ warns.add(msg);
+ }
+
+ public void error(String msg) {
+ peekLogger().error(msg);
+ problems.add("\tERROR: " + msg);
+ errors.add(msg);
+ }
+
+ public List getErrors() {
+ return errors;
+ }
+
+ public List getProblems() {
+ return problems;
+ }
+
+ public List getWarns() {
+ return warns;
+ }
+
+ public void sumupProblems() {
+ MessageLoggerHelper.sumupProblems(this);
+ clearProblems();
+ }
+
+ public void clearProblems() {
+ getDefaultLogger().clearProblems();
+ for (Iterator iter = getLoggerStack().iterator(); iter.hasNext();) {
+ MessageLogger l = (MessageLogger) iter.next();
+ l.clearProblems();
+ }
+ problems.clear();
+ errors.clear();
+ warns.clear();
+ }
+
+ public void setShowProgress(boolean progress) {
+ getDefaultLogger().setShowProgress(progress);
+ // updates all loggers in the stack
+ for (Iterator iter = getLoggerStack().iterator(); iter.hasNext();) {
+ MessageLogger l = (MessageLogger) iter.next();
+ l.setShowProgress(progress);
+ }
+ }
+
+ public boolean isShowProgress() {
+ // testing the default logger is enough, all loggers should be in sync
+ return getDefaultLogger().isShowProgress();
+ }
+
+ // delegation methods
+
+ public void debug(String msg) {
+ peekLogger().debug(msg);
+ }
+
+ public void deprecated(String msg) {
+ peekLogger().deprecated(msg);
+ }
+
+ public void endProgress() {
+ peekLogger().endProgress();
+ }
+
+ public void endProgress(String msg) {
+ peekLogger().endProgress(msg);
+ }
+
+ public void info(String msg) {
+ peekLogger().info(msg);
+ }
+
+ public void rawinfo(String msg) {
+ peekLogger().rawinfo(msg);
+ }
+
+ public void log(String msg, int level) {
+ peekLogger().log(msg, level);
+ }
+
+ public void progress() {
+ peekLogger().progress();
+ }
+
+ public void rawlog(String msg, int level) {
+ peekLogger().rawlog(msg, level);
+ }
+
+ public void verbose(String msg) {
+ peekLogger().verbose(msg);
+ }
+
+}
diff --git a/src/java/org/apache/ivy/util/MessageLoggerHelper.java b/src/java/org/apache/ivy/util/MessageLoggerHelper.java
new file mode 100644
index 0000000..c06f14a
--- /dev/null
+++ b/src/java/org/apache/ivy/util/MessageLoggerHelper.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.util;
+
+import java.util.Iterator;
+import java.util.List;
+
+public final class MessageLoggerHelper {
+ public static void sumupProblems(MessageLogger logger) {
+ List myProblems = logger.getProblems();
+ if (myProblems.size() > 0) {
+ List myWarns = logger.getWarns();
+ List myErrors = logger.getErrors();
+ logger.info(""); // new line on info to isolate error summary
+ if (!myErrors.isEmpty()) {
+ logger.log(":: problems summary ::", Message.MSG_ERR);
+ } else {
+ logger.log(":: problems summary ::", Message.MSG_WARN);
+ }
+ if (myWarns.size() > 0) {
+ logger.log(":::: WARNINGS", Message.MSG_WARN);
+ for (Iterator iter = myWarns.iterator(); iter.hasNext();) {
+ String msg = (String) iter.next();
+ logger.log("\t" + msg + "\n", Message.MSG_WARN);
+ }
+ }
+ if (myErrors.size() > 0) {
+ logger.log(":::: ERRORS", Message.MSG_ERR);
+ for (Iterator iter = myErrors.iterator(); iter.hasNext();) {
+ String msg = (String) iter.next();
+ logger.log("\t" + msg + "\n", Message.MSG_ERR);
+ }
+ }
+ logger.info("\n:: USE VERBOSE OR DEBUG MESSAGE LEVEL FOR MORE DETAILS");
+ }
+ }
+
+ private MessageLoggerHelper() {
+ }
+}
diff --git a/src/java/org/apache/ivy/util/PropertiesFile.java b/src/java/org/apache/ivy/util/PropertiesFile.java
new file mode 100644
index 0000000..f4d2048
--- /dev/null
+++ b/src/java/org/apache/ivy/util/PropertiesFile.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.util;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Properties;
+
+/**
+ * A simple Properties extension easing the loading and saving of data
+ */
+public class PropertiesFile extends Properties {
+ private File file;
+
+ private String header;
+
+ public PropertiesFile(File file, String header) {
+ this.file = file;
+ this.header = header;
+ if (file.exists()) {
+ FileInputStream fis = null;
+ try {
+ fis = new FileInputStream(file);
+ load(fis);
+ } catch (Exception ex) {
+ Message.warn("exception occurred while reading properties file " + file, ex);
+ }
+ try {
+ if (fis != null) {
+ fis.close();
+ }
+ } catch (IOException e) {
+ // ignored
+ }
+ }
+ }
+
+ public void save() {
+ FileOutputStream fos = null;
+ try {
+ if (file.getParentFile() != null && !file.getParentFile().exists()) {
+ file.getParentFile().mkdirs();
+ }
+ fos = new FileOutputStream(file);
+ store(fos, header);
+ } catch (Exception ex) {
+ Message.warn("exception occurred while writing properties file " + file, ex);
+ }
+ try {
+ if (fos != null) {
+ fos.close();
+ }
+ } catch (IOException e) {
+ // ignored
+ }
+ }
+
+}
diff --git a/src/java/org/apache/ivy/util/StringUtils.java b/src/java/org/apache/ivy/util/StringUtils.java
new file mode 100644
index 0000000..c60efb7
--- /dev/null
+++ b/src/java/org/apache/ivy/util/StringUtils.java
@@ -0,0 +1,192 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.util;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Locale;
+
+/**
+ * Convenient class used only for uncapitalization Usually use commons lang but here we do not want
+ * to have such a dependency for only one feature
+ */
+public final class StringUtils {
+
+ private StringUtils() {
+ // Utility class
+ }
+
+ public static String uncapitalize(String string) {
+ if (string == null || string.length() == 0) {
+ return string;
+ }
+ if (string.length() == 1) {
+ return string.toLowerCase(Locale.US);
+ }
+ return string.substring(0, 1).toLowerCase(Locale.US) + string.substring(1);
+ }
+
+ /**
+ * Returns the error message associated with the given Throwable. The error message returned
+ * will try to be as precise as possible, handling cases where e.getMessage() is not meaningful,
+ * like {@link NullPointerException} for instance.
+ *
+ * @param t
+ * the throwable to get the error message from
+ * @return the error message of the given exception
+ */
+ public static String getErrorMessage(Throwable t) {
+ if (t == null) {
+ return "";
+ }
+ if (t instanceof InvocationTargetException) {
+ InvocationTargetException ex = (InvocationTargetException) t;
+ t = ex.getTargetException();
+ }
+ String errMsg = t instanceof RuntimeException ? t.getMessage() : t.toString();
+ if (errMsg == null || errMsg.length() == 0 || "null".equals(errMsg)) {
+ errMsg = t.getClass().getName() + " at " + t.getStackTrace()[0].toString();
+ }
+ return errMsg;
+ }
+
+ /**
+ * Returns the exception stack trace as a String.
+ *
+ * @param e
+ * the exception to get the stack trace from.
+ * @return the exception stack trace
+ */
+ public static String getStackTrace(Throwable e) {
+ if (e == null) {
+ return "";
+ }
+ StringWriter sw = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(sw, true);
+ e.printStackTrace(printWriter);
+ return sw.getBuffer().toString();
+ }
+
+ /**
+ * Joins the given object array in one string, each separated by the given separator.
+ *
+ * Example:
+ *
+ * <pre>
+ * join(new String[] {"one", "two", "three"}, ", ") -> "one, two, three"
+ * </pre>
+ *
+ * @param objs
+ * The array of objects (<code>toString()</code> is used).
+ * @param sep
+ * The separator to use.
+ * @return The concatinated string.
+ */
+ public static String join(Object[] objs, String sep) {
+ StringBuffer buf = new StringBuffer();
+ for (int i = 0; i < objs.length; i++) {
+ buf.append(objs[i]).append(sep);
+ }
+ if (objs.length > 0) {
+ buf.setLength(buf.length() - sep.length()); // delete sep
+ }
+ return buf.toString();
+ }
+
+ // basic string codec (same algo as CVS passfile, inspired by ant CVSPass class
+ /** Array contain char conversion data */
+ private static final char[] SHIFTS = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 114, 120, 53, 79, 96, 109,
+ 72, 108, 70, 64, 76, 67, 116, 74, 68, 87, 111, 52, 75, 119, 49, 34, 82, 81, 95, 65,
+ 112, 86, 118, 110, 122, 105, 41, 57, 83, 43, 46, 102, 40, 89, 38, 103, 45, 50, 42, 123,
+ 91, 35, 125, 55, 54, 66, 124, 126, 59, 47, 92, 71, 115, 78, 88, 107, 106, 56, 36, 121,
+ 117, 104, 101, 100, 69, 73, 99, 63, 94, 93, 39, 37, 61, 48, 58, 113, 32, 90, 44, 98,
+ 60, 51, 33, 97, 62, 77, 84, 80, 85, 223, 225, 216, 187, 166, 229, 189, 222, 188, 141,
+ 249, 148, 200, 184, 136, 248, 190, 199, 170, 181, 204, 138, 232, 218, 183, 255, 234,
+ 220, 247, 213, 203, 226, 193, 174, 172, 228, 252, 217, 201, 131, 230, 197, 211, 145,
+ 238, 161, 179, 160, 212, 207, 221, 254, 173, 202, 146, 224, 151, 140, 196, 205, 130,
+ 135, 133, 143, 246, 192, 159, 244, 239, 185, 168, 215, 144, 139, 165, 180, 157, 147,
+ 186, 214, 176, 227, 231, 219, 169, 175, 156, 206, 198, 129, 164, 150, 210, 154, 177,
+ 134, 127, 182, 128, 158, 208, 162, 132, 167, 209, 149, 241, 153, 251, 237, 236, 171,
+ 195, 243, 233, 253, 240, 194, 250, 191, 155, 142, 137, 245, 235, 163, 242, 178, 152};
+
+ /**
+ * Encrypt the given string in a way which anybody having access to this method algorithm can
+ * easily decrypt. This is useful only to avoid clear string storage in a file for example, but
+ * shouldn't be considered as a real mean of security. This only works with simple characters
+ * (char < 256).
+ *
+ * @param str
+ * the string to encrypt
+ * @return the encrypted version of the string
+ */
+ public static final String encrypt(String str) {
+ if (str == null) {
+ return null;
+ }
+ StringBuffer buf = new StringBuffer();
+ for (int i = 0; i < str.length(); i++) {
+ char c = str.charAt(i);
+ if (c >= SHIFTS.length) {
+ throw new IllegalArgumentException(
+ "encrypt method can only be used with simple characters. '" + c
+ + "' not allowed");
+ }
+ buf.append(SHIFTS[c]);
+ }
+ return buf.toString();
+ }
+
+ /**
+ * Decrypts a string encrypted with encrypt.
+ *
+ * @param str
+ * the encrypted string to decrypt
+ * @return The decrypted string.
+ */
+ public static final String decrypt(String str) {
+ if (str == null) {
+ return null;
+ }
+ StringBuffer buf = new StringBuffer();
+ for (int i = 0; i < str.length(); i++) {
+ buf.append(decrypt(str.charAt(i)));
+ }
+ return buf.toString();
+ }
+
+ private static char decrypt(char c) {
+ for (char i = 0; i < SHIFTS.length; i++) {
+ if (SHIFTS[i] == c) {
+ return i;
+ }
+ }
+ throw new IllegalArgumentException("Impossible to decrypt '" + c
+ + "'. Unhandled character.");
+ }
+
+ public static String repeat(String str, int count) {
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < count; i++) {
+ sb.append(str);
+ }
+ return sb.toString();
+ }
+
+}
diff --git a/src/java/org/apache/ivy/util/XMLHelper.java b/src/java/org/apache/ivy/util/XMLHelper.java
new file mode 100644
index 0000000..8f64ad8
--- /dev/null
+++ b/src/java/org/apache/ivy/util/XMLHelper.java
@@ -0,0 +1,243 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.apache.ivy.util.url.URLHandlerRegistry;
+import org.w3c.dom.Document;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXNotRecognizedException;
+import org.xml.sax.ext.LexicalHandler;
+import org.xml.sax.helpers.DefaultHandler;
+
+public abstract class XMLHelper {
+
+ static final String JAXP_SCHEMA_LANGUAGE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
+
+ static final String JAXP_SCHEMA_SOURCE = "http://java.sun.com/xml/jaxp/properties/schemaSource";
+
+ static final String XERCES_LOAD_EXTERNAL_DTD = "http://apache.org/xml/features/nonvalidating/load-external-dtd";
+
+ static final String XML_NAMESPACE_PREFIXES = "http://xml.org/sax/features/namespace-prefixes";
+
+ static final String W3C_XML_SCHEMA = "http://www.w3.org/2001/XMLSchema";
+
+ private static boolean canUseSchemaValidation = true;
+
+ private static Boolean canDisableExternalDtds = null;
+
+ private static SAXParser newSAXParser(URL schema, InputStream schemaStream,
+ boolean loadExternalDtds) throws ParserConfigurationException, SAXException {
+ SAXParserFactory parserFactory = SAXParserFactory.newInstance();
+ parserFactory.setNamespaceAware(true);
+ parserFactory.setValidating(canUseSchemaValidation && (schema != null));
+ if (!loadExternalDtds && canDisableExternalDtds(parserFactory)) {
+ parserFactory.setFeature(XERCES_LOAD_EXTERNAL_DTD, false);
+ }
+ SAXParser parser = parserFactory.newSAXParser();
+
+ if (canUseSchemaValidation && (schema != null)) {
+ try {
+ parser.setProperty(JAXP_SCHEMA_LANGUAGE, W3C_XML_SCHEMA);
+ parser.setProperty(JAXP_SCHEMA_SOURCE, schemaStream);
+ } catch (SAXNotRecognizedException ex) {
+ Message.warn("problem while setting JAXP validating property on SAXParser... "
+ + "XML validation will not be done", ex);
+ canUseSchemaValidation = false;
+ parserFactory.setValidating(false);
+ parser = parserFactory.newSAXParser();
+ }
+ }
+
+ parser.getXMLReader().setFeature(XML_NAMESPACE_PREFIXES, true);
+ return parser;
+ }
+
+ private static boolean canDisableExternalDtds(SAXParserFactory parserFactory) {
+ if (canDisableExternalDtds == null) {
+ try {
+ parserFactory.getFeature(XERCES_LOAD_EXTERNAL_DTD);
+ canDisableExternalDtds = Boolean.TRUE;
+ } catch (Exception ex) {
+ canDisableExternalDtds = Boolean.FALSE;
+ }
+ }
+ return canDisableExternalDtds.booleanValue();
+ }
+
+ /**
+ * Convert an URL to a valid systemId according to RFC 2396.
+ */
+ public static String toSystemId(URL url) {
+ try {
+ return new URI(url.toExternalForm()).toASCIIString();
+ } catch (URISyntaxException e) {
+ return url.toExternalForm();
+ }
+ }
+
+ // IMPORTANT: validation errors are only notified to the given handler, and
+ // do not cause exception
+ // implement warning error and fatalError methods in handler to be informed
+ // of validation errors
+ public static void parse(URL xmlURL, URL schema, DefaultHandler handler) throws SAXException,
+ IOException, ParserConfigurationException {
+ parse(xmlURL, schema, handler, null);
+ }
+
+ public static void parse(URL xmlURL, URL schema, DefaultHandler handler, LexicalHandler lHandler)
+ throws SAXException, IOException, ParserConfigurationException {
+ InputStream xmlStream = URLHandlerRegistry.getDefault().openStream(xmlURL);
+ try {
+ InputSource inSrc = new InputSource(xmlStream);
+ inSrc.setSystemId(toSystemId(xmlURL));
+ parse(inSrc, schema, handler, lHandler);
+ } finally {
+ try {
+ xmlStream.close();
+ } catch (IOException e) {
+ // ignored
+ }
+ }
+ }
+
+ public static void parse(InputStream xmlStream, URL schema, DefaultHandler handler,
+ LexicalHandler lHandler) throws SAXException, IOException, ParserConfigurationException {
+ parse(new InputSource(xmlStream), schema, handler, lHandler);
+ }
+
+ public static void parse(InputSource xmlStream, URL schema, DefaultHandler handler,
+ LexicalHandler lHandler) throws SAXException, IOException, ParserConfigurationException {
+ parse(xmlStream, schema, handler, lHandler, true);
+ }
+
+ public static void parse(InputSource xmlStream, URL schema, DefaultHandler handler,
+ LexicalHandler lHandler, boolean loadExternalDtds) throws SAXException, IOException,
+ ParserConfigurationException {
+ InputStream schemaStream = null;
+ try {
+ if (schema != null) {
+ schemaStream = URLHandlerRegistry.getDefault().openStream(schema);
+ }
+ SAXParser parser = XMLHelper.newSAXParser(schema, schemaStream, loadExternalDtds);
+
+ if (lHandler != null) {
+ try {
+ parser.setProperty("http://xml.org/sax/properties/lexical-handler", lHandler);
+ } catch (SAXException ex) {
+ Message.warn("problem while setting the lexical handler property on SAXParser",
+ ex);
+ // continue without the lexical handler
+ }
+ }
+
+ parser.parse(xmlStream, handler);
+ } finally {
+ if (schemaStream != null) {
+ try {
+ schemaStream.close();
+ } catch (IOException ex) {
+ // ignored
+ }
+ }
+ }
+ }
+
+ public static boolean canUseSchemaValidation() {
+ return canUseSchemaValidation;
+ }
+
+ /**
+ * Escapes invalid XML characters in the given character data using XML entities. For the
+ * moment, only the following characters are being escaped: (<), (&), (') and (").
+ *
+ * Remark: we don't escape the (>) character to keep the readability of the configuration
+ * mapping! The XML spec only requires that the (&) and (<) characters are being escaped inside
+ * character data.
+ *
+ * @param text
+ * the character data to escape
+ * @return the escaped character data
+ */
+ public static String escape(String text) {
+ if (text == null) {
+ return null;
+ }
+
+ StringBuffer result = new StringBuffer(text.length());
+
+ char[] chars = text.toCharArray();
+ for (int i = 0; i < chars.length; i++) {
+ switch (chars[i]) {
+ case '&':
+ result.append("&");
+ break;
+ case '<':
+ result.append("<");
+ break;
+ case '\'':
+ result.append("'");
+ break;
+ case '\"':
+ result.append(""");
+ break;
+ default:
+ result.append(chars[i]);
+ }
+ }
+
+ return result.toString();
+ }
+
+ public static Document parseToDom(InputSource source, EntityResolver entityResolver)
+ throws IOException, SAXException {
+ DocumentBuilder docBuilder = getDocBuilder(entityResolver);
+ return docBuilder.parse(source);
+ }
+
+ public static DocumentBuilder getDocBuilder(EntityResolver entityResolver) {
+ try {
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ factory.setValidating(false);
+ DocumentBuilder docBuilder = factory.newDocumentBuilder();
+ if (entityResolver != null) {
+ docBuilder.setEntityResolver(entityResolver);
+ }
+ return docBuilder;
+ } catch (ParserConfigurationException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private XMLHelper() {
+ }
+
+}
diff --git a/src/java/org/apache/ivy/util/cli/CommandLine.java b/src/java/org/apache/ivy/util/cli/CommandLine.java
new file mode 100644
index 0000000..64e42b8
--- /dev/null
+++ b/src/java/org/apache/ivy/util/cli/CommandLine.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.util.cli;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class CommandLine {
+ private Map/* <String, String[]> */optionValues = new HashMap();
+
+ private String[] leftOverArgs;
+
+ void addOptionValues(String option, String[] values) {
+ optionValues.put(option, values);
+ }
+
+ void setLeftOverArgs(String[] args) {
+ leftOverArgs = args;
+ }
+
+ public boolean hasOption(String option) {
+ return optionValues.containsKey(option);
+ }
+
+ public String getOptionValue(String option) {
+ String[] values = getOptionValues(option);
+ return values == null || values.length == 0 ? null : values[0];
+ }
+
+ public String getOptionValue(String option, String defaultValue) {
+ String value = getOptionValue(option);
+ return value == null ? defaultValue : value;
+ }
+
+ public String[] getOptionValues(String option) {
+ return (String[]) optionValues.get(option);
+ }
+
+ public String[] getLeftOverArgs() {
+ return leftOverArgs;
+ }
+
+}
diff --git a/src/java/org/apache/ivy/util/cli/CommandLineParser.java b/src/java/org/apache/ivy/util/cli/CommandLineParser.java
new file mode 100644
index 0000000..7b2212d
--- /dev/null
+++ b/src/java/org/apache/ivy/util/cli/CommandLineParser.java
@@ -0,0 +1,141 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.util.cli;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.apache.ivy.util.StringUtils;
+
+public class CommandLineParser {
+ private static final int MIN_DESC_WIDTH = 40;
+
+ private static final int MAX_SPEC_WIDTH = 30;
+
+ private Map/* <String, Option> */options = new LinkedHashMap();
+
+ private Map/* <String, List<Option>> */categories = new LinkedHashMap();
+
+ public CommandLineParser() {
+ }
+
+ public CommandLineParser addCategory(String category) {
+ categories.put(category, new ArrayList());
+ return this;
+ }
+
+ public CommandLineParser addOption(Option option) {
+ options.put(option.getName(), option);
+ if (!categories.isEmpty()) {
+ ((List) categories.values().toArray()[categories.values().size() - 1]).add(option);
+ }
+ return this;
+ }
+
+ public CommandLine parse(String[] args) throws ParseException {
+ CommandLine line = new CommandLine();
+
+ int index = args.length;
+ ListIterator iterator = Arrays.asList(args).listIterator();
+ while (iterator.hasNext()) {
+ String arg = (String) iterator.next();
+ if ("--".equals(arg)) {
+ // skip this argument and stop looping
+ index = iterator.nextIndex();
+ break;
+ }
+
+ if (!arg.startsWith("-")) {
+ index = iterator.previousIndex();
+ break;
+ }
+
+ Option option = (Option) options.get(arg.substring(1));
+ if (option == null) {
+ throw new ParseException("Unrecognized option: " + arg);
+ }
+ line.addOptionValues(arg.substring(1), option.parse(iterator));
+ }
+
+ // left over args
+ String[] leftOverArgs = new String[args.length - index];
+ System.arraycopy(args, index, leftOverArgs, 0, leftOverArgs.length);
+ line.setLeftOverArgs(leftOverArgs);
+
+ return line;
+ }
+
+ public void printHelp(PrintWriter pw, int width, String command, boolean showDeprecated) {
+ pw.println("usage: " + command);
+ // compute the largest option spec
+ int specWidth = 0;
+ for (Iterator iterator = options.values().iterator(); iterator.hasNext();) {
+ Option option = (Option) iterator.next();
+ if (option.isDeprecated() && !showDeprecated) {
+ continue;
+ }
+ specWidth = Math.min(MAX_SPEC_WIDTH, Math.max(specWidth, option.getSpec().length()));
+ }
+
+ // print options help
+ for (Iterator iterator = categories.entrySet().iterator(); iterator.hasNext();) {
+ Entry entry = (Entry) iterator.next();
+ String category = (String) entry.getKey();
+ pw.println("==== " + category);
+ List/* <Option> */options = (List) entry.getValue();
+ for (Iterator it = options.iterator(); it.hasNext();) {
+ Option option = (Option) it.next();
+ if (option.isDeprecated() && !showDeprecated) {
+ continue;
+ }
+ // print option spec: option name + argument names
+ String spec = option.getSpec();
+ pw.print(" " + spec);
+ int specLength = spec.length() + 1;
+ pw.print(StringUtils.repeat(" ", specWidth - specLength));
+
+ // print description
+ StringBuffer desc = new StringBuffer((option.isDeprecated() ? "DEPRECATED: " : "")
+ + option.getDescription());
+ int count = Math.min(desc.length(), width - Math.max(specLength, specWidth));
+ // see if we have enough space to start on the same line as the spec
+ if (count > MIN_DESC_WIDTH || desc.length() + specLength < width) {
+ pw.print(desc.substring(0, count));
+ desc.delete(0, count);
+ }
+ pw.println();
+
+ // print remaining description
+ while (desc.length() > 0) {
+ pw.print(StringUtils.repeat(" ", specWidth));
+ count = Math.min(desc.length(), width - specWidth);
+ pw.println(desc.substring(0, count));
+ desc.delete(0, count);
+ }
+ }
+ pw.println();
+ }
+ }
+}
diff --git a/src/java/org/apache/ivy/util/cli/Option.java b/src/java/org/apache/ivy/util/cli/Option.java
new file mode 100644
index 0000000..da91565
--- /dev/null
+++ b/src/java/org/apache/ivy/util/cli/Option.java
@@ -0,0 +1,124 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.util.cli;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.ListIterator;
+
+public class Option {
+ private String name;
+
+ private String[] args;
+
+ private String description;
+
+ private boolean required;
+
+ private boolean countArgs;
+
+ private boolean deprecated;
+
+ Option(String name, String[] args, String description, boolean required, boolean countArgs,
+ boolean deprecated) {
+ this.name = name;
+ this.args = args;
+ this.description = description;
+ this.required = required;
+ this.countArgs = countArgs;
+ this.deprecated = deprecated;
+ if (required) {
+ throw new UnsupportedOperationException("required option not supported yet");
+ }
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public String[] getArgs() {
+ return args;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+
+ public boolean isRequired() {
+ return required;
+ }
+
+ public boolean isCountArgs() {
+ return countArgs;
+ }
+
+ public boolean isDeprecated() {
+ return deprecated;
+ }
+
+ String[] parse(ListIterator iterator) throws ParseException {
+ if (isCountArgs()) {
+ String[] values = new String[args.length];
+ for (int i = 0; i < values.length; i++) {
+ if (!iterator.hasNext()) {
+ missingArgument(i);
+ }
+ values[i] = (String) iterator.next();
+ if (values[i].startsWith("-")) {
+ missingArgument(i);
+ }
+ }
+ return values;
+ } else {
+ List values = new ArrayList();
+ while (iterator.hasNext()) {
+ String value = (String) iterator.next();
+ if (value.startsWith("-")) {
+ iterator.previous();
+ break;
+ }
+ values.add(value);
+ }
+ return (String[]) values.toArray(new String[values.size()]);
+ }
+ }
+
+ private void missingArgument(int i) throws ParseException {
+ if (i == 0) {
+ throw new ParseException("no argument for: " + name);
+ } else {
+ throw new ParseException("missing argument for: " + name + ". Expected: "
+ + getArgsSpec());
+ }
+ }
+
+ public String getSpec() {
+ return "-" + name + " " + getArgsSpec();
+ }
+
+ private String getArgsSpec() {
+ if (args.length == 0) {
+ return "";
+ }
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < args.length; i++) {
+ sb.append("<").append(args[i]).append("> ");
+ }
+ return sb.toString();
+ }
+}
diff --git a/src/java/org/apache/ivy/util/cli/OptionBuilder.java b/src/java/org/apache/ivy/util/cli/OptionBuilder.java
new file mode 100644
index 0000000..59f3250
--- /dev/null
+++ b/src/java/org/apache/ivy/util/cli/OptionBuilder.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.util.cli;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class OptionBuilder {
+ private String name;
+
+ private List/* <String> */args = new ArrayList();
+
+ private String description = "";
+
+ private boolean required = false;
+
+ private boolean countArgs = true;
+
+ private boolean deprecated = false;
+
+ public OptionBuilder(String name) {
+ this.name = name;
+ }
+
+ public OptionBuilder required(boolean required) {
+ this.required = required;
+ return this;
+ }
+
+ public OptionBuilder description(String description) {
+ this.description = description;
+ return this;
+ }
+
+ public OptionBuilder arg(String argName) {
+ this.args.add(argName);
+ return this;
+ }
+
+ public OptionBuilder countArgs(boolean countArgs) {
+ this.countArgs = countArgs;
+ return this;
+ }
+
+ public OptionBuilder deprecated() {
+ this.deprecated = true;
+ return this;
+ }
+
+ public Option create() {
+ return new Option(name, (String[]) args.toArray(new String[args.size()]), description,
+ required, countArgs, deprecated);
+ }
+}
diff --git a/src/java/org/apache/ivy/util/cli/ParseException.java b/src/java/org/apache/ivy/util/cli/ParseException.java
new file mode 100644
index 0000000..93fe6dc
--- /dev/null
+++ b/src/java/org/apache/ivy/util/cli/ParseException.java
@@ -0,0 +1,24 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.util.cli;
+
+public class ParseException extends Exception {
+ public ParseException(String reason) {
+ super(reason);
+ }
+}
diff --git a/src/java/org/apache/ivy/util/extendable/DefaultExtendableItem.java b/src/java/org/apache/ivy/util/extendable/DefaultExtendableItem.java
new file mode 100644
index 0000000..5f47699
--- /dev/null
+++ b/src/java/org/apache/ivy/util/extendable/DefaultExtendableItem.java
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.util.extendable;
+
+import java.util.Map;
+
+/**
+ * An item which is meant to be extended, i.e. defined using extra attributes
+ */
+public class DefaultExtendableItem extends UnmodifiableExtendableItem {
+ public DefaultExtendableItem() {
+ this(null, null);
+ }
+
+ public DefaultExtendableItem(Map stdAttributes, Map extraAttributes) {
+ super(stdAttributes, extraAttributes);
+ }
+
+ public void setExtraAttribute(String attName, String attValue) {
+ super.setExtraAttribute(attName, attValue);
+ }
+
+}
diff --git a/src/java/org/apache/ivy/util/extendable/ExtendableItem.java b/src/java/org/apache/ivy/util/extendable/ExtendableItem.java
new file mode 100644
index 0000000..91fb129
--- /dev/null
+++ b/src/java/org/apache/ivy/util/extendable/ExtendableItem.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.util.extendable;
+
+import java.util.Map;
+
+public interface ExtendableItem {
+ /**
+ * Gets the value of an attribute Can be used to access the value of a standard attribute (like
+ * organisation, revision) or of an extra attribute.
+ *
+ * @param attName
+ * the name of the attribute to get
+ * @return the value of the attribute, null if the attribute doesn't exist
+ */
+ String getAttribute(String attName);
+
+ /**
+ * Gets the value of an extra attribute Can be used only to access the value of an extra
+ * attribute, not a standard one (like organisation, revision)
+ *
+ * @param attName
+ * the name of the extra attribute to get. This name can be either qualified or
+ * unqualified.
+ * @return the value of the attribute, null if the attribute doesn't exist
+ */
+ String getExtraAttribute(String attName);
+
+ /**
+ * Returns a Map of all attributes of this extendable item, including standard and extra ones.
+ * The Map keys are attribute names as Strings, and values are corresponding attribute values
+ * (as String too). Extra attributes are included in unqualified form only.
+ *
+ * @return A Map instance containing all the attributes and their values.
+ */
+ Map getAttributes();
+
+ /**
+ * Returns a Map of all extra attributes of this extendable item. The Map keys are
+ * <b>unqualified</b> attribute names as Strings, and values are corresponding attribute values
+ * (as String too)
+ *
+ * @return A Map instance containing all the extra attributes and their values.
+ * @see #getQualifiedExtraAttributes()
+ */
+ Map getExtraAttributes();
+
+ /**
+ * Returns a Map of all extra attributes of this extendable item.
+ * <p>
+ * The Map keys are <b>qualified</b> attribute names as Strings, and values are corresponding
+ * attribute values (as String too).
+ * </p>
+ * <p>
+ * An attribute name is qualified with a namespace exactly the same way xml attributes are
+ * qualified. Thus qualified attribute names are of the form <code>prefix:name</code>
+ * </p>
+ *
+ * @return A Map instance containing all the extra attributes and their values.
+ * @see #getExtraAttributes()
+ */
+ Map getQualifiedExtraAttributes();
+}
diff --git a/src/java/org/apache/ivy/util/extendable/ExtendableItemHelper.java b/src/java/org/apache/ivy/util/extendable/ExtendableItemHelper.java
new file mode 100644
index 0000000..cfb4340
--- /dev/null
+++ b/src/java/org/apache/ivy/util/extendable/ExtendableItemHelper.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.util.extendable;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.ivy.plugins.parser.ParserSettings;
+import org.xml.sax.Attributes;
+
+public final class ExtendableItemHelper {
+ private ExtendableItemHelper() {
+ }
+
+ public static Map getExtraAttributes(Attributes attributes, String prefix) {
+ Map ret = new HashMap();
+ for (int i = 0; i < attributes.getLength(); i++) {
+ if (attributes.getQName(i).startsWith(prefix)) {
+ ret.put(attributes.getQName(i).substring(prefix.length()), attributes.getValue(i));
+ }
+ }
+ return ret;
+ }
+
+ /**
+ * Extract from the XML attribute the extra Ivy ones
+ *
+ * @param settings
+ * @param attributes
+ * @param ignoredAttNames
+ * the XML attributes names which are not extra but Ivy core ones
+ * @return
+ */
+ public static Map getExtraAttributes(ParserSettings settings, Attributes attributes,
+ String[] ignoredAttNames) {
+ Map ret = new HashMap();
+ Collection ignored = Arrays.asList(ignoredAttNames);
+ for (int i = 0; i < attributes.getLength(); i++) {
+ if (!ignored.contains(attributes.getQName(i))) {
+ ret.put(attributes.getQName(i), settings.substitute(attributes.getValue(i)));
+ }
+ }
+ return ret;
+ }
+
+ public static void fillExtraAttributes(ParserSettings settings, DefaultExtendableItem item,
+ Attributes attributes, String[] ignoredAttNames) {
+ Map att = getExtraAttributes(settings, attributes, ignoredAttNames);
+ for (Iterator iter = att.keySet().iterator(); iter.hasNext();) {
+ String attName = (String) iter.next();
+ String attValue = (String) att.get(attName);
+ item.setExtraAttribute(attName, attValue);
+ }
+ }
+
+}
diff --git a/src/java/org/apache/ivy/util/extendable/UnmodifiableExtendableItem.java b/src/java/org/apache/ivy/util/extendable/UnmodifiableExtendableItem.java
new file mode 100644
index 0000000..6342569
--- /dev/null
+++ b/src/java/org/apache/ivy/util/extendable/UnmodifiableExtendableItem.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.util.extendable;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+
+public class UnmodifiableExtendableItem implements ExtendableItem {
+ private final Map attributes = new HashMap();
+
+ private final Map unmodifiableAttributesView = Collections.unmodifiableMap(attributes);
+
+ private final Map extraAttributes = new HashMap();
+
+ private final Map unmodifiableExtraAttributesView = Collections
+ .unmodifiableMap(extraAttributes);
+
+ /*
+ * this is the only place where extra attributes are stored in qualified form. In all other maps
+ * they are stored unqualified.
+ */
+ private final Map qualifiedExtraAttributes = new HashMap();
+
+ private final Map unmodifiableQualifiedExtraAttributesView = Collections
+ .unmodifiableMap(qualifiedExtraAttributes);
+
+ public UnmodifiableExtendableItem(Map stdAttributes, Map extraAttributes) {
+ if (stdAttributes != null) {
+ this.attributes.putAll(stdAttributes);
+ }
+ if (extraAttributes != null) {
+ for (Iterator iter = extraAttributes.entrySet().iterator(); iter.hasNext();) {
+ Entry extraAtt = (Entry) iter.next();
+ setExtraAttribute((String) extraAtt.getKey(), (String) extraAtt.getValue());
+ }
+ }
+ }
+
+ public String getAttribute(String attName) {
+ return (String) attributes.get(attName);
+ }
+
+ public String getExtraAttribute(String attName) {
+ String v = (String) qualifiedExtraAttributes.get(attName);
+ if (v == null) {
+ v = (String) extraAttributes.get(attName);
+ }
+ return v;
+ }
+
+ protected void setExtraAttribute(String attName, String attValue) {
+ qualifiedExtraAttributes.put(attName, attValue);
+
+ // unqualify att name if required
+ int index = attName.indexOf(':');
+ if (index != -1) {
+ attName = attName.substring(index + 1);
+ }
+ extraAttributes.put(attName, attValue);
+ attributes.put(attName, attValue);
+ }
+
+ protected void setStandardAttribute(String attName, String attValue) {
+ attributes.put(attName, attValue);
+ }
+
+ public Map getAttributes() {
+ return unmodifiableAttributesView;
+ }
+
+ public Map getExtraAttributes() {
+ return unmodifiableExtraAttributesView;
+ }
+
+ public Map getQualifiedExtraAttributes() {
+ return unmodifiableQualifiedExtraAttributesView;
+ }
+
+}
diff --git a/src/java/org/apache/ivy/util/filter/AndFilter.java b/src/java/org/apache/ivy/util/filter/AndFilter.java
new file mode 100644
index 0000000..f747469
--- /dev/null
+++ b/src/java/org/apache/ivy/util/filter/AndFilter.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.util.filter;
+
+public class AndFilter implements Filter {
+ private Filter op1;
+
+ private Filter op2;
+
+ public AndFilter(Filter op1, Filter op2) {
+ this.op1 = op1;
+ this.op2 = op2;
+ }
+
+ public Filter getOp1() {
+ return op1;
+ }
+
+ public Filter getOp2() {
+ return op2;
+ }
+
+ public boolean accept(Object o) {
+ return op1.accept(o) && op2.accept(o);
+ }
+}
diff --git a/src/java/org/apache/ivy/util/filter/ArtifactTypeFilter.java b/src/java/org/apache/ivy/util/filter/ArtifactTypeFilter.java
new file mode 100644
index 0000000..932a891
--- /dev/null
+++ b/src/java/org/apache/ivy/util/filter/ArtifactTypeFilter.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.util.filter;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import org.apache.ivy.core.module.descriptor.Artifact;
+
+public class ArtifactTypeFilter implements Filter {
+ private Collection acceptedTypes;
+
+ public ArtifactTypeFilter(Collection acceptedTypes) {
+ this.acceptedTypes = new ArrayList(acceptedTypes);
+ }
+
+ public boolean accept(Object o) {
+ if (!(o instanceof Artifact)) {
+ return false;
+ }
+ Artifact art = (Artifact) o;
+ return acceptedTypes.contains(art.getType());
+ }
+}
diff --git a/src/java/org/apache/ivy/util/filter/Filter.java b/src/java/org/apache/ivy/util/filter/Filter.java
new file mode 100644
index 0000000..2950e14
--- /dev/null
+++ b/src/java/org/apache/ivy/util/filter/Filter.java
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.util.filter;
+
+public interface Filter {
+ boolean accept(Object o);
+}
diff --git a/src/java/org/apache/ivy/util/filter/FilterHelper.java b/src/java/org/apache/ivy/util/filter/FilterHelper.java
new file mode 100644
index 0000000..9d59ccd
--- /dev/null
+++ b/src/java/org/apache/ivy/util/filter/FilterHelper.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.util.filter;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+public final class FilterHelper {
+ private FilterHelper() {
+ }
+
+ public static final Filter NO_FILTER = NoFilter.INSTANCE;
+
+ public static Filter getArtifactTypeFilter(String types) {
+ if (types == null || types.trim().equals("*")) {
+ return NO_FILTER;
+ }
+ String[] t = types.split(",");
+ return getArtifactTypeFilter(t);
+ }
+
+ public static Filter getArtifactTypeFilter(String[] types) {
+ if (types == null || types.length == 0) {
+ return NO_FILTER;
+ }
+ List acceptedTypes = new ArrayList(types.length);
+ for (int i = 0; i < types.length; i++) {
+ String current = types[i].trim();
+ if ("*".equals(current)) {
+ return NO_FILTER;
+ }
+ acceptedTypes.add(current);
+ }
+ return new ArtifactTypeFilter(acceptedTypes);
+ }
+
+ /**
+ * Returns a new collection containing only the items from the given collectoin, which are
+ * accepted by the filter.
+ *
+ * @param col
+ * The collection to filter.
+ * @param filter
+ * The filter to use.
+ * @return A new collection instance containing the only the instance accepted by the filter.
+ *
+ * <br />
+ * Comment: We could have used <a
+ * href="http://jakarta.apache.org/commons/collections/">Commons-Collections</a>
+ * facility for this. If we accepted to add dependencies on third party jars.
+ */
+ public static Collection filter(Collection col, Filter filter) {
+ if (filter == null) {
+ return col;
+ }
+ Collection ret = new ArrayList(col);
+ for (Iterator iter = ret.iterator(); iter.hasNext();) {
+ Object element = iter.next();
+ if (!filter.accept(element)) {
+ iter.remove();
+ }
+ }
+ return ret;
+ }
+}
diff --git a/src/java/org/apache/ivy/util/filter/NoFilter.java b/src/java/org/apache/ivy/util/filter/NoFilter.java
new file mode 100644
index 0000000..92dbbe5
--- /dev/null
+++ b/src/java/org/apache/ivy/util/filter/NoFilter.java
@@ -0,0 +1,33 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.util.filter;
+
+public final class NoFilter implements Filter {
+ public static final Filter INSTANCE = new NoFilter();
+
+ private NoFilter() {
+ }
+
+ public boolean accept(Object o) {
+ return true;
+ }
+
+ public String toString() {
+ return "NoFilter";
+ }
+}
diff --git a/src/java/org/apache/ivy/util/filter/NotFilter.java b/src/java/org/apache/ivy/util/filter/NotFilter.java
new file mode 100644
index 0000000..7d55004
--- /dev/null
+++ b/src/java/org/apache/ivy/util/filter/NotFilter.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.util.filter;
+
+public class NotFilter implements Filter {
+ private Filter op;
+
+ public NotFilter(Filter op) {
+ this.op = op;
+ }
+
+ public Filter getOp() {
+ return op;
+ }
+
+ public boolean accept(Object o) {
+ return !op.accept(o);
+ }
+}
diff --git a/src/java/org/apache/ivy/util/filter/OrFilter.java b/src/java/org/apache/ivy/util/filter/OrFilter.java
new file mode 100644
index 0000000..46f1571
--- /dev/null
+++ b/src/java/org/apache/ivy/util/filter/OrFilter.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.util.filter;
+
+public class OrFilter implements Filter {
+ private Filter op1;
+
+ private Filter op2;
+
+ public OrFilter(Filter op1, Filter op2) {
+ this.op1 = op1;
+ this.op2 = op2;
+ }
+
+ public Filter getOp1() {
+ return op1;
+ }
+
+ public Filter getOp2() {
+ return op2;
+ }
+
+ public boolean accept(Object o) {
+ return op1.accept(o) || op2.accept(o);
+ }
+}
diff --git a/src/java/org/apache/ivy/util/url/AbstractURLHandler.java b/src/java/org/apache/ivy/util/url/AbstractURLHandler.java
new file mode 100644
index 0000000..2029724
--- /dev/null
+++ b/src/java/org/apache/ivy/util/url/AbstractURLHandler.java
@@ -0,0 +1,176 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.util.url;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.regex.Pattern;
+import java.util.zip.DataFormatException;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.Inflater;
+import java.util.zip.InflaterInputStream;
+import org.apache.ivy.Ivy;
+
+public abstract class AbstractURLHandler implements URLHandler {
+
+ private static final Pattern ESCAPE_PATTERN = Pattern.compile("%25([0-9a-fA-F][0-9a-fA-F])");
+
+ // the request method to use. TODO: don't use a static here
+ private static int requestMethod = REQUEST_METHOD_HEAD;
+
+ public boolean isReachable(URL url) {
+ return getURLInfo(url).isReachable();
+ }
+
+ public boolean isReachable(URL url, int timeout) {
+ return getURLInfo(url, timeout).isReachable();
+ }
+
+ public long getContentLength(URL url) {
+ return getURLInfo(url).getContentLength();
+ }
+
+ public long getContentLength(URL url, int timeout) {
+ return getURLInfo(url, timeout).getContentLength();
+ }
+
+ public long getLastModified(URL url) {
+ return getURLInfo(url).getLastModified();
+ }
+
+ public long getLastModified(URL url, int timeout) {
+ return getURLInfo(url, timeout).getLastModified();
+ }
+
+ protected String getUserAgent() {
+ String userAgent = System.getProperty("http.agent");
+ if (userAgent == null) {
+ userAgent = "Apache Ivy/" + Ivy.getIvyVersion();
+ }
+ return userAgent;
+ }
+
+ protected void validatePutStatusCode(URL dest, int statusCode, String statusMessage)
+ throws IOException {
+ switch (statusCode) {
+ case HttpURLConnection.HTTP_OK:
+ /* intentional fallthrough */
+ case HttpURLConnection.HTTP_CREATED:
+ /* intentional fallthrough */
+ case HttpURLConnection.HTTP_ACCEPTED:
+ /* intentional fallthrough */
+ case HttpURLConnection.HTTP_NO_CONTENT:
+ break;
+ case HttpURLConnection.HTTP_UNAUTHORIZED:
+ /* intentional fallthrough */
+ case HttpURLConnection.HTTP_FORBIDDEN:
+ throw new IOException("Access to URL " + dest + " was refused by the server"
+ + (statusMessage == null ? "" : ": " + statusMessage));
+ default:
+ throw new IOException("PUT operation to URL " + dest + " failed with status code "
+ + statusCode + (statusMessage == null ? "" : ": " + statusMessage));
+ }
+ }
+
+ public void setRequestMethod(int requestMethod) {
+ AbstractURLHandler.requestMethod = requestMethod;
+ }
+
+ public int getRequestMethod() {
+ return requestMethod;
+ }
+
+ protected String normalizeToString(URL url) throws IOException {
+ if (!"http".equals(url.getProtocol()) && !"https".equals(url.getProtocol())) {
+ return url.toExternalForm();
+ }
+
+ try {
+ URI uri = new URI(url.getProtocol(), url.getAuthority(), url.getPath(), url.getQuery(),
+ url.getRef());
+
+ // it is possible that the original url was already (partial) escaped,
+ // so we must unescape all '%' followed by 2 hexadecimals...
+ String uriString = uri.normalize().toASCIIString();
+
+ // manually escape the '+' character
+ uriString = uriString.replaceAll("\\+", "%2B");
+
+ return ESCAPE_PATTERN.matcher(uriString).replaceAll("%$1");
+ } catch (URISyntaxException e) {
+ IOException ioe = new MalformedURLException("Couldn't convert '" + url.toString()
+ + "' to a valid URI");
+ ioe.initCause(e);
+ throw ioe;
+ }
+ }
+
+ protected URL normalizeToURL(URL url) throws IOException {
+ if (!"http".equals(url.getProtocol()) && !"https".equals(url.getProtocol())) {
+ return url;
+ }
+
+ return new URL(normalizeToString(url));
+ }
+
+ protected InputStream getDecodingInputStream(String encoding, InputStream in)
+ throws IOException {
+ InputStream result = null;
+
+ if ("gzip".equals(encoding) || "x-gzip".equals(encoding)) {
+ result = new GZIPInputStream(in);
+ } else if ("deflate".equals(encoding)) {
+ // There seems to be 2 variants of the "deflate"-encoding.
+ // I couldn't find a way to auto-detect which variant was
+ // used, so as (a not really good) work-around we try do
+ // decompress the first 100 bytes using the "zlib"-variant.
+ BufferedInputStream bStream = new BufferedInputStream(in);
+ bStream.mark(100);
+ byte[] bytes = new byte[100];
+ int nbBytes = bStream.read(bytes);
+ bStream.reset();
+
+ Inflater inflater = new Inflater();
+ inflater.setInput(bytes, 0, nbBytes);
+ try {
+ inflater.inflate(new byte[1000]);
+
+ // no error decompressing the first 100 bytes, so we
+ // assume the "zlib"-variant was used.
+ result = new InflaterInputStream(bStream);
+ } catch (DataFormatException e) {
+ // there was an error decompressing the first 100 bytes,
+ // so we assume the "gzip/raw"-variant was used.
+ result = new InflaterInputStream(bStream, new Inflater(true));
+ } finally {
+ inflater.end();
+ }
+ } else {
+ result = in;
+ }
+
+ return result;
+ }
+
+}
diff --git a/src/java/org/apache/ivy/util/url/ApacheURLLister.java b/src/java/org/apache/ivy/util/url/ApacheURLLister.java
new file mode 100644
index 0000000..e2e7dea
--- /dev/null
+++ b/src/java/org/apache/ivy/util/url/ApacheURLLister.java
@@ -0,0 +1,209 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.util.url;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.ivy.util.FileUtil;
+import org.apache.ivy.util.Message;
+import org.apache.ivy.util.url.URLHandler.URLInfo;
+
+/**
+ * Utility class which helps to list urls under a given url. This has been tested with Apache 1.3.33
+ * server listing, as the one used at ibiblio, and with Apache 2.0.53 server listing, as the one on
+ * mirrors.sunsite.dk.
+ */
+public class ApacheURLLister {
+ // ~ Static variables/initializers ------------------------------------------
+
+ private static final Pattern PATTERN = Pattern.compile(
+ "<a[^>]*href=\"([^\"]*)\"[^>]*>(?:<[^>]+>)*?([^<>]+?)(?:<[^>]+>)*?</a>",
+ Pattern.CASE_INSENSITIVE);
+
+ // ~ Methods ----------------------------------------------------------------
+
+ /**
+ * Returns a list of sub urls of the given url. The returned list is a list of URL.
+ *
+ * @param url
+ * The base URL from which to retrieve the listing.
+ * @return a list of sub urls of the given url.
+ * @throws IOException
+ * If an error occures retrieving the HTML.
+ */
+ public List listAll(URL url) throws IOException {
+ return retrieveListing(url, true, true);
+ }
+
+ /**
+ * Returns a list of sub 'directories' of the given url. The returned list is a list of URL.
+ *
+ * @param url
+ * The base URL from which to retrieve the listing.
+ * @return a list of sub 'directories' of the given url.
+ * @throws IOException
+ * If an error occures retrieving the HTML.
+ */
+ public List listDirectories(URL url) throws IOException {
+ return retrieveListing(url, false, true);
+ }
+
+ /**
+ * Returns a list of sub 'files' (in opposition to directories) of the given url. The returned
+ * list is a list of URL.
+ *
+ * @param url
+ * The base URL from which to retrieve the listing.
+ * @return a list of sub 'files' of the given url.
+ * @throws IOException
+ * If an error occures retrieving the HTML.
+ */
+ public List listFiles(URL url) throws IOException {
+ return retrieveListing(url, true, false);
+ }
+
+ /**
+ * Retrieves a {@link List} of {@link URL}s corresponding to the files and/or directories found
+ * at the supplied base URL.
+ *
+ * @param url
+ * The base URL from which to retrieve the listing.
+ * @param includeFiles
+ * If true include files in the returned list.
+ * @param includeDirectories
+ * If true include directories in the returned list.
+ * @return A {@link List} of {@link URL}s.
+ * @throws IOException
+ * If an error occures retrieving the HTML.
+ */
+ public List retrieveListing(URL url, boolean includeFiles, boolean includeDirectories)
+ throws IOException {
+ List urlList = new ArrayList();
+
+ // add trailing slash for relative urls
+ if (!url.getPath().endsWith("/") && !url.getPath().endsWith(".html")) {
+ url = new URL(url.getProtocol(), url.getHost(), url.getPort(), url.getPath() + "/");
+ }
+
+ URLHandler urlHandler = URLHandlerRegistry.getDefault();
+ URLInfo urlInfo = urlHandler.getURLInfo(url);
+ if (urlInfo == URLHandler.UNAVAILABLE) {
+ return urlList; // not found => return empty list
+ }
+ // here, urlInfo is valid
+ String charset = urlInfo.getBodyCharset();
+
+ InputStream contentStream = urlHandler.openStream(url);
+ BufferedReader r = null;
+ if (charset == null) {
+ r = new BufferedReader(new InputStreamReader(contentStream));
+ } else {
+ r = new BufferedReader(new InputStreamReader(contentStream, charset));
+ }
+
+ String htmlText = FileUtil.readEntirely(r);
+
+ Matcher matcher = PATTERN.matcher(htmlText);
+
+ while (matcher.find()) {
+ // get the href text and the displayed text
+ String href = matcher.group(1);
+ String text = matcher.group(2);
+
+ if ((href == null) || (text == null)) {
+ // the groups were not found (shouldn't happen, really)
+ continue;
+ }
+
+ text = text.trim();
+
+ // handle complete URL listings
+ if (href.startsWith("http:") || href.startsWith("https:")) {
+ try {
+ href = new URL(href).getPath();
+ if (!href.startsWith(url.getPath())) {
+ // ignore URLs which aren't children of the base URL
+ continue;
+ }
+ href = href.substring(url.getPath().length());
+ } catch (Exception ignore) {
+ // incorrect URL, ignore
+ continue;
+ }
+ }
+
+ if (href.startsWith("../")) {
+ // we are only interested in sub-URLs, not parent URLs, so skip this one
+ continue;
+ }
+
+ // absolute href: convert to relative one
+ if (href.startsWith("/")) {
+ int slashIndex = href.substring(0, href.length() - 1).lastIndexOf('/');
+ href = href.substring(slashIndex + 1);
+ }
+
+ // relative to current href: convert to simple relative one
+ if (href.startsWith("./")) {
+ href = href.substring("./".length());
+ }
+
+ // exclude those where they do not match
+ // href will never be truncated, text may be truncated by apache
+ if (text.endsWith("..>")) {
+ // text is probably truncated, we can only check if the href starts with text
+ if (!href.startsWith(text.substring(0, text.length() - 3))) {
+ continue;
+ }
+ } else if (text.endsWith("..>")) {
+ // text is probably truncated, we can only check if the href starts with text
+ if (!href.startsWith(text.substring(0, text.length() - 6))) {
+ continue;
+ }
+ } else {
+ // text is not truncated, so it must match the url after stripping optional
+ // trailing slashes
+ String strippedHref = href.endsWith("/") ? href.substring(0, href.length() - 1)
+ : href;
+ String strippedText = text.endsWith("/") ? text.substring(0, text.length() - 1)
+ : text;
+ if (!strippedHref.equalsIgnoreCase(strippedText)) {
+ continue;
+ }
+ }
+
+ boolean directory = href.endsWith("/");
+
+ if ((directory && includeDirectories) || (!directory && includeFiles)) {
+ URL child = new URL(url, href);
+ urlList.add(child);
+ Message.debug("ApacheURLLister found URL=[" + child + "].");
+ }
+ }
+
+ return urlList;
+ }
+}
diff --git a/src/java/org/apache/ivy/util/url/BasicURLHandler.java b/src/java/org/apache/ivy/util/url/BasicURLHandler.java
new file mode 100644
index 0000000..72641ef
--- /dev/null
+++ b/src/java/org/apache/ivy/util/url/BasicURLHandler.java
@@ -0,0 +1,330 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.util.url;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.UnknownHostException;
+
+import org.apache.ivy.Ivy;
+import org.apache.ivy.util.CopyProgressListener;
+import org.apache.ivy.util.FileUtil;
+import org.apache.ivy.util.Message;
+
+/**
+ *
+ */
+public class BasicURLHandler extends AbstractURLHandler {
+
+ private static final int BUFFER_SIZE = 64 * 1024;
+
+ private static final class HttpStatus {
+ static final int SC_OK = 200;
+
+ static final int SC_PROXY_AUTHENTICATION_REQUIRED = 407;
+
+ private HttpStatus() {
+ }
+ }
+
+ public URLInfo getURLInfo(URL url) {
+ return getURLInfo(url, 0);
+ }
+
+ public URLInfo getURLInfo(URL url, int timeout) {
+ // Install the IvyAuthenticator
+ if ("http".equals(url.getProtocol()) || "https".equals(url.getProtocol())) {
+ IvyAuthenticator.install();
+ }
+
+ URLConnection con = null;
+ try {
+ url = normalizeToURL(url);
+ con = url.openConnection();
+ con.setRequestProperty("User-Agent", getUserAgent());
+ if (con instanceof HttpURLConnection) {
+ HttpURLConnection httpCon = (HttpURLConnection) con;
+ if (getRequestMethod() == URLHandler.REQUEST_METHOD_HEAD) {
+ httpCon.setRequestMethod("HEAD");
+ }
+ if (checkStatusCode(url, httpCon)) {
+ String bodyCharset = getCharSetFromContentType(con.getContentType());
+ return new URLInfo(true, httpCon.getContentLength(), con.getLastModified(),
+ bodyCharset);
+ }
+ } else {
+ int contentLength = con.getContentLength();
+ if (contentLength <= 0) {
+ return UNAVAILABLE;
+ } else {
+ // TODO: not HTTP... maybe we *don't* want to default to ISO-8559-1 here?
+ String bodyCharset = getCharSetFromContentType(con.getContentType());
+ return new URLInfo(true, contentLength, con.getLastModified(), bodyCharset);
+ }
+ }
+ } catch (UnknownHostException e) {
+ Message.warn("Host " + e.getMessage() + " not found. url=" + url);
+ Message.info("You probably access the destination server through "
+ + "a proxy server that is not well configured.");
+ } catch (IOException e) {
+ Message.error("Server access error at url " + url, e);
+ } finally {
+ disconnect(con);
+ }
+ return UNAVAILABLE;
+ }
+
+ /**
+ * Extract the charset from the Content-Type header string, or default to ISO-8859-1 as per
+ * rfc2616-sec3.html#sec3.7.1 .
+ *
+ * @param contentType
+ * the Content-Type header string
+ * @return the charset as specified in the content type, or ISO-8859-1 if unspecified.
+ */
+ public static String getCharSetFromContentType(String contentType) {
+
+ String charSet = null;
+
+ if (contentType != null) {
+ String[] elements = contentType.split(";");
+ for (int i = 0; i < elements.length; i++) {
+ String element = elements[i].trim();
+ if (element.toLowerCase().startsWith("charset=")) {
+ charSet = element.substring("charset=".length());
+ }
+ }
+ }
+
+ if (charSet == null || charSet.length() == 0) {
+ // default to ISO-8859-1 as per rfc2616-sec3.html#sec3.7.1
+ charSet = "ISO-8859-1";
+ }
+
+ return charSet;
+ }
+
+ private boolean checkStatusCode(URL url, HttpURLConnection con) throws IOException {
+ int status = con.getResponseCode();
+ if (status == HttpStatus.SC_OK) {
+ return true;
+ }
+
+ // IVY-1328: some servers return a 204 on a HEAD request
+ if ("HEAD".equals(con.getRequestMethod()) && (status == 204)) {
+ return true;
+ }
+
+ Message.debug("HTTP response status: " + status + " url=" + url);
+ if (status == HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED) {
+ Message.warn("Your proxy requires authentication.");
+ } else if (String.valueOf(status).startsWith("4")) {
+ Message.verbose("CLIENT ERROR: " + con.getResponseMessage() + " url=" + url);
+ } else if (String.valueOf(status).startsWith("5")) {
+ Message.error("SERVER ERROR: " + con.getResponseMessage() + " url=" + url);
+ }
+ return false;
+ }
+
+ public InputStream openStream(URL url) throws IOException {
+ // Install the IvyAuthenticator
+ if ("http".equals(url.getProtocol()) || "https".equals(url.getProtocol())) {
+ IvyAuthenticator.install();
+ }
+
+ URLConnection conn = null;
+ try {
+ url = normalizeToURL(url);
+ conn = url.openConnection();
+ conn.setRequestProperty("User-Agent", getUserAgent());
+ conn.setRequestProperty("Accept-Encoding", "gzip,deflate");
+ if (conn instanceof HttpURLConnection) {
+ HttpURLConnection httpCon = (HttpURLConnection) conn;
+ if (!checkStatusCode(url, httpCon)) {
+ throw new IOException("The HTTP response code for " + url
+ + " did not indicate a success." + " See log for more detail.");
+ }
+ }
+ InputStream inStream = getDecodingInputStream(conn.getContentEncoding(),
+ conn.getInputStream());
+ ByteArrayOutputStream outStream = new ByteArrayOutputStream();
+
+ byte[] buffer = new byte[BUFFER_SIZE];
+ int len;
+ while ((len = inStream.read(buffer)) > 0) {
+ outStream.write(buffer, 0, len);
+ }
+ return new ByteArrayInputStream(outStream.toByteArray());
+ } finally {
+ disconnect(conn);
+ }
+ }
+
+ public void download(URL src, File dest, CopyProgressListener l) throws IOException {
+ // Install the IvyAuthenticator
+ if ("http".equals(src.getProtocol()) || "https".equals(src.getProtocol())) {
+ IvyAuthenticator.install();
+ }
+
+ URLConnection srcConn = null;
+ try {
+ src = normalizeToURL(src);
+ srcConn = src.openConnection();
+ srcConn.setRequestProperty("User-Agent", getUserAgent());
+ srcConn.setRequestProperty("Accept-Encoding", "gzip,deflate");
+ if (srcConn instanceof HttpURLConnection) {
+ HttpURLConnection httpCon = (HttpURLConnection) srcConn;
+ if (!checkStatusCode(src, httpCon)) {
+ throw new IOException("The HTTP response code for " + src
+ + " did not indicate a success." + " See log for more detail.");
+ }
+ }
+
+ // do the download
+ InputStream inStream = getDecodingInputStream(srcConn.getContentEncoding(),
+ srcConn.getInputStream());
+ FileUtil.copy(inStream, dest, l);
+
+ // check content length only if content was not encoded
+ if (srcConn.getContentEncoding() == null) {
+ int contentLength = srcConn.getContentLength();
+ if (contentLength != -1 && dest.length() != contentLength) {
+ dest.delete();
+ throw new IOException(
+ "Downloaded file size doesn't match expected Content Length for " + src
+ + ". Please retry.");
+ }
+ }
+
+ // update modification date
+ long lastModified = srcConn.getLastModified();
+ if (lastModified > 0) {
+ dest.setLastModified(lastModified);
+ }
+ } finally {
+ disconnect(srcConn);
+ }
+ }
+
+ public void upload(File source, URL dest, CopyProgressListener l) throws IOException {
+ if (!"http".equals(dest.getProtocol()) && !"https".equals(dest.getProtocol())) {
+ throw new UnsupportedOperationException(
+ "URL repository only support HTTP PUT at the moment");
+ }
+
+ // Install the IvyAuthenticator
+ IvyAuthenticator.install();
+
+ HttpURLConnection conn = null;
+ try {
+ dest = normalizeToURL(dest);
+ conn = (HttpURLConnection) dest.openConnection();
+ conn.setDoOutput(true);
+ conn.setRequestMethod("PUT");
+ conn.setRequestProperty("User-Agent", getUserAgent());
+ conn.setRequestProperty("Content-type", "application/octet-stream");
+ conn.setRequestProperty("Content-length", Long.toString(source.length()));
+ conn.setInstanceFollowRedirects(true);
+
+ InputStream in = new FileInputStream(source);
+ try {
+ OutputStream os = conn.getOutputStream();
+ FileUtil.copy(in, os, l);
+ } finally {
+ try {
+ in.close();
+ } catch (IOException e) {
+ /* ignored */
+ }
+ }
+ validatePutStatusCode(dest, conn.getResponseCode(), conn.getResponseMessage());
+ } finally {
+ disconnect(conn);
+ }
+ }
+
+ private void disconnect(URLConnection con) {
+ if (con instanceof HttpURLConnection) {
+ if (!"HEAD".equals(((HttpURLConnection) con).getRequestMethod())) {
+ // We must read the response body before disconnecting!
+ // Cfr. http://java.sun.com/j2se/1.5.0/docs/guide/net/http-keepalive.html
+ // [quote]Do not abandon a connection by ignoring the response body. Doing
+ // so may results in idle TCP connections.[/quote]
+ readResponseBody((HttpURLConnection) con);
+ }
+
+ ((HttpURLConnection) con).disconnect();
+ } else if (con != null) {
+ try {
+ con.getInputStream().close();
+ } catch (IOException e) {
+ // ignored
+ }
+ }
+ }
+
+ /**
+ * Read and ignore the response body.
+ */
+ private void readResponseBody(HttpURLConnection conn) {
+ byte[] buffer = new byte[BUFFER_SIZE];
+
+ InputStream inStream = null;
+ try {
+ inStream = conn.getInputStream();
+ while (inStream.read(buffer) > 0) {
+ // Skip content
+ }
+ } catch (IOException e) {
+ // ignore
+ } finally {
+ if (inStream != null) {
+ try {
+ inStream.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ }
+
+ InputStream errStream = conn.getErrorStream();
+ if (errStream != null) {
+ try {
+ while (errStream.read(buffer) > 0) {
+ // Skip content
+ }
+ } catch (IOException e) {
+ // ignore
+ } finally {
+ try {
+ errStream.close();
+ } catch (IOException e) {
+ // ignore
+ }
+ }
+ }
+ }
+}
diff --git a/src/java/org/apache/ivy/util/url/CredentialsStore.java b/src/java/org/apache/ivy/util/url/CredentialsStore.java
new file mode 100644
index 0000000..50b973c
--- /dev/null
+++ b/src/java/org/apache/ivy/util/url/CredentialsStore.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.util.url;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.ivy.util.Credentials;
+import org.apache.ivy.util.Message;
+
+/**
+ *
+ */
+public final class CredentialsStore {
+ /**
+ * A Map of Credentials objects keyed by the 'key' of the Credentials.
+ */
+ private static final Map KEYRING = new HashMap();
+
+ private static final Set SECURED_HOSTS = new HashSet();
+
+ public static final CredentialsStore INSTANCE = new CredentialsStore();
+
+ private CredentialsStore() {
+ }
+
+ public void addCredentials(String realm, String host, String userName, String passwd) {
+ if (userName == null) {
+ return;
+ }
+ Credentials c = new Credentials(realm, host, userName, passwd);
+ Message.debug("credentials added: " + c);
+ KEYRING.put(c.getKey(), c);
+ SECURED_HOSTS.add(host);
+ }
+
+ public Credentials getCredentials(String realm, String host) {
+ String key = Credentials.buildKey(realm, host);
+ Message.debug("try to get credentials for: " + key);
+ return (Credentials) KEYRING.get(key);
+ }
+
+ public boolean hasCredentials(String host) {
+ return SECURED_HOSTS.contains(host);
+ }
+
+}
diff --git a/src/java/org/apache/ivy/util/url/HttpClientHandler.java b/src/java/org/apache/ivy/util/url/HttpClientHandler.java
new file mode 100644
index 0000000..64d4a25
--- /dev/null
+++ b/src/java/org/apache/ivy/util/url/HttpClientHandler.java
@@ -0,0 +1,431 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.util.url;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URL;
+import java.net.UnknownHostException;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+import org.apache.commons.httpclient.Credentials;
+import org.apache.commons.httpclient.Header;
+import org.apache.commons.httpclient.HttpClient;
+import org.apache.commons.httpclient.HttpException;
+import org.apache.commons.httpclient.HttpMethodBase;
+import org.apache.commons.httpclient.HttpStatus;
+import org.apache.commons.httpclient.MultiThreadedHttpConnectionManager;
+import org.apache.commons.httpclient.NTCredentials;
+import org.apache.commons.httpclient.auth.AuthPolicy;
+import org.apache.commons.httpclient.auth.AuthScheme;
+import org.apache.commons.httpclient.auth.AuthScope;
+import org.apache.commons.httpclient.auth.CredentialsNotAvailableException;
+import org.apache.commons.httpclient.auth.CredentialsProvider;
+import org.apache.commons.httpclient.methods.GetMethod;
+import org.apache.commons.httpclient.methods.HeadMethod;
+import org.apache.commons.httpclient.methods.PutMethod;
+import org.apache.commons.httpclient.methods.RequestEntity;
+import org.apache.commons.httpclient.params.HttpMethodParams;
+import org.apache.ivy.Ivy;
+import org.apache.ivy.util.CopyProgressListener;
+import org.apache.ivy.util.FileUtil;
+import org.apache.ivy.util.HostUtil;
+import org.apache.ivy.util.Message;
+
+/**
+ *
+ */
+public class HttpClientHandler extends AbstractURLHandler {
+ private static final SimpleDateFormat LAST_MODIFIED_FORMAT = new SimpleDateFormat(
+ "EEE, d MMM yyyy HH:mm:ss z", Locale.US);
+
+ // proxy configuration: obtain from system properties
+ private int proxyPort;
+
+ private String proxyHost = null;
+
+ private String proxyUserName = null;
+
+ private String proxyPasswd = null;
+
+ private HttpClientHelper httpClientHelper;
+
+ private static HttpClient httpClient;
+
+ public HttpClientHandler() {
+ configureProxy();
+ }
+
+ private void configureProxy() {
+ proxyHost = System.getProperty("http.proxyHost");
+ // TODO constant is better ...
+ if (useProxy()) {
+ proxyPort = Integer.parseInt(System.getProperty("http.proxyPort", "80"));
+ proxyUserName = System.getProperty("http.proxyUser");
+ proxyPasswd = System.getProperty("http.proxyPassword");
+ // It seems there is no equivalent in HttpClient for
+ // 'http.nonProxyHosts' property
+ Message.verbose("proxy configured: host=" + proxyHost + " port=" + proxyPort + " user="
+ + proxyUserName);
+ } else {
+ Message.verbose("no proxy configured");
+ }
+ }
+
+ public InputStream openStream(URL url) throws IOException {
+ GetMethod get = doGet(url, 0);
+ if (!checkStatusCode(url, get)) {
+ get.releaseConnection();
+ throw new IOException("The HTTP response code for " + url
+ + " did not indicate a success." + " See log for more detail.");
+ }
+
+ Header encoding = get.getResponseHeader("Content-Encoding");
+ return getDecodingInputStream(encoding == null ? null : encoding.getValue(),
+ get.getResponseBodyAsStream());
+ }
+
+ public void download(URL src, File dest, CopyProgressListener l) throws IOException {
+ GetMethod get = doGet(src, 0);
+ try {
+ // We can only figure the content we got is want we want if the status is success.
+ if (!checkStatusCode(src, get)) {
+ throw new IOException("The HTTP response code for " + src
+ + " did not indicate a success." + " See log for more detail.");
+ }
+
+ Header encoding = get.getResponseHeader("Content-Encoding");
+ InputStream is = getDecodingInputStream(encoding == null ? null : encoding.getValue(),
+ get.getResponseBodyAsStream());
+ FileUtil.copy(is, dest, l);
+ dest.setLastModified(getLastModified(get));
+ } finally {
+ get.releaseConnection();
+ }
+ }
+
+ public void upload(File src, URL dest, CopyProgressListener l) throws IOException {
+ HttpClient client = getClient();
+
+ PutMethod put = new PutMethod(normalizeToString(dest));
+ put.setDoAuthentication(useAuthentication(dest) || useProxyAuthentication());
+ put.getParams().setBooleanParameter("http.protocol.expect-continue", true);
+ try {
+ put.setRequestEntity(new FileRequestEntity(src));
+ int statusCode = client.executeMethod(put);
+ validatePutStatusCode(dest, statusCode, null);
+ } finally {
+ put.releaseConnection();
+ }
+ }
+
+ public URLInfo getURLInfo(URL url) {
+ return getURLInfo(url, 0);
+ }
+
+ public URLInfo getURLInfo(URL url, int timeout) {
+ HttpMethodBase method = null;
+ try {
+ if (getRequestMethod() == URLHandler.REQUEST_METHOD_HEAD) {
+ method = doHead(url, timeout);
+ } else {
+ method = doGet(url, timeout);
+ }
+ if (checkStatusCode(url, method)) {
+ return new URLInfo(true, getResponseContentLength(method), getLastModified(method),
+ method.getRequestCharSet());
+ }
+ } catch (HttpException e) {
+ Message.error("HttpClientHandler: " + e.getMessage() + ":" + e.getReasonCode() + "="
+ + e.getReason() + " url=" + url);
+ } catch (UnknownHostException e) {
+ Message.warn("Host " + e.getMessage() + " not found. url=" + url);
+ Message.info("You probably access the destination server through "
+ + "a proxy server that is not well configured.");
+ } catch (IOException e) {
+ Message.error("HttpClientHandler: " + e.getMessage() + " url=" + url);
+ } catch (IllegalArgumentException e) {
+ // thrown by HttpClient to indicate the URL is not valid, this happens for instance
+ // when trying to download a dynamic version (cfr IVY-390)
+ } finally {
+ if (method != null) {
+ method.releaseConnection();
+ }
+ }
+ return UNAVAILABLE;
+ }
+
+ private boolean checkStatusCode(URL url, HttpMethodBase method) throws IOException {
+ int status = method.getStatusCode();
+ if (status == HttpStatus.SC_OK) {
+ return true;
+ }
+
+ // IVY-1328: some servers return a 204 on a HEAD request
+ if ("HEAD".equals(method.getName()) && (status == 204)) {
+ return true;
+ }
+
+ Message.debug("HTTP response status: " + status + " url=" + url);
+ if (status == HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED) {
+ Message.warn("Your proxy requires authentication.");
+ } else if (String.valueOf(status).startsWith("4")) {
+ Message.verbose("CLIENT ERROR: " + method.getStatusText() + " url=" + url);
+ } else if (String.valueOf(status).startsWith("5")) {
+ Message.error("SERVER ERROR: " + method.getStatusText() + " url=" + url);
+ }
+
+ return false;
+ }
+
+ private long getLastModified(HttpMethodBase method) {
+ Header header = method.getResponseHeader("last-modified");
+ if (header != null) {
+ String lastModified = header.getValue();
+ try {
+ return LAST_MODIFIED_FORMAT.parse(lastModified).getTime();
+ } catch (ParseException e) {
+ // ignored
+ }
+ return System.currentTimeMillis();
+ } else {
+ return System.currentTimeMillis();
+ }
+ }
+
+ private long getResponseContentLength(HttpMethodBase head) {
+ return getHttpClientHelper().getResponseContentLength(head);
+ }
+
+ private HttpClientHelper getHttpClientHelper() {
+ if (httpClientHelper == null) {
+ // use commons httpclient 3.0 if available
+ try {
+ HttpMethodBase.class.getMethod("getResponseContentLength", new Class[0]);
+ httpClientHelper = new HttpClientHelper3x();
+ Message.verbose("using commons httpclient 3.x helper");
+ } catch (SecurityException e) {
+ Message.verbose("unable to get access to getResponseContentLength of "
+ + "commons-httpclient HeadMethod. Please use commons-httpclient 3.0 or "
+ + "use ivy with sufficient security permissions.");
+ Message.verbose("exception: " + e.getMessage());
+ httpClientHelper = new HttpClientHelper2x();
+ Message.verbose("using commons httpclient 2.x helper");
+ } catch (NoSuchMethodException e) {
+ httpClientHelper = new HttpClientHelper2x();
+ Message.verbose("using commons httpclient 2.x helper");
+ }
+ }
+ return httpClientHelper;
+ }
+
+ public int getHttpClientMajorVersion() {
+ HttpClientHelper helper = getHttpClientHelper();
+ return helper.getHttpClientMajorVersion();
+ }
+
+ private GetMethod doGet(URL url, int timeout) throws IOException {
+ HttpClient client = getClient();
+ client.setTimeout(timeout);
+
+ GetMethod get = new GetMethod(normalizeToString(url));
+ get.setDoAuthentication(useAuthentication(url) || useProxyAuthentication());
+ get.setRequestHeader("Accept-Encoding", "gzip,deflate");
+ client.executeMethod(get);
+ return get;
+ }
+
+ private HeadMethod doHead(URL url, int timeout) throws IOException {
+ HttpClient client = getClient();
+ client.setTimeout(timeout);
+
+ HeadMethod head = new HeadMethod(normalizeToString(url));
+ head.setDoAuthentication(useAuthentication(url) || useProxyAuthentication());
+ client.executeMethod(head);
+ return head;
+ }
+
+ private HttpClient getClient() {
+ if (httpClient == null) {
+ final MultiThreadedHttpConnectionManager connManager = new MultiThreadedHttpConnectionManager();
+ httpClient = new HttpClient(connManager);
+
+ Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
+ public void run() {
+ connManager.shutdown();
+ }
+ }));
+
+ List authPrefs = new ArrayList(3);
+ authPrefs.add(AuthPolicy.DIGEST);
+ authPrefs.add(AuthPolicy.BASIC);
+ authPrefs.add(AuthPolicy.NTLM); // put it at the end to give less priority (IVY-213)
+ httpClient.getParams().setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, authPrefs);
+
+ if (useProxy()) {
+ httpClient.getHostConfiguration().setProxy(proxyHost, proxyPort);
+ if (useProxyAuthentication()) {
+ httpClient.getState().setProxyCredentials(
+ new AuthScope(proxyHost, proxyPort, AuthScope.ANY_REALM),
+ createCredentials(proxyUserName, proxyPasswd));
+ }
+ }
+
+ // user-agent
+ httpClient.getParams().setParameter(HttpMethodParams.USER_AGENT,
+ getUserAgent());
+
+ // authentication
+ httpClient.getParams().setParameter(CredentialsProvider.PROVIDER,
+ new IvyCredentialsProvider());
+ }
+
+ return httpClient;
+ }
+
+ private boolean useProxy() {
+ return proxyHost != null && proxyHost.trim().length() > 0;
+ }
+
+ private boolean useAuthentication(URL url) {
+ return CredentialsStore.INSTANCE.hasCredentials(url.getHost());
+ }
+
+ private boolean useProxyAuthentication() {
+ return (proxyUserName != null && proxyUserName.trim().length() > 0);
+ }
+
+ private static final class HttpClientHelper3x implements HttpClientHelper {
+ private static final int VERSION = 3;
+
+ private HttpClientHelper3x() {
+ }
+
+ public long getResponseContentLength(HttpMethodBase method) {
+ return method.getResponseContentLength();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int getHttpClientMajorVersion() {
+ return VERSION;
+ }
+ }
+
+ private static final class HttpClientHelper2x implements HttpClientHelper {
+ private static final int VERSION = 2;
+
+ private HttpClientHelper2x() {
+ }
+
+ public long getResponseContentLength(HttpMethodBase method) {
+ Header header = method.getResponseHeader("Content-Length");
+ if (header != null) {
+ try {
+ return Integer.parseInt(header.getValue());
+ } catch (NumberFormatException e) {
+ Message.verbose("Invalid content-length value: " + e.getMessage());
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int getHttpClientMajorVersion() {
+ return VERSION;
+ }
+ }
+
+ public interface HttpClientHelper {
+ long getResponseContentLength(HttpMethodBase method);
+
+ int getHttpClientMajorVersion();
+ }
+
+ private static class IvyCredentialsProvider implements CredentialsProvider {
+
+ public Credentials getCredentials(AuthScheme scheme, String host, int port, boolean proxy)
+ throws CredentialsNotAvailableException {
+ String realm = scheme.getRealm();
+
+ org.apache.ivy.util.Credentials c = CredentialsStore.INSTANCE.getCredentials(realm,
+ host);
+ if (c != null) {
+ return createCredentials(c.getUserName(), c.getPasswd());
+ }
+
+ return null;
+ }
+ }
+
+ private static Credentials createCredentials(String username, String password) {
+ String user;
+ String domain;
+
+ int backslashIndex = username.indexOf('\\');
+ if (backslashIndex >= 0) {
+ user = username.substring(backslashIndex + 1);
+ domain = username.substring(0, backslashIndex);
+ } else {
+ user = username;
+ domain = System.getProperty("http.auth.ntlm.domain", "");
+ }
+
+ return new NTCredentials(user, password, HostUtil.getLocalHostName(), domain);
+ }
+
+ private static class FileRequestEntity implements RequestEntity {
+ private File file;
+
+ public FileRequestEntity(File file) {
+ this.file = file;
+ }
+
+ public long getContentLength() {
+ return file.length();
+ }
+
+ public String getContentType() {
+ return null;
+ }
+
+ public boolean isRepeatable() {
+ return true;
+ }
+
+ public void writeRequest(OutputStream out) throws IOException {
+ InputStream instream = new FileInputStream(file);
+ try {
+ FileUtil.copy(instream, out, null, false);
+ } finally {
+ instream.close();
+ }
+ }
+ }
+
+}
diff --git a/src/java/org/apache/ivy/util/url/IvyAuthenticator.java b/src/java/org/apache/ivy/util/url/IvyAuthenticator.java
new file mode 100644
index 0000000..a529ad2
--- /dev/null
+++ b/src/java/org/apache/ivy/util/url/IvyAuthenticator.java
@@ -0,0 +1,157 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.util.url;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.net.Authenticator;
+import java.net.PasswordAuthentication;
+
+import org.apache.ivy.util.Credentials;
+import org.apache.ivy.util.Message;
+
+/**
+ *
+ */
+public final class IvyAuthenticator extends Authenticator {
+
+ private Authenticator original;
+
+ private static boolean securityWarningLogged = false;
+
+ /**
+ * Private c'tor to prevent instantiation.
+ */
+ private IvyAuthenticator(Authenticator original) {
+ this.original = original;
+ }
+
+ /**
+ * Installs an <tt>IvyAuthenticator</tt> as default <tt>Authenticator</tt>. Call this method
+ * before opening HTTP(S) connections to enable Ivy authentication.
+ */
+ public static void install() {
+ // We will try to use the original authenticator as backup authenticator.
+ // Since there is no getter available, so try to use some reflection to
+ // obtain it. If that doesn't work, assume there is no original authenticator
+ Authenticator original = null;
+
+ try {
+ Field f = Authenticator.class.getDeclaredField("theAuthenticator");
+ f.setAccessible(true);
+ original = (Authenticator) f.get(null);
+ } catch (Throwable t) {
+ Message.debug("Error occurred while getting the original authenticator: "
+ + t.getMessage());
+ }
+
+ if (!(original instanceof IvyAuthenticator)) {
+ try {
+ Authenticator.setDefault(new IvyAuthenticator(original));
+ } catch (SecurityException e) {
+ if (!securityWarningLogged) {
+ securityWarningLogged = true;
+ Message.warn("Not enough permissions to set the IvyAuthenticator. "
+ + "HTTP(S) authentication will be disabled!");
+ }
+ }
+ }
+ }
+
+ // API ******************************************************************
+
+ // Overriding Authenticator *********************************************
+
+ protected PasswordAuthentication getPasswordAuthentication() {
+ PasswordAuthentication result = null;
+
+ if (isProxyAuthentication()) {
+ String proxyUser = System.getProperty("http.proxyUser");
+ if ((proxyUser != null) && (proxyUser.trim().length() > 0)) {
+ String proxyPass = System.getProperty("http.proxyPassword", "");
+ Message.debug("authenicating to proxy server with username [" + proxyUser + "]");
+ result = new PasswordAuthentication(proxyUser, proxyPass.toCharArray());
+ }
+ } else {
+ Credentials c = CredentialsStore.INSTANCE.getCredentials(getRequestingPrompt(),
+ getRequestingHost());
+ Message.debug("authentication: k='"
+ + Credentials.buildKey(getRequestingPrompt(), getRequestingHost()) + "' c='"
+ + c + "'");
+ if (c != null) {
+ final String password = c.getPasswd() == null ? "" : c.getPasswd();
+ result = new PasswordAuthentication(c.getUserName(), password.toCharArray());
+ }
+ }
+
+ if ((result == null) && (original != null)) {
+ Authenticator.setDefault(original);
+ try {
+ result = Authenticator.requestPasswordAuthentication(getRequestingHost(),
+ getRequestingSite(), getRequestingPort(), getRequestingProtocol(),
+ getRequestingPrompt(), getRequestingScheme());
+ } finally {
+ Authenticator.setDefault(this);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Checks if the current authentication request is for the proxy server. This functionality is
+ * not available in JDK1.4, so we check this in a very dirty way which is probably not very
+ * portable, but will work for the SUN 1.4 JDKs.
+ *
+ * @return
+ */
+ private boolean isProxyAuthentication() {
+ try {
+ // we first try to invoke the getRequestorType() method which is a JDK1.5+ method
+ Method m = Authenticator.class.getDeclaredMethod("getRequestorType", null);
+ Object result = m.invoke(this, null);
+ return "PROXY".equals(String.valueOf(result));
+ } catch (NoSuchMethodException e) {
+ // do nothing, this is a JDK1.5+ method
+ } catch (Throwable t) {
+ Message.debug("Error occurred while checking if the authentication request is for the proxy server: "
+ + t.getMessage());
+ }
+
+ // now we will do something very dirty and analyse the stack trace to see
+ // if this method is called from within the 'getHttpProxyAuthentication' method
+ // or the 'getServerAuthentication' method which are both part of the
+ // sun.net.www.protocol.http.HttpURLConnection class.
+ // This might not work on other 1.4 JVM's!
+ // This code should be removed when Ivy requires JDK1.5+
+ StackTraceElement[] stackTrace = (new Exception()).getStackTrace();
+ for (int i = 0; i < stackTrace.length; i++) {
+ if ("getHttpProxyAuthentication".equals(stackTrace[i].getMethodName())) {
+ return true;
+ }
+ if ("getServerAuthentication".equals(stackTrace[i].getMethodName())) {
+ return false;
+ }
+ }
+
+ // fallback to the Ivy 2.2.0 behavior
+ String proxyHost = System.getProperty("http.proxyHost");
+ return getRequestingHost().equals(proxyHost);
+ }
+
+}
diff --git a/src/java/org/apache/ivy/util/url/URLHandler.java b/src/java/org/apache/ivy/util/url/URLHandler.java
new file mode 100644
index 0000000..96b51d9
--- /dev/null
+++ b/src/java/org/apache/ivy/util/url/URLHandler.java
@@ -0,0 +1,173 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.util.url;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+
+import org.apache.ivy.util.CopyProgressListener;
+
+/**
+ * This interface is responsible for handling some URL manipulation (stream opening, downloading,
+ * check reachability, ...).
+ */
+public interface URLHandler {
+
+ /**
+ * Using the slower REQUEST method for getting the basic URL infos. Use this when getting errors
+ * behind a problematic/special proxy or firewall chain.
+ */
+ public static final int REQUEST_METHOD_GET = 1;
+
+ /**
+ * Using the faster HEAD method for getting the basic URL infos. Works for most common networks.
+ */
+ public static final int REQUEST_METHOD_HEAD = 2;
+
+ public static class URLInfo {
+ private long contentLength;
+
+ private long lastModified;
+
+ private boolean available;
+
+ private String bodyCharset;
+
+ protected URLInfo(boolean available, long contentLength, long lastModified) {
+ this(available, contentLength, lastModified, null);
+ }
+
+ protected URLInfo(boolean available, long contentLength, long lastModified,
+ String bodyCharset) {
+ this.available = available;
+ this.contentLength = contentLength;
+ this.lastModified = lastModified;
+ this.bodyCharset = bodyCharset;
+ }
+
+ public boolean isReachable() {
+ return available;
+ }
+
+ public long getContentLength() {
+ return contentLength;
+ }
+
+ public long getLastModified() {
+ return lastModified;
+ }
+
+ public String getBodyCharset() {
+ return bodyCharset;
+ }
+ }
+
+ public static final URLInfo UNAVAILABLE = new URLInfo(false, 0, 0);
+
+ /**
+ * Please prefer getURLInfo when several infos are needed.
+ *
+ * @param url
+ * the url to check
+ * @return true if the target is reachable
+ */
+ public boolean isReachable(URL url);
+
+ /**
+ * Please prefer getURLInfo when several infos are needed.
+ *
+ * @param url
+ * the url to check
+ * @return true if the target is reachable
+ */
+ public boolean isReachable(URL url, int timeout);
+
+ /**
+ * Returns the length of the target if the given url is reachable, and without error code in
+ * case of http urls. Please prefer getURLInfo when several infos are needed.
+ *
+ * @param url
+ * the url to check
+ * @return the length of the target if available, 0 if not reachable
+ */
+ public long getContentLength(URL url);
+
+ /**
+ * Returns the length of the target if the given url is reachable, and without error code in
+ * case of http urls.
+ *
+ * @param url
+ * the url to check
+ * @param timeout
+ * the maximum time before considering an url is not reachable a timeout of zero
+ * indicates no timeout
+ * @return the length of the target if available, 0 if not reachable
+ */
+ public long getContentLength(URL url, int timeout);
+
+ /**
+ * Please prefer getURLInfo when several infos are needed.
+ *
+ * @param url
+ * the url to check
+ * @return last modified timestamp of the given url
+ */
+ public long getLastModified(URL url);
+
+ /**
+ * Please prefer getURLInfo when several infos are needed.
+ *
+ * @param url
+ * the url to check
+ * @return last modified timestamp of the given url
+ */
+ public long getLastModified(URL url, int timeout);
+
+ /**
+ * Returns the URLInfo of the given url or a {@link #UNAVAILABLE} instance, if the url is not
+ * reachable.
+ *
+ * @param url
+ * The url from which information is retrieved.
+ * @return The URLInfo extracted from the given url, or {@link #UNAVAILABLE} when the url is not
+ * available.
+ */
+ public URLInfo getURLInfo(URL url);
+
+ /**
+ * never returns null, return UNAVAILABLE when url is not reachable
+ *
+ * @param url
+ * The url from which information is retrieved.
+ * @param timeout
+ * The timeout in milliseconds.
+ * @return The URLInfo extracted from the given url, or {@link #UNAVAILABLE} when the url is not
+ * available.
+ */
+ public URLInfo getURLInfo(URL url, int timeout);
+
+ public InputStream openStream(URL url) throws IOException;
+
+ public void download(URL src, File dest, CopyProgressListener l) throws IOException;
+
+ public void upload(File src, URL dest, CopyProgressListener l) throws IOException;
+
+ public void setRequestMethod(int requestMethod);
+}
diff --git a/src/java/org/apache/ivy/util/url/URLHandlerDispatcher.java b/src/java/org/apache/ivy/util/url/URLHandlerDispatcher.java
new file mode 100644
index 0000000..be15009
--- /dev/null
+++ b/src/java/org/apache/ivy/util/url/URLHandlerDispatcher.java
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.util.url;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.ivy.util.CopyProgressListener;
+
+/**
+ * This class is used to dispatch downloading requests
+ */
+public class URLHandlerDispatcher implements URLHandler {
+ private Map handlers = new HashMap();
+
+ private URLHandler defaultHandler = new BasicURLHandler();
+
+ public URLHandlerDispatcher() {
+ }
+
+ public boolean isReachable(URL url) {
+ return getHandler(url.getProtocol()).isReachable(url);
+ }
+
+ public boolean isReachable(URL url, int timeout) {
+ return getHandler(url.getProtocol()).isReachable(url, timeout);
+ }
+
+ public long getContentLength(URL url) {
+ return getHandler(url.getProtocol()).getContentLength(url);
+ }
+
+ public long getContentLength(URL url, int timeout) {
+ return getHandler(url.getProtocol()).getContentLength(url, timeout);
+ }
+
+ public long getLastModified(URL url) {
+ return getHandler(url.getProtocol()).getLastModified(url);
+ }
+
+ public long getLastModified(URL url, int timeout) {
+ return getHandler(url.getProtocol()).getLastModified(url, timeout);
+ }
+
+ public URLInfo getURLInfo(URL url) {
+ return getHandler(url.getProtocol()).getURLInfo(url);
+ }
+
+ public URLInfo getURLInfo(URL url, int timeout) {
+ return getHandler(url.getProtocol()).getURLInfo(url, timeout);
+ }
+
+ public InputStream openStream(URL url) throws IOException {
+ return getHandler(url.getProtocol()).openStream(url);
+ }
+
+ public void download(URL src, File dest, CopyProgressListener l) throws IOException {
+ getHandler(src.getProtocol()).download(src, dest, l);
+ }
+
+ public void upload(File src, URL dest, CopyProgressListener l) throws IOException {
+ getHandler(dest.getProtocol()).upload(src, dest, l);
+ }
+
+ public void setRequestMethod(int requestMethod) {
+ defaultHandler.setRequestMethod(requestMethod);
+ for (Iterator it = handlers.values().iterator(); it.hasNext();) {
+ URLHandler handler = (URLHandler) it.next();
+ handler.setRequestMethod(requestMethod);
+ }
+ }
+
+ public void setDownloader(String protocol, URLHandler downloader) {
+ handlers.put(protocol, downloader);
+ }
+
+ public URLHandler getHandler(String protocol) {
+ URLHandler downloader = (URLHandler) handlers.get(protocol);
+ return downloader == null ? defaultHandler : downloader;
+ }
+
+ public URLHandler getDefault() {
+ return defaultHandler;
+ }
+
+ public void setDefault(URLHandler default1) {
+ defaultHandler = default1;
+ }
+}
diff --git a/src/java/org/apache/ivy/util/url/URLHandlerRegistry.java b/src/java/org/apache/ivy/util/url/URLHandlerRegistry.java
new file mode 100644
index 0000000..21f10f1
--- /dev/null
+++ b/src/java/org/apache/ivy/util/url/URLHandlerRegistry.java
@@ -0,0 +1,73 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.ivy.util.url;
+
+import org.apache.ivy.util.Message;
+
+/**
+ *
+ */
+public final class URLHandlerRegistry {
+ private URLHandlerRegistry() {
+ }
+
+ private static URLHandler defaultHandler = new BasicURLHandler();
+
+ public static URLHandler getDefault() {
+ return defaultHandler;
+ }
+
+ public static void setDefault(URLHandler def) {
+ defaultHandler = def;
+ }
+
+ /**
+ * This method is used to get appropriate http downloader dependening on Jakarta Commons
+ * HttpClient availability in classpath, or simply use jdk url handling in other cases.
+ *
+ * @return most accurate http downloader
+ */
+ public static URLHandler getHttp() {
+ try {
+ Class.forName("org.apache.commons.httpclient.HttpClient");
+
+ // temporary fix for IVY-880: only use HttpClientHandler when
+ // http-client-3.x is available
+ Class.forName("org.apache.commons.httpclient.params.HttpClientParams");
+
+ Class handler = Class.forName("org.apache.ivy.util.url.HttpClientHandler");
+ Message.verbose("jakarta commons httpclient detected: using it for http downloading");
+ return (URLHandler) handler.newInstance();
+ } catch (ClassNotFoundException e) {
+ Message.verbose("jakarta commons httpclient not found: using jdk url handling");
+ return new BasicURLHandler();
+ } catch (NoClassDefFoundError e) {
+ Message.verbose("error occurred while loading jakarta commons httpclient: "
+ + e.getMessage());
+ Message.verbose("Using jdk url handling instead.");
+ return new BasicURLHandler();
+ } catch (InstantiationException e) {
+ Message.verbose("couldn't instantiate HttpClientHandler: using jdk url handling");
+ return new BasicURLHandler();
+ } catch (IllegalAccessException e) {
+ Message.verbose("couldn't instantiate HttpClientHandler: using jdk url handling");
+ return new BasicURLHandler();
+ }
+ }
+
+}
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-java/ivy.git
More information about the pkg-java-commits
mailing list