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.internal.impl.scope; 020 021import java.util.Collection; 022import java.util.Collections; 023import java.util.Comparator; 024import java.util.HashMap; 025import java.util.HashSet; 026import java.util.Map; 027import java.util.Objects; 028import java.util.Optional; 029import java.util.Set; 030import java.util.concurrent.atomic.AtomicReference; 031import java.util.stream.Collectors; 032 033import org.eclipse.aether.RepositorySystemSession; 034import org.eclipse.aether.artifact.Artifact; 035import org.eclipse.aether.collection.CollectResult; 036import org.eclipse.aether.collection.DependencySelector; 037import org.eclipse.aether.impl.scope.BuildPath; 038import org.eclipse.aether.impl.scope.BuildScope; 039import org.eclipse.aether.impl.scope.BuildScopeQuery; 040import org.eclipse.aether.impl.scope.BuildScopeSource; 041import org.eclipse.aether.impl.scope.InternalScopeManager; 042import org.eclipse.aether.impl.scope.ProjectPath; 043import org.eclipse.aether.impl.scope.ScopeManagerConfiguration; 044import org.eclipse.aether.scope.DependencyScope; 045import org.eclipse.aether.scope.ResolutionScope; 046import org.eclipse.aether.scope.SystemDependencyScope; 047import org.eclipse.aether.util.filter.ScopeDependencyFilter; 048import org.eclipse.aether.util.graph.selector.AndDependencySelector; 049import org.eclipse.aether.util.graph.selector.ExclusionDependencySelector; 050import org.eclipse.aether.util.graph.visitor.CloningDependencyVisitor; 051import org.eclipse.aether.util.graph.visitor.FilteringDependencyVisitor; 052 053import static java.util.Objects.requireNonNull; 054 055public final class ScopeManagerImpl implements InternalScopeManager { 056 private final String id; 057 private final boolean strictDependencyScopes; 058 private final boolean strictResolutionScopes; 059 private final BuildScopeSource buildScopeSource; 060 private final AtomicReference<SystemDependencyScopeImpl> systemDependencyScope; 061 private final Map<String, DependencyScopeImpl> dependencyScopes; 062 private final Collection<DependencyScope> dependencyScopesUniverse; 063 private final Map<String, ResolutionScopeImpl> resolutionScopes; 064 private final Collection<ResolutionScope> resolutionScopesUniverse; 065 066 public ScopeManagerImpl(ScopeManagerConfiguration configuration) { 067 this.id = configuration.getId(); 068 this.strictDependencyScopes = configuration.isStrictDependencyScopes(); 069 this.strictResolutionScopes = configuration.isStrictResolutionScopes(); 070 this.buildScopeSource = configuration.getBuildScopeSource(); 071 this.systemDependencyScope = new AtomicReference<>(null); 072 this.dependencyScopes = Collections.unmodifiableMap(buildDependencyScopes(configuration)); 073 this.dependencyScopesUniverse = Collections.unmodifiableCollection(new HashSet<>(dependencyScopes.values())); 074 this.resolutionScopes = Collections.unmodifiableMap(buildResolutionScopes(configuration)); 075 this.resolutionScopesUniverse = Collections.unmodifiableCollection(new HashSet<>(resolutionScopes.values())); 076 } 077 078 private Map<String, DependencyScopeImpl> buildDependencyScopes(ScopeManagerConfiguration configuration) { 079 Collection<DependencyScope> dependencyScopes = configuration.buildDependencyScopes(this); 080 HashMap<String, DependencyScopeImpl> result = new HashMap<>(dependencyScopes.size()); 081 dependencyScopes.forEach(d -> result.put(d.getId(), (DependencyScopeImpl) d)); 082 return result; 083 } 084 085 private Map<String, ResolutionScopeImpl> buildResolutionScopes(ScopeManagerConfiguration configuration) { 086 Collection<ResolutionScope> resolutionScopes = configuration.buildResolutionScopes(this); 087 HashMap<String, ResolutionScopeImpl> result = new HashMap<>(resolutionScopes.size()); 088 resolutionScopes.forEach(r -> result.put(r.getId(), (ResolutionScopeImpl) r)); 089 return result; 090 } 091 092 @Override 093 public String getId() { 094 return id; 095 } 096 097 @Override 098 public Optional<SystemDependencyScope> getSystemDependencyScope() { 099 return Optional.ofNullable(systemDependencyScope.get()); 100 } 101 102 @Override 103 public Optional<DependencyScope> getDependencyScope(String id) { 104 DependencyScope dependencyScope = dependencyScopes.get(id); 105 if (strictDependencyScopes && dependencyScope == null) { 106 throw new IllegalArgumentException("unknown dependency scope"); 107 } 108 return Optional.ofNullable(dependencyScope); 109 } 110 111 @Override 112 public Collection<DependencyScope> getDependencyScopeUniverse() { 113 return dependencyScopesUniverse; 114 } 115 116 @Override 117 public Optional<ResolutionScope> getResolutionScope(String id) { 118 ResolutionScope resolutionScope = resolutionScopes.get(id); 119 if (strictResolutionScopes && resolutionScope == null) { 120 throw new IllegalArgumentException("unknown resolution scope"); 121 } 122 return Optional.ofNullable(resolutionScope); 123 } 124 125 @Override 126 public Collection<ResolutionScope> getResolutionScopeUniverse() { 127 return resolutionScopesUniverse; 128 } 129 130 @Override 131 public int getDependencyScopeWidth(DependencyScope dependencyScope) { 132 return translate(dependencyScope).getWidth(); 133 } 134 135 @Override 136 public Optional<BuildScope> getDependencyScopeMainProjectBuildScope(DependencyScope dependencyScope) { 137 return Optional.ofNullable(translate(dependencyScope).getMainBuildScope()); 138 } 139 140 @Override 141 public DependencySelector getDependencySelector(RepositorySystemSession session, ResolutionScope resolutionScope) { 142 ResolutionScopeImpl rs = translate(resolutionScope); 143 Set<String> directlyExcludedLabels = getDirectlyExcludedLabels(rs); 144 Set<String> transitivelyExcludedLabels = getTransitivelyExcludedLabels(rs); 145 if (session.getDependencySelector() != null) { 146 return new AndDependencySelector( 147 rs.getMode() == Mode.ELIMINATE 148 ? ScopeDependencySelector.fromTo(2, 2, null, directlyExcludedLabels) 149 : ScopeDependencySelector.fromTo(1, 2, null, directlyExcludedLabels), 150 ScopeDependencySelector.from(2, null, transitivelyExcludedLabels), 151 OptionalDependencySelector.fromDirect(), 152 new ExclusionDependencySelector(), 153 session.getDependencySelector()); 154 } else { 155 return new AndDependencySelector( 156 rs.getMode() == Mode.ELIMINATE 157 ? ScopeDependencySelector.fromTo(2, 2, null, directlyExcludedLabels) 158 : ScopeDependencySelector.fromTo(1, 2, null, directlyExcludedLabels), 159 ScopeDependencySelector.from(2, null, transitivelyExcludedLabels), 160 OptionalDependencySelector.fromDirect(), 161 new ExclusionDependencySelector()); 162 } 163 } 164 165 @Override 166 public CollectResult postProcess( 167 RepositorySystemSession session, ResolutionScope resolutionScope, CollectResult collectResult) { 168 ResolutionScopeImpl rs = translate(resolutionScope); 169 if (rs.getMode() == Mode.ELIMINATE) { 170 CloningDependencyVisitor cloning = new CloningDependencyVisitor(); 171 FilteringDependencyVisitor filter = new FilteringDependencyVisitor( 172 cloning, new ScopeDependencyFilter(null, getDirectlyExcludedLabels(rs))); 173 collectResult.getRoot().accept(filter); 174 collectResult.setRoot(cloning.getRootNode()); 175 } 176 return collectResult; 177 } 178 179 @Override 180 public DependencyScope createDependencyScope(String id, boolean transitive, Collection<BuildScopeQuery> presence) { 181 return new DependencyScopeImpl(id, transitive, presence); 182 } 183 184 @Override 185 public SystemDependencyScope createSystemDependencyScope( 186 String id, boolean transitive, Collection<BuildScopeQuery> presence, String systemPathProperty) { 187 SystemDependencyScopeImpl system = new SystemDependencyScopeImpl(id, transitive, presence, systemPathProperty); 188 if (systemDependencyScope.compareAndSet(null, system)) { 189 return system; 190 } else { 191 throw new IllegalStateException("system dependency scope already created"); 192 } 193 } 194 195 @Override 196 public ResolutionScope createResolutionScope( 197 String id, 198 Mode mode, 199 Collection<BuildScopeQuery> wantedPresence, 200 Collection<DependencyScope> explicitlyIncluded, 201 Collection<DependencyScope> transitivelyExcluded) { 202 return new ResolutionScopeImpl(id, mode, wantedPresence, explicitlyIncluded, transitivelyExcluded); 203 } 204 205 private Set<DependencyScope> collectScopes(Collection<BuildScopeQuery> wantedPresence) { 206 HashSet<DependencyScope> result = new HashSet<>(); 207 for (BuildScope buildScope : buildScopeSource.query(wantedPresence)) { 208 dependencyScopes.values().stream() 209 .filter(s -> buildScopeSource.query(s.getPresence()).contains(buildScope)) 210 .filter(s -> systemDependencyScope.get() == null 211 || !systemDependencyScope.get().is(s.id)) // system scope must be always explicitly added 212 .forEach(result::add); 213 } 214 return result; 215 } 216 217 private int calculateDependencyScopeWidth(DependencyScopeImpl dependencyScope) { 218 int result = 0; 219 if (dependencyScope.isTransitive()) { 220 result += 1000; 221 } 222 for (BuildScope buildScope : buildScopeSource.query(dependencyScope.getPresence())) { 223 result += 1000 224 / buildScope.getProjectPaths().stream() 225 .map(ProjectPath::order) 226 .reduce(0, Integer::sum); 227 } 228 return result; 229 } 230 231 private BuildScope calculateMainProjectBuildScope(DependencyScopeImpl dependencyScope) { 232 for (ProjectPath projectPath : buildScopeSource.allProjectPaths().stream() 233 .sorted(Comparator.comparing(ProjectPath::order)) 234 .collect(Collectors.toList())) { 235 for (BuildPath buildPath : buildScopeSource.allBuildPaths().stream() 236 .sorted(Comparator.comparing(BuildPath::order)) 237 .collect(Collectors.toList())) { 238 for (BuildScope buildScope : buildScopeSource.query(dependencyScope.getPresence())) { 239 if (buildScope.getProjectPaths().contains(projectPath) 240 && buildScope.getBuildPaths().contains(buildPath)) { 241 return buildScope; 242 } 243 } 244 } 245 } 246 return null; 247 } 248 249 /** 250 * Visible for testing. 251 */ 252 Set<String> getDirectlyIncludedLabels(ResolutionScope resolutionScope) { 253 return translate(resolutionScope).getDirectlyIncluded().stream() 254 .map(DependencyScope::getId) 255 .collect(Collectors.toSet()); 256 } 257 258 /** 259 * Visible for testing. 260 */ 261 Set<String> getDirectlyExcludedLabels(ResolutionScope resolutionScope) { 262 ResolutionScopeImpl rs = translate(resolutionScope); 263 return dependencyScopes.values().stream() 264 .filter(s -> !rs.getDirectlyIncluded().contains(s)) 265 .map(DependencyScope::getId) 266 .collect(Collectors.toSet()); 267 } 268 269 /** 270 * Visible for testing. 271 */ 272 Set<String> getTransitivelyExcludedLabels(ResolutionScope resolutionScope) { 273 return translate(resolutionScope).getTransitivelyExcluded().stream() 274 .map(DependencyScope::getId) 275 .collect(Collectors.toSet()); 276 } 277 278 /** 279 * Visible for testing. 280 */ 281 Set<BuildScopeQuery> getPresence(DependencyScope dependencyScope) { 282 return translate(dependencyScope).getPresence(); 283 } 284 285 /** 286 * Visible for testing. 287 */ 288 BuildScopeSource getBuildScopeSource() { 289 return buildScopeSource; 290 } 291 292 private DependencyScopeImpl translate(DependencyScope dependencyScope) { 293 return requireNonNull(dependencyScopes.get(dependencyScope.getId()), "unknown dependency scope"); 294 } 295 296 private ResolutionScopeImpl translate(ResolutionScope resolutionScope) { 297 return requireNonNull(resolutionScopes.get(resolutionScope.getId()), "unknown resolution scope"); 298 } 299 300 @Override 301 public boolean equals(Object o) { 302 if (this == o) { 303 return true; 304 } 305 if (o == null || getClass() != o.getClass()) { 306 return false; 307 } 308 ScopeManagerImpl that = (ScopeManagerImpl) o; 309 return Objects.equals(id, that.id); 310 } 311 312 @Override 313 public int hashCode() { 314 return Objects.hash(id); 315 } 316 317 @Override 318 public String toString() { 319 return id; 320 } 321 322 private class DependencyScopeImpl implements DependencyScope { 323 private final String id; 324 private final boolean transitive; 325 private final Set<BuildScopeQuery> presence; 326 private final BuildScope mainBuildScope; 327 private final int width; 328 329 private DependencyScopeImpl(String id, boolean transitive, Collection<BuildScopeQuery> presence) { 330 this.id = requireNonNull(id, "id"); 331 this.transitive = transitive; 332 this.presence = Collections.unmodifiableSet(new HashSet<>(presence)); 333 this.mainBuildScope = calculateMainProjectBuildScope(this); 334 this.width = calculateDependencyScopeWidth(this); 335 } 336 337 @Override 338 public String getId() { 339 return id; 340 } 341 342 @Override 343 public boolean isTransitive() { 344 return transitive; 345 } 346 347 public Set<BuildScopeQuery> getPresence() { 348 return presence; 349 } 350 351 public BuildScope getMainBuildScope() { 352 return mainBuildScope; 353 } 354 355 public int getWidth() { 356 return width; 357 } 358 359 @Override 360 public boolean equals(Object o) { 361 if (this == o) { 362 return true; 363 } 364 if (o == null || getClass() != o.getClass()) { 365 return false; 366 } 367 DependencyScopeImpl that = (DependencyScopeImpl) o; 368 return Objects.equals(id, that.id); 369 } 370 371 @Override 372 public int hashCode() { 373 return Objects.hash(id); 374 } 375 376 @Override 377 public String toString() { 378 return id; 379 } 380 } 381 382 private class SystemDependencyScopeImpl extends DependencyScopeImpl implements SystemDependencyScope { 383 private final String systemPathProperty; 384 385 private SystemDependencyScopeImpl( 386 String id, boolean transitive, Collection<BuildScopeQuery> presence, String systemPathProperty) { 387 super(id, transitive, presence); 388 this.systemPathProperty = requireNonNull(systemPathProperty); 389 } 390 391 @Override 392 public String getSystemPath(Artifact artifact) { 393 return artifact.getProperty(systemPathProperty, null); 394 } 395 396 @Override 397 public void setSystemPath(Map<String, String> properties, String systemPath) { 398 if (systemPath == null) { 399 properties.remove(systemPathProperty); 400 } else { 401 properties.put(systemPathProperty, systemPath); 402 } 403 } 404 } 405 406 private class ResolutionScopeImpl implements ResolutionScope { 407 private final String id; 408 private final Mode mode; 409 private final Set<BuildScopeQuery> wantedPresence; 410 private final Set<DependencyScope> directlyIncluded; 411 private final Set<DependencyScope> transitivelyExcluded; 412 413 private ResolutionScopeImpl( 414 String id, 415 Mode mode, 416 Collection<BuildScopeQuery> wantedPresence, 417 Collection<DependencyScope> explicitlyIncluded, 418 Collection<DependencyScope> transitivelyExcluded) { 419 this.id = requireNonNull(id, "id"); 420 this.mode = requireNonNull(mode, "mode"); 421 this.wantedPresence = Collections.unmodifiableSet(new HashSet<>(wantedPresence)); 422 Set<DependencyScope> included = collectScopes(wantedPresence); 423 // here we may have null elements, based on existence of system scope 424 if (explicitlyIncluded != null && !explicitlyIncluded.isEmpty()) { 425 explicitlyIncluded.stream().filter(Objects::nonNull).forEach(included::add); 426 } 427 this.directlyIncluded = Collections.unmodifiableSet(included); 428 this.transitivelyExcluded = Collections.unmodifiableSet( 429 transitivelyExcluded.stream().filter(Objects::nonNull).collect(Collectors.toSet())); 430 } 431 432 @Override 433 public String getId() { 434 return id; 435 } 436 437 public Mode getMode() { 438 return mode; 439 } 440 441 public Set<BuildScopeQuery> getWantedPresence() { 442 return wantedPresence; 443 } 444 445 public Set<DependencyScope> getDirectlyIncluded() { 446 return directlyIncluded; 447 } 448 449 public Set<DependencyScope> getTransitivelyExcluded() { 450 return transitivelyExcluded; 451 } 452 453 @Override 454 public boolean equals(Object o) { 455 if (this == o) { 456 return true; 457 } 458 if (o == null || getClass() != o.getClass()) { 459 return false; 460 } 461 ResolutionScopeImpl that = (ResolutionScopeImpl) o; 462 return Objects.equals(id, that.id); 463 } 464 465 @Override 466 public int hashCode() { 467 return Objects.hash(id); 468 } 469 470 @Override 471 public String toString() { 472 return id; 473 } 474 } 475}