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 038import static java.util.Objects.requireNonNull; 039 040/** 041 * A dependency manager managing transitive dependencies supporting transitive dependency management. 042 * 043 * @author Christian Schulte 044 * @since 1.4.0 045 */ 046public final class TransitiveDependencyManager 047 implements DependencyManager 048{ 049 050 private final Map<Object, String> managedVersions; 051 052 private final Map<Object, String> managedScopes; 053 054 private final Map<Object, Boolean> managedOptionals; 055 056 private final Map<Object, String> managedLocalPaths; 057 058 private final Map<Object, Collection<Exclusion>> managedExclusions; 059 060 private final int depth; 061 062 private int hashCode; 063 064 /** 065 * Creates a new dependency manager without any management information. 066 */ 067 public TransitiveDependencyManager() 068 { 069 this( 0, Collections.<Object, String>emptyMap(), Collections.<Object, String>emptyMap(), 070 Collections.<Object, Boolean>emptyMap(), Collections.<Object, String>emptyMap(), 071 Collections.<Object, Collection<Exclusion>>emptyMap() ); 072 } 073 074 private TransitiveDependencyManager( final int depth, 075 final Map<Object, String> managedVersions, 076 final Map<Object, String> managedScopes, 077 final Map<Object, Boolean> managedOptionals, 078 final Map<Object, String> managedLocalPaths, 079 final Map<Object, Collection<Exclusion>> managedExclusions ) 080 { 081 super(); 082 this.depth = depth; 083 this.managedVersions = managedVersions; 084 this.managedScopes = managedScopes; 085 this.managedOptionals = managedOptionals; 086 this.managedLocalPaths = managedLocalPaths; 087 this.managedExclusions = managedExclusions; 088 } 089 090 public DependencyManager deriveChildManager( final DependencyCollectionContext context ) 091 { 092 requireNonNull( context, "context cannot be null" ); 093 Map<Object, String> versions = managedVersions; 094 Map<Object, String> scopes = managedScopes; 095 Map<Object, Boolean> optionals = managedOptionals; 096 Map<Object, String> localPaths = managedLocalPaths; 097 Map<Object, Collection<Exclusion>> exclusions = managedExclusions; 098 099 for ( Dependency managedDependency : context.getManagedDependencies() ) 100 { 101 Artifact artifact = managedDependency.getArtifact(); 102 Object key = getKey( artifact ); 103 104 String version = artifact.getVersion(); 105 if ( version.length() > 0 && !versions.containsKey( key ) ) 106 { 107 if ( versions == managedVersions ) 108 { 109 versions = new HashMap<>( managedVersions ); 110 } 111 versions.put( key, version ); 112 } 113 114 String scope = managedDependency.getScope(); 115 if ( scope.length() > 0 && !scopes.containsKey( key ) ) 116 { 117 if ( scopes == this.managedScopes ) 118 { 119 scopes = new HashMap<>( this.managedScopes ); 120 } 121 scopes.put( key, scope ); 122 } 123 124 Boolean optional = managedDependency.getOptional(); 125 if ( optional != null && !optionals.containsKey( key ) ) 126 { 127 if ( optionals == managedOptionals ) 128 { 129 optionals = new HashMap<>( managedOptionals ); 130 } 131 optionals.put( key, optional ); 132 } 133 134 String localPath = managedDependency.getArtifact().getProperty( ArtifactProperties.LOCAL_PATH, null ); 135 if ( localPath != null && !localPaths.containsKey( key ) ) 136 { 137 if ( localPaths == this.managedLocalPaths ) 138 { 139 localPaths = new HashMap<>( managedLocalPaths ); 140 } 141 localPaths.put( key, localPath ); 142 } 143 144 if ( !managedDependency.getExclusions().isEmpty() ) 145 { 146 if ( exclusions == managedExclusions ) 147 { 148 exclusions = new HashMap<>( managedExclusions ); 149 } 150 Collection<Exclusion> managed = exclusions.computeIfAbsent( key, k -> new LinkedHashSet<>() ); 151 managed.addAll( managedDependency.getExclusions() ); 152 } 153 } 154 155 return new TransitiveDependencyManager( depth + 1, versions, scopes, optionals, localPaths, 156 exclusions ); 157 158 } 159 160 public DependencyManagement manageDependency( Dependency dependency ) 161 { 162 requireNonNull( dependency, "dependency cannot be null" ); 163 DependencyManagement management = null; 164 165 Object key = getKey( dependency.getArtifact() ); 166 167 if ( depth >= 2 ) 168 { 169 String version = managedVersions.get( key ); 170 if ( version != null ) 171 { 172 management = new DependencyManagement(); 173 management.setVersion( version ); 174 } 175 176 String scope = managedScopes.get( key ); 177 if ( scope != null ) 178 { 179 if ( management == null ) 180 { 181 management = new DependencyManagement(); 182 } 183 management.setScope( scope ); 184 185 if ( !JavaScopes.SYSTEM.equals( scope ) && dependency.getArtifact().getProperty( 186 ArtifactProperties.LOCAL_PATH, null ) != null ) 187 { 188 Map<String, String> properties = new HashMap<>( dependency.getArtifact().getProperties() ); 189 properties.remove( ArtifactProperties.LOCAL_PATH ); 190 management.setProperties( properties ); 191 } 192 } 193 194 if ( ( JavaScopes.SYSTEM.equals( scope ) ) 195 || ( scope == null && JavaScopes.SYSTEM.equals( dependency.getScope() ) ) ) 196 { 197 String localPath = managedLocalPaths.get( key ); 198 if ( localPath != null ) 199 { 200 if ( management == null ) 201 { 202 management = new DependencyManagement(); 203 } 204 Map<String, String> properties = new HashMap<>( dependency.getArtifact().getProperties() ); 205 properties.put( ArtifactProperties.LOCAL_PATH, localPath ); 206 management.setProperties( properties ); 207 } 208 } 209 210 Boolean optional = managedOptionals.get( key ); 211 if ( optional != null ) 212 { 213 if ( management == null ) 214 { 215 management = new DependencyManagement(); 216 } 217 management.setOptional( optional ); 218 } 219 } 220 221 Collection<Exclusion> exclusions = managedExclusions.get( key ); 222 if ( exclusions != null ) 223 { 224 if ( management == null ) 225 { 226 management = new DependencyManagement(); 227 } 228 Collection<Exclusion> result = new LinkedHashSet<>( dependency.getExclusions() ); 229 result.addAll( exclusions ); 230 management.setExclusions( result ); 231 } 232 233 return management; 234 } 235 236 private Object getKey( Artifact a ) 237 { 238 return new Key( a ); 239 } 240 241 @Override 242 public boolean equals( final Object obj ) 243 { 244 boolean equal = obj instanceof TransitiveDependencyManager; 245 246 if ( equal ) 247 { 248 final TransitiveDependencyManager that = (TransitiveDependencyManager) obj; 249 return depth == that.depth 250 && Objects.equals( managedVersions, that.managedVersions ) 251 && Objects.equals( managedScopes, that.managedScopes ) 252 && Objects.equals( managedOptionals, that.managedOptionals ) 253 && Objects.equals( managedExclusions, that.managedExclusions ); 254 } 255 256 return false; 257 } 258 259 @Override 260 public int hashCode() 261 { 262 if ( hashCode == 0 ) 263 { 264 hashCode = Objects.hash( depth, managedVersions, managedScopes, managedOptionals, managedExclusions ); 265 } 266 return hashCode; 267 } 268 269 static class Key 270 { 271 private final Artifact artifact; 272 273 private final int hashCode; 274 275 Key( final Artifact artifact ) 276 { 277 this.artifact = artifact; 278 this.hashCode = Objects.hash( artifact.getGroupId(), artifact.getArtifactId() ); 279 } 280 281 @Override 282 public boolean equals( final Object obj ) 283 { 284 boolean equal = obj instanceof Key; 285 286 if ( equal ) 287 { 288 final Key that = (Key) obj; 289 return Objects.equals( artifact.getArtifactId(), that.artifact.getArtifactId() ) 290 && Objects.equals( artifact.getGroupId(), that.artifact.getGroupId() ) 291 && Objects.equals( artifact.getExtension(), that.artifact.getExtension() ) 292 && Objects.equals( artifact.getClassifier(), that.artifact.getClassifier() ); 293 } 294 295 return false; 296 } 297 298 @Override 299 public int hashCode() 300 { 301 return this.hashCode; 302 } 303 304 } 305 306}