View Javadoc
1   package org.eclipse.aether.util.graph.selector;
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.Arrays;
23  import java.util.Collection;
24  import java.util.Comparator;
25  import java.util.TreeSet;
26  
27  import org.eclipse.aether.artifact.Artifact;
28  import org.eclipse.aether.collection.DependencyCollectionContext;
29  import org.eclipse.aether.collection.DependencySelector;
30  import org.eclipse.aether.graph.Dependency;
31  import org.eclipse.aether.graph.Exclusion;
32  
33  import static java.util.Objects.requireNonNull;
34  
35  /**
36   * A dependency selector that applies exclusions based on artifact coordinates.
37   * 
38   * @see Dependency#getExclusions()
39   */
40  public final class ExclusionDependencySelector
41      implements DependencySelector
42  {
43  
44      // sorted and dupe-free array, faster to iterate than LinkedHashSet
45      private final Exclusion[] exclusions;
46  
47      private int hashCode;
48  
49      /**
50       * Creates a new selector without any exclusions.
51       */
52      public ExclusionDependencySelector()
53      {
54          this.exclusions = new Exclusion[0];
55      }
56  
57      /**
58       * Creates a new selector with the specified exclusions.
59       * 
60       * @param exclusions The exclusions, may be {@code null}.
61       */
62      public ExclusionDependencySelector( Collection<Exclusion> exclusions )
63      {
64          if ( exclusions != null && !exclusions.isEmpty() )
65          {
66              TreeSet<Exclusion> sorted = new TreeSet<>( ExclusionComparator.INSTANCE );
67              sorted.addAll( exclusions );
68              this.exclusions = sorted.toArray( new Exclusion[0] );
69          }
70          else
71          {
72              this.exclusions = new Exclusion[0];
73          }
74      }
75  
76      private ExclusionDependencySelector( Exclusion[] exclusions )
77      {
78          this.exclusions = exclusions;
79      }
80  
81      public boolean selectDependency( Dependency dependency )
82      {
83          requireNonNull( dependency, "dependency cannot be null" );
84          Artifact artifact = dependency.getArtifact();
85          for ( Exclusion exclusion : exclusions )
86          {
87              if ( matches( exclusion, artifact ) )
88              {
89                  return false;
90              }
91          }
92          return true;
93      }
94  
95      private boolean matches( Exclusion exclusion, Artifact artifact )
96      {
97          if ( !matches( exclusion.getArtifactId(), artifact.getArtifactId() ) )
98          {
99              return false;
100         }
101         if ( !matches( exclusion.getGroupId(), artifact.getGroupId() ) )
102         {
103             return false;
104         }
105         if ( !matches( exclusion.getExtension(), artifact.getExtension() ) )
106         {
107             return false;
108         }
109         if ( !matches( exclusion.getClassifier(), artifact.getClassifier() ) )
110         {
111             return false;
112         }
113         return true;
114     }
115 
116     private boolean matches( String pattern, String value )
117     {
118         return "*".equals( pattern ) || pattern.equals( value );
119     }
120 
121     public DependencySelector deriveChildSelector( DependencyCollectionContext context )
122     {
123         requireNonNull( context, "context cannot be null" );
124         Dependency dependency = context.getDependency();
125         Collection<Exclusion> exclusions = ( dependency != null ) ? dependency.getExclusions() : null;
126         if ( exclusions == null || exclusions.isEmpty() )
127         {
128             return this;
129         }
130 
131         Exclusion[] merged = this.exclusions;
132         int count = merged.length;
133         for ( Exclusion exclusion : exclusions )
134         {
135             int index = Arrays.binarySearch( merged, exclusion, ExclusionComparator.INSTANCE );
136             if ( index < 0 )
137             {
138                 index = -( index + 1 );
139                 if ( count >= merged.length )
140                 {
141                     Exclusion[] tmp = new Exclusion[merged.length + exclusions.size()];
142                     System.arraycopy( merged, 0, tmp, 0, index );
143                     tmp[index] = exclusion;
144                     System.arraycopy( merged, index, tmp, index + 1, count - index );
145                     merged = tmp;
146                 }
147                 else
148                 {
149                     System.arraycopy( merged, index, merged, index + 1, count - index );
150                     merged[index] = exclusion;
151                 }
152                 count++;
153             }
154         }
155         if ( merged == this.exclusions )
156         {
157             return this;
158         }
159         if ( merged.length != count )
160         {
161             Exclusion[] tmp = new Exclusion[count];
162             System.arraycopy( merged, 0, tmp, 0, count );
163             merged = tmp;
164         }
165 
166         return new ExclusionDependencySelector( merged );
167     }
168 
169     @Override
170     public boolean equals( Object obj )
171     {
172         if ( this == obj )
173         {
174             return true;
175         }
176         else if ( null == obj || !getClass().equals( obj.getClass() ) )
177         {
178             return false;
179         }
180 
181         ExclusionDependencySelector that = (ExclusionDependencySelector) obj;
182         return Arrays.equals( exclusions, that.exclusions );
183     }
184 
185     @Override
186     public int hashCode()
187     {
188         if ( hashCode == 0 )
189         {
190             int hash = getClass().hashCode();
191             hash = hash * 31 + Arrays.hashCode( exclusions );
192             hashCode = hash;
193         }
194         return hashCode;
195     }
196 
197     @Override
198     public String toString()
199     {
200         StringBuilder builder = new StringBuilder().append( this.getClass().getSimpleName() ).append( '(' );
201         for ( int i = 0; i < this.exclusions.length; i++ )
202         {
203             builder.append( this.exclusions[i] );
204             if ( i < this.exclusions.length - 1 )
205             {
206                 builder.append( ", " );
207             }
208         }
209         return builder.append( ')' ).toString();
210     }
211 
212     private static class ExclusionComparator
213         implements Comparator<Exclusion>
214     {
215 
216         static final ExclusionComparator INSTANCE = new ExclusionComparator();
217 
218         public int compare( Exclusion e1, Exclusion e2 )
219         {
220             if ( e1 == null )
221             {
222                 return ( e2 == null ) ? 0 : 1;
223             }
224             else if ( e2 == null )
225             {
226                 return -1;
227             }
228             int rel = e1.getArtifactId().compareTo( e2.getArtifactId() );
229             if ( rel == 0 )
230             {
231                 rel = e1.getGroupId().compareTo( e2.getGroupId() );
232                 if ( rel == 0 )
233                 {
234                     rel = e1.getExtension().compareTo( e2.getExtension() );
235                     if ( rel == 0 )
236                     {
237                         rel = e1.getClassifier().compareTo( e2.getClassifier() );
238                     }
239                 }
240             }
241             return rel;
242         }
243 
244     }
245 
246 }