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.Keys;
25  import org.eclipse.aether.collection.DependencyCollectionContext;
26  import org.eclipse.aether.collection.DependencySelector;
27  import org.eclipse.aether.graph.Dependency;
28  import org.eclipse.aether.util.artifact.ArtifactIdUtils;
29  
30  import static java.util.Objects.requireNonNull;
31  
32  /**
33   * A dependency selector that excludes optional dependencies which occur beyond given level.
34   * <p>
35   * <em>Important note:</em> equals/hashCode must factor in starting state, as instances of this class
36   * (potentially differentially configured) are used now in session, but are kept in a set.
37   *
38   * @see Dependency#isOptional()
39   */
40  public final class OptionalDependencySelector implements DependencySelector {
41      public static final Object IGNORED_KEYS = Keys.of(OptionalDependencySelector.class, "ignored");
42      public static final Object UNSELECTED_KEYS = Keys.of(OptionalDependencySelector.class, "unselected");
43  
44      /**
45       * Excludes optional dependencies always (from root).
46       */
47      public static OptionalDependencySelector fromRoot() {
48          return from(1);
49      }
50  
51      /**
52       * Excludes optional transitive dependencies of direct dependencies.
53       */
54      public static OptionalDependencySelector fromDirect() {
55          return from(2);
56      }
57  
58      /**
59       * Excludes optional transitive dependencies from given depth (1=root, 2=direct, 3=transitives of direct ones...).
60       */
61      public static OptionalDependencySelector from(int applyFrom) {
62          if (applyFrom < 1) {
63              throw new IllegalArgumentException("applyFrom must be non-zero and positive");
64          }
65          return new OptionalDependencySelector(Objects.hash(applyFrom), 0, applyFrom, null, null);
66      }
67  
68      private final int seed;
69      private final int depth;
70      private final int applyFrom;
71      private final Collection<String> ignoredKeys;
72      private final Collection<String> unselectedKeys;
73  
74      private OptionalDependencySelector(
75              int seed, int depth, int applyFrom, Collection<String> ignoredKeys, Collection<String> unselectedKeys) {
76          this.seed = seed;
77          this.depth = depth;
78          this.applyFrom = applyFrom;
79          this.ignoredKeys = ignoredKeys; // nullable
80          this.unselectedKeys = unselectedKeys; // nullable
81      }
82  
83      @Override
84      public boolean selectDependency(Dependency dependency) {
85          requireNonNull(dependency, "dependency cannot be null");
86          String key = null;
87          if (ignoredKeys != null || unselectedKeys != null) {
88              key = ArtifactIdUtils.toId(dependency.getArtifact());
89          }
90          if (ignoredKeys != null) {
91              if (ignoredKeys.contains(key)) {
92                  return true;
93              }
94          }
95          boolean result = depth < applyFrom || !dependency.isOptional();
96          if (!result && unselectedKeys != null) {
97              unselectedKeys.add(key);
98          }
99          return result;
100     }
101 
102     @Override
103     @SuppressWarnings("unchecked")
104     public DependencySelector deriveChildSelector(DependencyCollectionContext context) {
105         requireNonNull(context, "context cannot be null");
106         if (depth >= applyFrom) {
107             return this;
108         }
109         return new OptionalDependencySelector(
110                 seed,
111                 depth + 1,
112                 applyFrom,
113                 (Collection<String>) context.getSession().getData().get(IGNORED_KEYS),
114                 (Collection<String>) context.getSession().getData().get(UNSELECTED_KEYS));
115     }
116 
117     @Override
118     public boolean equals(Object obj) {
119         if (this == obj) {
120             return true;
121         } else if (null == obj || !getClass().equals(obj.getClass())) {
122             return false;
123         }
124 
125         OptionalDependencySelector that = (OptionalDependencySelector) obj;
126         return seed == that.seed && depth == that.depth && applyFrom == that.applyFrom;
127     }
128 
129     @Override
130     public int hashCode() {
131         int hash = getClass().hashCode();
132         hash = hash * 31 + seed;
133         hash = hash * 31 + depth;
134         hash = hash * 31 + applyFrom;
135         return hash;
136     }
137 
138     @Override
139     public String toString() {
140         return String.format("%s(applied: %s)", this.getClass().getSimpleName(), depth >= applyFrom);
141     }
142 }