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