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 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 }