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