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.traverser;
20  
21  import java.util.Arrays;
22  import java.util.Collection;
23  import java.util.Collections;
24  import java.util.LinkedHashSet;
25  import java.util.Set;
26  
27  import org.eclipse.aether.collection.DependencyCollectionContext;
28  import org.eclipse.aether.collection.DependencyTraverser;
29  import org.eclipse.aether.graph.Dependency;
30  
31  import static java.util.Objects.requireNonNull;
32  
33  /**
34   * A dependency traverser that combines zero or more other traversers using a logical {@code AND}. The resulting
35   * traverser enables processing of child dependencies if and only if all constituent traversers request traversal.
36   */
37  public final class AndDependencyTraverser implements DependencyTraverser {
38  
39      private final Set<? extends DependencyTraverser> traversers;
40  
41      private final int hashCode;
42  
43      /**
44       * Creates a new traverser from the specified traversers. Prefer
45       * {@link #newInstance(DependencyTraverser, DependencyTraverser)} if any of the input traversers might be
46       * {@code null}.
47       *
48       * @param traversers the traversers to combine, may be {@code null} but must not contain {@code null} elements
49       */
50      public AndDependencyTraverser(DependencyTraverser... traversers) {
51          if (traversers != null && traversers.length > 0) {
52              this.traversers = new LinkedHashSet<>(Arrays.asList(traversers));
53          } else {
54              this.traversers = Collections.emptySet();
55          }
56          this.hashCode = 17 * 31 + this.traversers.hashCode();
57      }
58  
59      /**
60       * Creates a new traverser from the specified traversers.
61       *
62       * @param traversers the traversers to combine, may be {@code null} but must not contain {@code null} elements
63       */
64      public AndDependencyTraverser(Collection<? extends DependencyTraverser> traversers) {
65          if (traversers != null && !traversers.isEmpty()) {
66              this.traversers = new LinkedHashSet<>(traversers);
67          } else {
68              this.traversers = Collections.emptySet();
69          }
70          this.hashCode = 17 * 31 + this.traversers.hashCode();
71      }
72  
73      private AndDependencyTraverser(Set<DependencyTraverser> traversers) {
74          if (traversers != null && !traversers.isEmpty()) {
75              this.traversers = traversers;
76          } else {
77              this.traversers = Collections.emptySet();
78          }
79          this.hashCode = 17 * 31 + this.traversers.hashCode();
80      }
81  
82      /**
83       * Creates a new traverser from the specified traversers.
84       *
85       * @param traverser1 the first traverser to combine, may be {@code null}
86       * @param traverser2 the second traverser to combine, may be {@code null}
87       * @return the combined traverser or {@code null} if both traversers were {@code null}
88       */
89      public static DependencyTraverser newInstance(DependencyTraverser traverser1, DependencyTraverser traverser2) {
90          if (traverser1 == null) {
91              return traverser2;
92          } else if (traverser2 == null || traverser2.equals(traverser1)) {
93              return traverser1;
94          }
95          return new AndDependencyTraverser(traverser1, traverser2);
96      }
97  
98      @Override
99      public boolean traverseDependency(Dependency dependency) {
100         requireNonNull(dependency, "dependency cannot be null");
101         for (DependencyTraverser traverser : traversers) {
102             if (!traverser.traverseDependency(dependency)) {
103                 return false;
104             }
105         }
106         return true;
107     }
108 
109     @Override
110     public DependencyTraverser deriveChildTraverser(DependencyCollectionContext context) {
111         requireNonNull(context, "context cannot be null");
112         int seen = 0;
113         Set<DependencyTraverser> childTraversers = null;
114 
115         for (DependencyTraverser traverser : traversers) {
116             DependencyTraverser childTraverser = traverser.deriveChildTraverser(context);
117             if (childTraversers != null) {
118                 if (childTraverser != null) {
119                     childTraversers.add(childTraverser);
120                 }
121             } else if (traverser != childTraverser) {
122                 childTraversers = new LinkedHashSet<>();
123                 if (seen > 0) {
124                     for (DependencyTraverser s : traversers) {
125                         if (childTraversers.size() >= seen) {
126                             break;
127                         }
128                         childTraversers.add(s);
129                     }
130                 }
131                 if (childTraverser != null) {
132                     childTraversers.add(childTraverser);
133                 }
134             } else {
135                 seen++;
136             }
137         }
138 
139         if (childTraversers == null) {
140             return this;
141         }
142         if (childTraversers.size() <= 1) {
143             if (childTraversers.isEmpty()) {
144                 return null;
145             }
146             return childTraversers.iterator().next();
147         }
148         return new AndDependencyTraverser(childTraversers);
149     }
150 
151     @Override
152     public boolean equals(Object obj) {
153         if (this == obj) {
154             return true;
155         } else if (null == obj || !getClass().equals(obj.getClass())) {
156             return false;
157         }
158 
159         AndDependencyTraverser that = (AndDependencyTraverser) obj;
160         return traversers.equals(that.traversers);
161     }
162 
163     @Override
164     public int hashCode() {
165         return hashCode;
166     }
167 }