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.util.graph.selector;
20  
21  import java.util.ArrayList;
22  import java.util.Arrays;
23  import java.util.Collection;
24  import java.util.HashSet;
25  import java.util.Objects;
26  import java.util.TreeSet;
27  
28  import org.eclipse.aether.collection.DependencyCollectionContext;
29  import org.eclipse.aether.collection.DependencySelector;
30  import org.eclipse.aether.graph.Dependency;
31  
32  import static java.util.Objects.requireNonNull;
33  
34  /**
35   * A dependency selector that filters transitive dependencies based on their scope. Direct dependencies are always
36   * included regardless of their scope. <em>Note:</em> This filter does not assume any relationships between the scopes.
37   * In particular, the filter is not aware of scopes that logically include other scopes.
38   *
39   * @see Dependency#getScope()
40   */
41  public final class ScopeDependencySelector implements DependencySelector {
42  
43      private final boolean transitive;
44  
45      private final Collection<String> included;
46  
47      private final Collection<String> excluded;
48  
49      /**
50       * Creates a new selector using the specified includes and excludes.
51       *
52       * @param included The set of scopes to include, may be {@code null} or empty to include any scope.
53       * @param excluded The set of scopes to exclude, may be {@code null} or empty to exclude no scope.
54       */
55      public ScopeDependencySelector(Collection<String> included, Collection<String> excluded) {
56          transitive = false;
57          this.included = clone(included);
58          this.excluded = clone(excluded);
59      }
60  
61      private static Collection<String> clone(Collection<String> scopes) {
62          Collection<String> copy;
63          if (scopes == null || scopes.isEmpty()) {
64              // checking for null is faster than isEmpty()
65              copy = null;
66          } else {
67              copy = new HashSet<>(scopes);
68              if (copy.size() <= 2) {
69                  // contains() is faster for smallish array (sorted for equals()!)
70                  copy = new ArrayList<>(new TreeSet<>(copy));
71              }
72          }
73          return copy;
74      }
75  
76      /**
77       * Creates a new selector using the specified excludes.
78       *
79       * @param excluded The set of scopes to exclude, may be {@code null} or empty to exclude no scope.
80       */
81      public ScopeDependencySelector(String... excluded) {
82          this(null, (excluded != null) ? Arrays.asList(excluded) : null);
83      }
84  
85      private ScopeDependencySelector(boolean transitive, Collection<String> included, Collection<String> excluded) {
86          this.transitive = transitive;
87          this.included = included;
88          this.excluded = excluded;
89      }
90  
91      public boolean selectDependency(Dependency dependency) {
92          requireNonNull(dependency, "dependency cannot be null");
93          if (!transitive) {
94              return true;
95          }
96  
97          String scope = dependency.getScope();
98          return (included == null || included.contains(scope)) && (excluded == null || !excluded.contains(scope));
99      }
100 
101     public DependencySelector deriveChildSelector(DependencyCollectionContext context) {
102         requireNonNull(context, "context cannot be null");
103         if (this.transitive || context.getDependency() == null) {
104             return this;
105         }
106 
107         return new ScopeDependencySelector(true, included, excluded);
108     }
109 
110     @Override
111     public boolean equals(Object obj) {
112         if (this == obj) {
113             return true;
114         } else if (null == obj || !getClass().equals(obj.getClass())) {
115             return false;
116         }
117 
118         ScopeDependencySelector that = (ScopeDependencySelector) obj;
119         return transitive == that.transitive
120                 && Objects.equals(included, that.included)
121                 && Objects.equals(excluded, that.excluded);
122     }
123 
124     @Override
125     public int hashCode() {
126         int hash = 17;
127         hash = hash * 31 + (transitive ? 1 : 0);
128         hash = hash * 31 + (included != null ? included.hashCode() : 0);
129         hash = hash * 31 + (excluded != null ? excluded.hashCode() : 0);
130         return hash;
131     }
132 
133     @Override
134     public String toString() {
135         return String.format(
136                 "%s(included: %s, excluded: %s, transitive: %s)",
137                 getClass().getSimpleName(), included, excluded, transitive);
138     }
139 }