001package org.eclipse.aether.util.graph.manager; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import java.util.Collection; 023import java.util.Collections; 024import java.util.HashMap; 025import java.util.LinkedHashSet; 026import java.util.Map; 027import java.util.Objects; 028 029import org.eclipse.aether.artifact.Artifact; 030import org.eclipse.aether.artifact.ArtifactProperties; 031import org.eclipse.aether.collection.DependencyCollectionContext; 032import org.eclipse.aether.collection.DependencyManagement; 033import org.eclipse.aether.collection.DependencyManager; 034import org.eclipse.aether.graph.Dependency; 035import org.eclipse.aether.graph.Exclusion; 036import org.eclipse.aether.util.artifact.JavaScopes; 037 038/** 039 * A dependency manager managing transitive dependencies supporting transitive dependency management. 040 * 041 * @author Christian Schulte 042 * @since 1.4.0 043 */ 044public final class TransitiveDependencyManager 045 implements DependencyManager 046{ 047 048 private final Map<Object, String> managedVersions; 049 050 private final Map<Object, String> managedScopes; 051 052 private final Map<Object, Boolean> managedOptionals; 053 054 private final Map<Object, String> managedLocalPaths; 055 056 private final Map<Object, Collection<Exclusion>> managedExclusions; 057 058 private final int depth; 059 060 private int hashCode; 061 062 /** 063 * Creates a new dependency manager without any management information. 064 */ 065 public TransitiveDependencyManager() 066 { 067 this( 0, Collections.<Object, String>emptyMap(), Collections.<Object, String>emptyMap(), 068 Collections.<Object, Boolean>emptyMap(), Collections.<Object, String>emptyMap(), 069 Collections.<Object, Collection<Exclusion>>emptyMap() ); 070 } 071 072 private TransitiveDependencyManager( final int depth, 073 final Map<Object, String> managedVersions, 074 final Map<Object, String> managedScopes, 075 final Map<Object, Boolean> managedOptionals, 076 final Map<Object, String> managedLocalPaths, 077 final Map<Object, Collection<Exclusion>> managedExclusions ) 078 { 079 super(); 080 this.depth = depth; 081 this.managedVersions = managedVersions; 082 this.managedScopes = managedScopes; 083 this.managedOptionals = managedOptionals; 084 this.managedLocalPaths = managedLocalPaths; 085 this.managedExclusions = managedExclusions; 086 } 087 088 public DependencyManager deriveChildManager( final DependencyCollectionContext context ) 089 { 090 Map<Object, String> versions = managedVersions; 091 Map<Object, String> scopes = managedScopes; 092 Map<Object, Boolean> optionals = managedOptionals; 093 Map<Object, String> localPaths = managedLocalPaths; 094 Map<Object, Collection<Exclusion>> exclusions = managedExclusions; 095 096 for ( Dependency managedDependency : context.getManagedDependencies() ) 097 { 098 Artifact artifact = managedDependency.getArtifact(); 099 Object key = getKey( artifact ); 100 101 String version = artifact.getVersion(); 102 if ( version.length() > 0 && !versions.containsKey( key ) ) 103 { 104 if ( versions == managedVersions ) 105 { 106 versions = new HashMap<>( managedVersions ); 107 } 108 versions.put( key, version ); 109 } 110 111 String scope = managedDependency.getScope(); 112 if ( scope.length() > 0 && !scopes.containsKey( key ) ) 113 { 114 if ( scopes == this.managedScopes ) 115 { 116 scopes = new HashMap<>( this.managedScopes ); 117 } 118 scopes.put( key, scope ); 119 } 120 121 Boolean optional = managedDependency.getOptional(); 122 if ( optional != null && !optionals.containsKey( key ) ) 123 { 124 if ( optionals == managedOptionals ) 125 { 126 optionals = new HashMap<>( managedOptionals ); 127 } 128 optionals.put( key, optional ); 129 } 130 131 String localPath = managedDependency.getArtifact().getProperty( ArtifactProperties.LOCAL_PATH, null ); 132 if ( localPath != null && !localPaths.containsKey( key ) ) 133 { 134 if ( localPaths == this.managedLocalPaths ) 135 { 136 localPaths = new HashMap<>( managedLocalPaths ); 137 } 138 localPaths.put( key, localPath ); 139 } 140 141 if ( !managedDependency.getExclusions().isEmpty() ) 142 { 143 if ( exclusions == managedExclusions ) 144 { 145 exclusions = new HashMap<>( managedExclusions ); 146 } 147 Collection<Exclusion> managed = exclusions.get( key ); 148 if ( managed == null ) 149 { 150 managed = new LinkedHashSet<>(); 151 exclusions.put( key, managed ); 152 } 153 managed.addAll( managedDependency.getExclusions() ); 154 } 155 } 156 157 return new TransitiveDependencyManager( depth + 1, versions, scopes, optionals, localPaths, 158 exclusions ); 159 160 } 161 162 public DependencyManagement manageDependency( Dependency dependency ) 163 { 164 DependencyManagement management = null; 165 166 Object key = getKey( dependency.getArtifact() ); 167 168 if ( depth >= 2 ) 169 { 170 String version = managedVersions.get( key ); 171 if ( version != null ) 172 { 173 management = new DependencyManagement(); 174 management.setVersion( version ); 175 } 176 177 String scope = managedScopes.get( key ); 178 if ( scope != null ) 179 { 180 if ( management == null ) 181 { 182 management = new DependencyManagement(); 183 } 184 management.setScope( scope ); 185 186 if ( !JavaScopes.SYSTEM.equals( scope ) && dependency.getArtifact().getProperty( 187 ArtifactProperties.LOCAL_PATH, null ) != null ) 188 { 189 Map<String, String> properties = new HashMap<>( dependency.getArtifact().getProperties() ); 190 properties.remove( ArtifactProperties.LOCAL_PATH ); 191 management.setProperties( properties ); 192 } 193 } 194 195 if ( ( JavaScopes.SYSTEM.equals( scope ) ) 196 || ( scope == null && JavaScopes.SYSTEM.equals( dependency.getScope() ) ) ) 197 { 198 String localPath = managedLocalPaths.get( key ); 199 if ( localPath != null ) 200 { 201 if ( management == null ) 202 { 203 management = new DependencyManagement(); 204 } 205 Map<String, String> properties = new HashMap<>( dependency.getArtifact().getProperties() ); 206 properties.put( ArtifactProperties.LOCAL_PATH, localPath ); 207 management.setProperties( properties ); 208 } 209 } 210 211 Boolean optional = managedOptionals.get( key ); 212 if ( optional != null ) 213 { 214 if ( management == null ) 215 { 216 management = new DependencyManagement(); 217 } 218 management.setOptional( optional ); 219 } 220 } 221 222 Collection<Exclusion> exclusions = managedExclusions.get( key ); 223 if ( exclusions != null ) 224 { 225 if ( management == null ) 226 { 227 management = new DependencyManagement(); 228 } 229 Collection<Exclusion> result = new LinkedHashSet<>( dependency.getExclusions() ); 230 result.addAll( exclusions ); 231 management.setExclusions( result ); 232 } 233 234 return management; 235 } 236 237 private Object getKey( Artifact a ) 238 { 239 return new Key( a ); 240 } 241 242 @Override 243 public boolean equals( final Object obj ) 244 { 245 boolean equal = obj instanceof TransitiveDependencyManager; 246 247 if ( equal ) 248 { 249 final TransitiveDependencyManager that = (TransitiveDependencyManager) obj; 250 return depth == that.depth 251 && Objects.equals( managedVersions, that.managedVersions ) 252 && Objects.equals( managedScopes, that.managedScopes ) 253 && Objects.equals( managedOptionals, that.managedOptionals ) 254 && Objects.equals( managedExclusions, that.managedExclusions ); 255 } 256 257 return false; 258 } 259 260 @Override 261 public int hashCode() 262 { 263 if ( hashCode == 0 ) 264 { 265 hashCode = Objects.hash( depth, managedVersions, managedScopes, managedOptionals, managedExclusions ); 266 } 267 return hashCode; 268 } 269 270 static class Key 271 { 272 private final Artifact artifact; 273 274 private final int hashCode; 275 276 Key( final Artifact artifact ) 277 { 278 this.artifact = artifact; 279 this.hashCode = Objects.hash( artifact.getGroupId(), artifact.getArtifactId() ); 280 } 281 282 @Override 283 public boolean equals( final Object obj ) 284 { 285 boolean equal = obj instanceof Key; 286 287 if ( equal ) 288 { 289 final Key that = (Key) obj; 290 return Objects.equals( artifact.getArtifactId(), that.artifact.getArtifactId() ) 291 && Objects.equals( artifact.getGroupId(), that.artifact.getGroupId() ) 292 && Objects.equals( artifact.getExtension(), that.artifact.getExtension() ) 293 && Objects.equals( artifact.getClassifier(), that.artifact.getClassifier() ); 294 } 295 296 return equal; 297 } 298 299 @Override 300 public int hashCode() 301 { 302 return this.hashCode; 303 } 304 305 } 306 307}