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