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.Objects;
23  
24  import org.eclipse.aether.collection.DependencyCollectionContext;
25  import org.eclipse.aether.collection.DependencySelector;
26  import org.eclipse.aether.graph.Dependency;
27  
28  import static java.util.Objects.requireNonNull;
29  
30  /**
31   * A dependency selector that filters transitive dependencies based on their scope. It is configurable from which level
32   * applies, as it depend on "as project" and "as dependency" use cases.
33   * <p>
34   * <em>Important note:</em> equals/hashCode must factor in starting state, as instances of this class
35   * (potentially differentially configured) are used now in session, but are kept in a set.
36   * <p>
37   * <em>Note:</em> This filter does not assume any relationships between the scopes.
38   * In particular, the filter is not aware of scopes that logically include other scopes.
39   *
40   * @see Dependency#getScope()
41   */
42  public final class ScopeDependencySelector implements DependencySelector {
43      /**
44       * This enables "legacy" mode (proper): in Resolver 1.x "transitive" state depended on the presence or
45       * absence of the root node.
46       */
47      public static ScopeDependencySelector legacy(Collection<String> included, Collection<String> excluded) {
48          return new ScopeDependencySelector(
49                  Objects.hash(true, 0, 1, Integer.MAX_VALUE, included, excluded),
50                  true,
51                  0,
52                  1,
53                  Integer.MAX_VALUE,
54                  included,
55                  excluded);
56      }
57  
58      /**
59       * Selects dependencies by scope always (from root).
60       */
61      public static ScopeDependencySelector fromRoot(Collection<String> included, Collection<String> excluded) {
62          return from(1, included, excluded);
63      }
64  
65      /**
66       * Selects dependencies by scope starting from direct dependencies.
67       */
68      public static ScopeDependencySelector fromDirect(Collection<String> included, Collection<String> excluded) {
69          return from(2, included, excluded);
70      }
71  
72      /**
73       * Selects dependencies by scope starting from given depth (1=root, 2=direct, 3=transitives of direct ones...).
74       */
75      public static ScopeDependencySelector from(
76              int applyFrom, Collection<String> included, Collection<String> excluded) {
77          return fromTo(applyFrom, Integer.MAX_VALUE, included, excluded);
78      }
79  
80      /**
81       * Selects dependencies by scope starting from given depth (1=root, 2=direct, 3=transitives of direct ones...) to
82       * given depth.
83       */
84      public static ScopeDependencySelector fromTo(
85              int applyFrom, int applyTo, Collection<String> included, Collection<String> excluded) {
86          if (applyFrom < 1) {
87              throw new IllegalArgumentException("applyFrom must be non-zero and positive");
88          }
89          if (applyFrom > applyTo) {
90              throw new IllegalArgumentException("applyTo must be greater or equal than applyFrom");
91          }
92          return new ScopeDependencySelector(
93                  Objects.hash(false, 0, applyFrom, applyTo, included, excluded),
94                  false,
95                  0,
96                  applyFrom,
97                  applyTo,
98                  included,
99                  excluded);
100     }
101 
102     private final int seed;
103     private final boolean shiftIfRootNull;
104     private final int depth;
105     private final int applyFrom;
106     private final int applyTo;
107     private final Collection<String> included;
108     private final Collection<String> excluded;
109 
110     private ScopeDependencySelector(
111             int seed,
112             boolean shiftIfRootNull,
113             int depth,
114             int applyFrom,
115             int applyTo,
116             Collection<String> included,
117             Collection<String> excluded) {
118         this.seed = seed;
119         this.shiftIfRootNull = shiftIfRootNull;
120         this.depth = depth;
121         this.applyFrom = applyFrom;
122         this.applyTo = applyTo;
123         this.included = included;
124         this.excluded = excluded;
125     }
126 
127     @Override
128     public boolean selectDependency(Dependency dependency) {
129         requireNonNull(dependency, "dependency cannot be null");
130         if (depth < applyFrom || depth > applyTo) {
131             return true;
132         }
133 
134         String scope = dependency.getScope();
135         return (included == null || included.contains(scope)) && (excluded == null || !excluded.contains(scope));
136     }
137 
138     @Override
139     public DependencySelector deriveChildSelector(DependencyCollectionContext context) {
140         requireNonNull(context, "context cannot be null");
141         if (depth == 0 && shiftIfRootNull && context.getDependency() == null) {
142             return new ScopeDependencySelector(
143                     seed, shiftIfRootNull, depth + 1, applyFrom + 1, applyTo, included, excluded);
144         } else {
145             return new ScopeDependencySelector(
146                     seed, shiftIfRootNull, depth + 1, applyFrom, applyTo, included, excluded);
147         }
148     }
149 
150     @Override
151     public boolean equals(Object obj) {
152         if (this == obj) {
153             return true;
154         } else if (null == obj || !getClass().equals(obj.getClass())) {
155             return false;
156         }
157 
158         ScopeDependencySelector that = (ScopeDependencySelector) obj;
159         return seed == that.seed
160                 && shiftIfRootNull == that.shiftIfRootNull
161                 && depth == that.depth
162                 && applyFrom == that.applyFrom
163                 && applyTo == that.applyTo
164                 && Objects.equals(included, that.included)
165                 && Objects.equals(excluded, that.excluded);
166     }
167 
168     @Override
169     public int hashCode() {
170         int hash = 17;
171         hash = hash * 31 + seed;
172         hash = hash * 31 + (shiftIfRootNull ? 0 : 1);
173         hash = hash * 31 + depth;
174         hash = hash * 31 + applyFrom;
175         hash = hash * 31 + applyTo;
176         hash = hash * 31 + (included != null ? included.hashCode() : 0);
177         hash = hash * 31 + (excluded != null ? excluded.hashCode() : 0);
178         return hash;
179     }
180 
181     @Override
182     public String toString() {
183         return String.format(
184                 "%s(included: %s, excluded: %s, applied: %s)",
185                 getClass().getSimpleName(), included, excluded, depth >= applyFrom);
186     }
187 }