View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.eclipse.aether.internal.impl.scope;
20  
21  import java.util.Collection;
22  import java.util.Collections;
23  import java.util.Comparator;
24  import java.util.HashSet;
25  import java.util.List;
26  import java.util.Set;
27  import java.util.stream.Collectors;
28  
29  import org.eclipse.aether.impl.scope.InternalScopeManager;
30  import org.eclipse.aether.scope.DependencyScope;
31  import org.eclipse.aether.scope.ScopeManager;
32  import org.eclipse.aether.util.graph.transformer.ConflictResolver;
33  import org.eclipse.aether.util.graph.transformer.ConflictResolver.ConflictContext;
34  import org.eclipse.aether.util.graph.transformer.ConflictResolver.ConflictItem;
35  import org.eclipse.aether.util.graph.transformer.ConflictResolver.ScopeSelector;
36  
37  import static java.util.Objects.requireNonNull;
38  
39  /**
40   * A scope selector for use with {@link ConflictResolver} that supports the scopes from {@link ScopeManager}.
41   * In general, this selector picks the widest scope present among conflicting dependencies where e.g. "compile" is
42   * wider than "runtime" which is wider than "test". If however a direct dependency is involved, its scope is selected.
43   * <p>
44   * This class also "bridges" between {@link DependencyScope} and Resolver that uses plain string labels for scopes.
45   */
46  public final class ManagedScopeSelector extends ScopeSelector {
47      private final DependencyScope systemScope;
48      private final List<DependencyScope> dependencyScopesByWidthDescending;
49  
50      public ManagedScopeSelector(InternalScopeManager scopeManager) {
51          requireNonNull(scopeManager, "scopeManager");
52          this.systemScope = scopeManager.getSystemDependencyScope().orElse(null);
53          this.dependencyScopesByWidthDescending =
54                  Collections.unmodifiableList(scopeManager.getDependencyScopeUniverse().stream()
55                          .sorted(Comparator.comparing(scopeManager::getDependencyScopeWidth)
56                                  .reversed())
57                          .collect(Collectors.toList()));
58      }
59  
60      @Override
61      public void selectScope(ConflictContext context) {
62          String scope = context.getWinner().getDependency().getScope();
63          if (systemScope == null || !systemScope.getId().equals(scope)) {
64              scope = chooseEffectiveScope(context.getItems());
65          }
66          context.setScope(scope);
67      }
68  
69      private String chooseEffectiveScope(Collection<ConflictItem> items) {
70          Set<String> scopes = new HashSet<>();
71          for (ConflictItem item : items) {
72              if (item.getDepth() <= 1) {
73                  return item.getDependency().getScope();
74              }
75              scopes.addAll(item.getScopes());
76          }
77          return chooseEffectiveScope(scopes);
78      }
79  
80      /**
81       * Visible for testing. It chooses "widest" scope out of provided ones, unless system scope is present, in which
82       * case system scope is selected.
83       */
84      public String chooseEffectiveScope(Set<String> scopes) {
85          if (scopes.size() > 1 && systemScope != null) {
86              scopes.remove(systemScope.getId());
87          }
88  
89          String effectiveScope = "";
90  
91          if (scopes.size() == 1) {
92              effectiveScope = scopes.iterator().next();
93          } else {
94              for (DependencyScope dependencyScope : dependencyScopesByWidthDescending) {
95                  if (scopes.contains(dependencyScope.getId())) {
96                      effectiveScope = dependencyScope.getId();
97                      break;
98                  }
99              }
100         }
101 
102         return effectiveScope;
103     }
104 }