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