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