1 package org.eclipse.aether.graph;
2
3 /*
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
19 * under the License.
20 */
21
22 import java.util.AbstractSet;
23 import java.util.Collection;
24 import java.util.Collections;
25 import java.util.Iterator;
26 import java.util.LinkedHashSet;
27 import java.util.NoSuchElementException;
28 import static java.util.Objects.requireNonNull;
29 import java.util.Set;
30
31 import org.eclipse.aether.artifact.Artifact;
32
33 /**
34 * A dependency to some artifact. <em>Note:</em> Instances of this class are immutable and the exposed mutators return
35 * new objects rather than changing the current instance.
36 */
37 public final class Dependency
38 {
39
40 private final Artifact artifact;
41
42 private final String scope;
43
44 private final Boolean optional;
45
46 private final Set<Exclusion> exclusions;
47
48 /**
49 * Creates a mandatory dependency on the specified artifact with the given scope.
50 *
51 * @param artifact The artifact being depended on, must not be {@code null}.
52 * @param scope The scope of the dependency, may be {@code null}.
53 */
54 public Dependency( Artifact artifact, String scope )
55 {
56 this( artifact, scope, false );
57 }
58
59 /**
60 * Creates a dependency on the specified artifact with the given scope.
61 *
62 * @param artifact The artifact being depended on, must not be {@code null}.
63 * @param scope The scope of the dependency, may be {@code null}.
64 * @param optional A flag whether the dependency is optional or mandatory, may be {@code null}.
65 */
66 public Dependency( Artifact artifact, String scope, Boolean optional )
67 {
68 this( artifact, scope, optional, null );
69 }
70
71 /**
72 * Creates a dependency on the specified artifact with the given scope and exclusions.
73 *
74 * @param artifact The artifact being depended on, must not be {@code null}.
75 * @param scope The scope of the dependency, may be {@code null}.
76 * @param optional A flag whether the dependency is optional or mandatory, may be {@code null}.
77 * @param exclusions The exclusions that apply to transitive dependencies, may be {@code null} if none.
78 */
79 public Dependency( Artifact artifact, String scope, Boolean optional, Collection<Exclusion> exclusions )
80 {
81 this( artifact, scope, Exclusions.copy( exclusions ), optional );
82 }
83
84 private Dependency( Artifact artifact, String scope, Set<Exclusion> exclusions, Boolean optional )
85 {
86 // NOTE: This constructor assumes immutability of the provided exclusion collection, for internal use only
87 this.artifact = requireNonNull( artifact, "artifact cannot be null" );
88 this.scope = ( scope != null ) ? scope : "";
89 this.optional = optional;
90 this.exclusions = exclusions;
91 }
92
93 /**
94 * Gets the artifact being depended on.
95 *
96 * @return The artifact, never {@code null}.
97 */
98 public Artifact getArtifact()
99 {
100 return artifact;
101 }
102
103 /**
104 * Sets the artifact being depended on.
105 *
106 * @param artifact The artifact, must not be {@code null}.
107 * @return The new dependency, never {@code null}.
108 */
109 public Dependency setArtifact( Artifact artifact )
110 {
111 if ( this.artifact.equals( artifact ) )
112 {
113 return this;
114 }
115 return new Dependency( artifact, scope, exclusions, optional );
116 }
117
118 /**
119 * Gets the scope of the dependency. The scope defines in which context this dependency is relevant.
120 *
121 * @return The scope or an empty string if not set, never {@code null}.
122 */
123 public String getScope()
124 {
125 return scope;
126 }
127
128 /**
129 * Sets the scope of the dependency, e.g. "compile".
130 *
131 * @param scope The scope of the dependency, may be {@code null}.
132 * @return The new dependency, never {@code null}.
133 */
134 public Dependency setScope( String scope )
135 {
136 if ( this.scope.equals( scope ) || ( scope == null && this.scope.length() <= 0 ) )
137 {
138 return this;
139 }
140 return new Dependency( artifact, scope, exclusions, optional );
141 }
142
143 /**
144 * Indicates whether this dependency is optional or not. Optional dependencies can be ignored in some contexts.
145 *
146 * @return {@code true} if the dependency is (definitively) optional, {@code false} otherwise.
147 */
148 public boolean isOptional()
149 {
150 return Boolean.TRUE.equals( optional );
151 }
152
153 /**
154 * Gets the optional flag for the dependency. Note: Most clients will usually call {@link #isOptional()} to
155 * determine the optional flag, this method is for advanced use cases where three-valued logic is required.
156 *
157 * @return The optional flag or {@code null} if unspecified.
158 */
159 public Boolean getOptional()
160 {
161 return optional;
162 }
163
164 /**
165 * Sets the optional flag for the dependency.
166 *
167 * @param optional {@code true} if the dependency is optional, {@code false} if the dependency is mandatory, may be
168 * {@code null} if unspecified.
169 * @return The new dependency, never {@code null}.
170 */
171 public Dependency setOptional( Boolean optional )
172 {
173 if ( eq( this.optional, optional ) )
174 {
175 return this;
176 }
177 return new Dependency( artifact, scope, exclusions, optional );
178 }
179
180 /**
181 * Gets the exclusions for this dependency. Exclusions can be used to remove transitive dependencies during
182 * resolution.
183 *
184 * @return The (read-only) exclusions, never {@code null}.
185 */
186 public Collection<Exclusion> getExclusions()
187 {
188 return exclusions;
189 }
190
191 /**
192 * Sets the exclusions for the dependency.
193 *
194 * @param exclusions The exclusions, may be {@code null}.
195 * @return The new dependency, never {@code null}.
196 */
197 public Dependency setExclusions( Collection<Exclusion> exclusions )
198 {
199 if ( hasEquivalentExclusions( exclusions ) )
200 {
201 return this;
202 }
203 return new Dependency( artifact, scope, optional, exclusions );
204 }
205
206 private boolean hasEquivalentExclusions( Collection<Exclusion> exclusions )
207 {
208 if ( exclusions == null || exclusions.isEmpty() )
209 {
210 return this.exclusions.isEmpty();
211 }
212 if ( exclusions instanceof Set )
213 {
214 return this.exclusions.equals( exclusions );
215 }
216 return exclusions.size() >= this.exclusions.size() && this.exclusions.containsAll( exclusions )
217 && exclusions.containsAll( this.exclusions );
218 }
219
220 @Override
221 public String toString()
222 {
223 return String.valueOf( getArtifact() ) + " (" + getScope() + ( isOptional() ? "?" : "" ) + ")";
224 }
225
226 @Override
227 public boolean equals( Object obj )
228 {
229 if ( obj == this )
230 {
231 return true;
232 }
233 else if ( obj == null || !getClass().equals( obj.getClass() ) )
234 {
235 return false;
236 }
237
238 Dependency that = (Dependency) obj;
239
240 return artifact.equals( that.artifact ) && scope.equals( that.scope ) && eq( optional, that.optional )
241 && exclusions.equals( that.exclusions );
242 }
243
244 private static <T> boolean eq( T o1, T o2 )
245 {
246 return ( o1 != null ) ? o1.equals( o2 ) : o2 == null;
247 }
248
249 @Override
250 public int hashCode()
251 {
252 int hash = 17;
253 hash = hash * 31 + artifact.hashCode();
254 hash = hash * 31 + scope.hashCode();
255 hash = hash * 31 + ( optional != null ? optional.hashCode() : 0 );
256 hash = hash * 31 + exclusions.size();
257 return hash;
258 }
259
260 private static class Exclusions
261 extends AbstractSet<Exclusion>
262 {
263
264 private final Exclusion[] exclusions;
265
266 public static Set<Exclusion> copy( Collection<Exclusion> exclusions )
267 {
268 if ( exclusions == null || exclusions.isEmpty() )
269 {
270 return Collections.emptySet();
271 }
272 return new Exclusions( exclusions );
273 }
274
275 private Exclusions( Collection<Exclusion> exclusions )
276 {
277 if ( exclusions.size() > 1 && !( exclusions instanceof Set ) )
278 {
279 exclusions = new LinkedHashSet<Exclusion>( exclusions );
280 }
281 this.exclusions = exclusions.toArray( new Exclusion[exclusions.size()] );
282 }
283
284 @Override
285 public Iterator<Exclusion> iterator()
286 {
287 return new Iterator<Exclusion>()
288 {
289
290 private int cursor = 0;
291
292 public boolean hasNext()
293 {
294 return cursor < exclusions.length;
295 }
296
297 public Exclusion next()
298 {
299 try
300 {
301 Exclusion exclusion = exclusions[cursor];
302 cursor++;
303 return exclusion;
304 }
305 catch ( IndexOutOfBoundsException e )
306 {
307 throw new NoSuchElementException();
308 }
309 }
310
311 public void remove()
312 {
313 throw new UnsupportedOperationException();
314 }
315
316 };
317 }
318
319 @Override
320 public int size()
321 {
322 return exclusions.length;
323 }
324
325 }
326
327 }