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