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