View Javadoc

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