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