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 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      }
57  
58      /**
59       * Creates a new traverser from the specified traversers.
60       *
61       * @param traversers The traversers to combine, may be {@code null} but must not contain {@code null} elements.
62       */
63      public AndDependencyTraverser(Collection<? extends DependencyTraverser> traversers) {
64          if (traversers != null && !traversers.isEmpty()) {
65              this.traversers = new LinkedHashSet<>(traversers);
66          } else {
67              this.traversers = Collections.emptySet();
68          }
69      }
70  
71      private AndDependencyTraverser(Set<DependencyTraverser> traversers) {
72          if (traversers != null && !traversers.isEmpty()) {
73              this.traversers = traversers;
74          } else {
75              this.traversers = Collections.emptySet();
76          }
77      }
78  
79      /**
80       * Creates a new traverser from the specified traversers.
81       *
82       * @param traverser1 The first traverser to combine, may be {@code null}.
83       * @param traverser2 The second traverser to combine, may be {@code null}.
84       * @return The combined traverser or {@code null} if both traversers were {@code null}.
85       */
86      public static DependencyTraverser newInstance(DependencyTraverser traverser1, DependencyTraverser traverser2) {
87          if (traverser1 == null) {
88              return traverser2;
89          } else if (traverser2 == null || traverser2.equals(traverser1)) {
90              return traverser1;
91          }
92          return new AndDependencyTraverser(traverser1, traverser2);
93      }
94  
95      @Override
96      public boolean traverseDependency(Dependency dependency) {
97          requireNonNull(dependency, "dependency cannot be null");
98          for (DependencyTraverser traverser : traversers) {
99              if (!traverser.traverseDependency(dependency)) {
100                 return false;
101             }
102         }
103         return true;
104     }
105 
106     @Override
107     public DependencyTraverser deriveChildTraverser(DependencyCollectionContext context) {
108         requireNonNull(context, "context cannot be null");
109         int seen = 0;
110         Set<DependencyTraverser> childTraversers = null;
111 
112         for (DependencyTraverser traverser : traversers) {
113             DependencyTraverser childTraverser = traverser.deriveChildTraverser(context);
114             if (childTraversers != null) {
115                 if (childTraverser != null) {
116                     childTraversers.add(childTraverser);
117                 }
118             } else if (traverser != childTraverser) {
119                 childTraversers = new LinkedHashSet<>();
120                 if (seen > 0) {
121                     for (DependencyTraverser s : traversers) {
122                         if (childTraversers.size() >= seen) {
123                             break;
124                         }
125                         childTraversers.add(s);
126                     }
127                 }
128                 if (childTraverser != null) {
129                     childTraversers.add(childTraverser);
130                 }
131             } else {
132                 seen++;
133             }
134         }
135 
136         if (childTraversers == null) {
137             return this;
138         }
139         if (childTraversers.size() <= 1) {
140             if (childTraversers.isEmpty()) {
141                 return null;
142             }
143             return childTraversers.iterator().next();
144         }
145         return new AndDependencyTraverser(childTraversers);
146     }
147 
148     @Override
149     public boolean equals(Object obj) {
150         if (this == obj) {
151             return true;
152         } else if (null == obj || !getClass().equals(obj.getClass())) {
153             return false;
154         }
155 
156         AndDependencyTraverser that = (AndDependencyTraverser) obj;
157         return traversers.equals(that.traversers);
158     }
159 
160     @Override
161     public int hashCode() {
162         if (hashCode == 0) {
163             int hash = 17;
164             hash = hash * 31 + traversers.hashCode();
165             hashCode = hash;
166         }
167         return hashCode;
168     }
169 }