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      public boolean traverseDependency(Dependency dependency) {
96          requireNonNull(dependency, "dependency cannot be null");
97          for (DependencyTraverser traverser : traversers) {
98              if (!traverser.traverseDependency(dependency)) {
99                  return false;
100             }
101         }
102         return true;
103     }
104 
105     public DependencyTraverser deriveChildTraverser(DependencyCollectionContext context) {
106         requireNonNull(context, "context cannot be null");
107         int seen = 0;
108         Set<DependencyTraverser> childTraversers = null;
109 
110         for (DependencyTraverser traverser : traversers) {
111             DependencyTraverser childTraverser = traverser.deriveChildTraverser(context);
112             if (childTraversers != null) {
113                 if (childTraverser != null) {
114                     childTraversers.add(childTraverser);
115                 }
116             } else if (traverser != childTraverser) {
117                 childTraversers = new LinkedHashSet<>();
118                 if (seen > 0) {
119                     for (DependencyTraverser s : traversers) {
120                         if (childTraversers.size() >= seen) {
121                             break;
122                         }
123                         childTraversers.add(s);
124                     }
125                 }
126                 if (childTraverser != null) {
127                     childTraversers.add(childTraverser);
128                 }
129             } else {
130                 seen++;
131             }
132         }
133 
134         if (childTraversers == null) {
135             return this;
136         }
137         if (childTraversers.size() <= 1) {
138             if (childTraversers.isEmpty()) {
139                 return null;
140             }
141             return childTraversers.iterator().next();
142         }
143         return new AndDependencyTraverser(childTraversers);
144     }
145 
146     @Override
147     public boolean equals(Object obj) {
148         if (this == obj) {
149             return true;
150         } else if (null == obj || !getClass().equals(obj.getClass())) {
151             return false;
152         }
153 
154         AndDependencyTraverser that = (AndDependencyTraverser) obj;
155         return traversers.equals(that.traversers);
156     }
157 
158     @Override
159     public int hashCode() {
160         if (hashCode == 0) {
161             int hash = 17;
162             hash = hash * 31 + traversers.hashCode();
163             hashCode = hash;
164         }
165         return hashCode;
166     }
167 }