View Javadoc
1   package org.eclipse.aether.util.filter;
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.HashSet;
25  import java.util.List;
26  import java.util.Objects;
27  import java.util.Set;
28  
29  import org.eclipse.aether.artifact.Artifact;
30  import org.eclipse.aether.graph.Dependency;
31  import org.eclipse.aether.graph.DependencyFilter;
32  import org.eclipse.aether.graph.DependencyNode;
33  import org.eclipse.aether.version.InvalidVersionSpecificationException;
34  import org.eclipse.aether.version.Version;
35  import org.eclipse.aether.version.VersionRange;
36  import org.eclipse.aether.version.VersionScheme;
37  
38  import static java.util.Objects.requireNonNull;
39  
40  /**
41   */
42  class AbstractPatternDependencyFilter
43      implements DependencyFilter
44  {
45  
46      private final Set<String> patterns = new HashSet<>();
47  
48      private final VersionScheme versionScheme;
49  
50      /**
51       * Creates a new filter using the specified patterns.
52       * 
53       * @param patterns The include patterns, may be {@code null} or empty to include no artifacts.
54       */
55      AbstractPatternDependencyFilter( final String... patterns )
56      {
57          this( null, patterns );
58      }
59  
60      /**
61       * Creates a new filter using the specified patterns.
62       * 
63       * @param versionScheme To be used for parsing versions/version ranges. If {@code null} and pattern specifies a
64       *            range no artifact will be included.
65       * @param patterns The include patterns, may be {@code null} or empty to include no artifacts.
66       */
67      AbstractPatternDependencyFilter( final VersionScheme versionScheme, final String... patterns )
68      {
69          this( versionScheme, patterns == null ? null : Arrays.asList( patterns ) );
70      }
71  
72      /**
73       * Creates a new filter using the specified patterns.
74       * 
75       * @param patterns The include patterns, may be {@code null} or empty to include no artifacts.
76       */
77      AbstractPatternDependencyFilter( final Collection<String> patterns )
78      {
79          this( null, patterns );
80      }
81  
82      /**
83       * Creates a new filter using the specified patterns and {@link VersionScheme} .
84       * 
85       * @param versionScheme To be used for parsing versions/version ranges. If {@code null} and pattern specifies a
86       *            range no artifact will be included.
87       * @param patterns The include patterns, may be {@code null} or empty to include no artifacts.
88       */
89      AbstractPatternDependencyFilter( final VersionScheme versionScheme, final Collection<String> patterns )
90      {
91          if ( patterns != null )
92          {
93              this.patterns.addAll( patterns );
94          }
95          this.versionScheme = versionScheme;
96      }
97  
98      public boolean accept( final DependencyNode node, List<DependencyNode> parents )
99      {
100         requireNonNull( node, "node cannot be null" );
101         requireNonNull( parents, "parents cannot be null" );
102         final Dependency dependency = node.getDependency();
103         if ( dependency == null )
104         {
105             return true;
106         }
107         return accept( dependency.getArtifact() );
108     }
109 
110     protected boolean accept( final Artifact artifact )
111     {
112         for ( final String pattern : patterns )
113         {
114             final boolean matched = accept( artifact, pattern );
115             if ( matched )
116             {
117                 return true;
118             }
119         }
120         return false;
121     }
122 
123     private boolean accept( final Artifact artifact, final String pattern )
124     {
125         final String[] tokens =
126             new String[] { artifact.getGroupId(), artifact.getArtifactId(), artifact.getExtension(),
127                 artifact.getBaseVersion() };
128 
129         final String[] patternTokens = pattern.split( ":" );
130 
131         // fail immediately if pattern tokens outnumber tokens to match
132         boolean matched = ( patternTokens.length <= tokens.length );
133 
134         for ( int i = 0; matched && i < patternTokens.length; i++ )
135         {
136             matched = matches( tokens[i], patternTokens[i] );
137         }
138 
139         return matched;
140     }
141 
142     private boolean matches( final String token, final String pattern )
143     {
144         boolean matches;
145 
146         // support full wildcard and implied wildcard
147         if ( "*".equals( pattern ) || pattern.length() == 0 )
148         {
149             matches = true;
150         }
151         // support contains wildcard
152         else if ( pattern.startsWith( "*" ) && pattern.endsWith( "*" ) )
153         {
154             final String contains = pattern.substring( 1, pattern.length() - 1 );
155 
156             matches = ( token.contains( contains ) );
157         }
158         // support leading wildcard
159         else if ( pattern.startsWith( "*" ) )
160         {
161             final String suffix = pattern.substring( 1 );
162 
163             matches = token.endsWith( suffix );
164         }
165         // support trailing wildcard
166         else if ( pattern.endsWith( "*" ) )
167         {
168             final String prefix = pattern.substring( 0, pattern.length() - 1 );
169 
170             matches = token.startsWith( prefix );
171         }
172         // support versions range
173         else if ( pattern.startsWith( "[" ) || pattern.startsWith( "(" ) )
174         {
175             matches = isVersionIncludedInRange( token, pattern );
176         }
177         // support exact match
178         else
179         {
180             matches = token.equals( pattern );
181         }
182 
183         return matches;
184     }
185 
186     private boolean isVersionIncludedInRange( final String version, final String range )
187     {
188         if ( versionScheme == null )
189         {
190             return false;
191         }
192         else
193         {
194             try
195             {
196                 final Version parsedVersion = versionScheme.parseVersion( version );
197                 final VersionRange parsedRange = versionScheme.parseVersionRange( range );
198 
199                 return parsedRange.containsVersion( parsedVersion );
200             }
201             catch ( final InvalidVersionSpecificationException e )
202             {
203                 return false;
204             }
205         }
206     }
207 
208     @Override
209     public boolean equals( final Object obj )
210     {
211         if ( this == obj )
212         {
213             return true;
214         }
215 
216         if ( obj == null || !getClass().equals( obj.getClass() ) )
217         {
218             return false;
219         }
220 
221         final AbstractPatternDependencyFilter that = (AbstractPatternDependencyFilter) obj;
222 
223         return Objects.equals( this.patterns, that.patterns )
224             && Objects.equals( this.versionScheme, that.versionScheme );
225     }
226 
227     @Override
228     public int hashCode()
229     {
230         int hash = 17;
231         hash = hash * 31 + patterns.hashCode();
232         hash = hash * 31 + ( ( versionScheme == null ) ? 0 : versionScheme.hashCode() );
233         return hash;
234     }
235 
236 }