View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.tools.plugin;
20  
21  import java.io.File;
22  import java.net.URI;
23  import java.nio.file.FileSystem;
24  import java.nio.file.InvalidPathException;
25  import java.nio.file.Path;
26  import java.util.Collection;
27  import java.util.HashSet;
28  import java.util.List;
29  import java.util.Set;
30  
31  import org.apache.maven.artifact.Artifact;
32  import org.apache.maven.plugin.descriptor.PluginDescriptor;
33  import org.apache.maven.project.MavenProject;
34  import org.apache.maven.settings.Settings;
35  import org.codehaus.plexus.util.ReaderFactory;
36  import org.eclipse.aether.RepositorySystemSession;
37  
38  /**
39   * Default implementation of {@link PluginToolsRequest}, which is used to pass parameters to components used to extract
40   * {@link org.apache.maven.plugin.descriptor.MojoDescriptor MojoDescriptor} instances from different types of metadata
41   * for a given plugin.
42   *
43   * @author jdcasey
44   * @since 2.5
45   */
46  public class DefaultPluginToolsRequest implements PluginToolsRequest {
47  
48      private static final String DEFAULT_ENCODING = ReaderFactory.FILE_ENCODING;
49  
50      private PluginDescriptor pluginDescriptor;
51  
52      private MavenProject project;
53  
54      private String encoding = DEFAULT_ENCODING;
55  
56      private boolean skipErrorNoDescriptorsFound;
57  
58      private Set<Artifact> dependencies;
59  
60      private RepositorySystemSession repoSession;
61  
62      private URI internalJavadocBaseUrl;
63  
64      private String internalJavadocVersion;
65  
66      private List<URI> externalJavadocBaseUrls;
67  
68      private Settings settings;
69  
70      private String requiredJavaVersion;
71  
72      private String mavenApiVersion;
73  
74      private Collection<String> excludedScanDirectories;
75  
76      public DefaultPluginToolsRequest(MavenProject project, PluginDescriptor pluginDescriptor) {
77          this.project = project;
78          this.pluginDescriptor = pluginDescriptor;
79      }
80  
81      /**
82       * {@inheritDoc}
83       */
84      @Override
85      public PluginDescriptor getPluginDescriptor() {
86          return pluginDescriptor;
87      }
88  
89      /**
90       * {@inheritDoc}
91       */
92      @Override
93      public PluginToolsRequest setPluginDescriptor(PluginDescriptor pluginDescriptor) {
94          this.pluginDescriptor = pluginDescriptor;
95          return this;
96      }
97  
98      /**
99       * {@inheritDoc}
100      */
101     @Override
102     public MavenProject getProject() {
103         return project;
104     }
105 
106     /**
107      * {@inheritDoc}
108      */
109     @Override
110     public PluginToolsRequest setProject(MavenProject project) {
111         this.project = project;
112         return this;
113     }
114 
115     /**
116      * {@inheritDoc}
117      */
118     @Override
119     public String getEncoding() {
120         return this.encoding;
121     }
122 
123     /**
124      * {@inheritDoc}
125      */
126     @Override
127     public PluginToolsRequest setEncoding(String encoding) {
128         if (encoding != null && !encoding.isEmpty()) {
129             this.encoding = encoding;
130         } else {
131             this.encoding = DEFAULT_ENCODING;
132         }
133 
134         return this;
135     }
136 
137     /**
138      * {@inheritDoc}
139      */
140     @Override
141     public boolean isSkipErrorNoDescriptorsFound() {
142         return skipErrorNoDescriptorsFound;
143     }
144 
145     /**
146      * {@inheritDoc}
147      */
148     @Override
149     public PluginToolsRequest setSkipErrorNoDescriptorsFound(boolean skipErrorNoDescriptorsFound) {
150         this.skipErrorNoDescriptorsFound = skipErrorNoDescriptorsFound;
151         return this;
152     }
153 
154     @Override
155     public Set<Artifact> getDependencies() {
156         if (this.dependencies == null) {
157             this.dependencies = new HashSet<>();
158         }
159         return dependencies;
160     }
161 
162     @Override
163     public PluginToolsRequest setDependencies(Set<Artifact> dependencies) {
164         this.dependencies = dependencies;
165         return this;
166     }
167 
168     @Override
169     public RepositorySystemSession getRepoSession() {
170         return repoSession;
171     }
172 
173     public void setRepoSession(RepositorySystemSession repoSession) {
174         this.repoSession = repoSession;
175     }
176 
177     @Override
178     public PluginToolsRequest setInternalJavadocBaseUrl(URI baseUrl) {
179         internalJavadocBaseUrl = baseUrl;
180         return this;
181     }
182 
183     @Override
184     public URI getInternalJavadocBaseUrl() {
185         return internalJavadocBaseUrl;
186     }
187 
188     @Override
189     public PluginToolsRequest setInternalJavadocVersion(String javadocVersion) {
190         this.internalJavadocVersion = javadocVersion;
191         return this;
192     }
193 
194     @Override
195     public String getInternalJavadocVersion() {
196         return internalJavadocVersion;
197     }
198 
199     @Override
200     public PluginToolsRequest setExternalJavadocBaseUrls(List<URI> javadocLinks) {
201         this.externalJavadocBaseUrls = javadocLinks;
202         return this;
203     }
204 
205     @Override
206     public List<URI> getExternalJavadocBaseUrls() {
207         return externalJavadocBaseUrls;
208     }
209 
210     @Override
211     public PluginToolsRequest setSettings(Settings settings) {
212         this.settings = settings;
213         return this;
214     }
215 
216     @Override
217     public Settings getSettings() {
218         return settings;
219     }
220 
221     @Override
222     public PluginToolsRequest setRequiredJavaVersion(String requiredJavaVersion) {
223         this.requiredJavaVersion = requiredJavaVersion;
224         return this;
225     }
226 
227     @Override
228     public String getRequiredJavaVersion() {
229         return requiredJavaVersion;
230     }
231 
232     @Override
233     public PluginToolsRequest setUsedMavenApiVersion(String mavenApiVersion) {
234         this.mavenApiVersion = mavenApiVersion;
235         return this;
236     }
237 
238     @Override
239     public String getUsedMavenApiVersion() {
240         return mavenApiVersion;
241     }
242 
243     @Override
244     public Collection<String> getExcludedScanDirectories() {
245         if (excludedScanDirectories == null) {
246             excludedScanDirectories = new HashSet<>();
247         }
248         return excludedScanDirectories;
249     }
250 
251     @Override
252     public void setExcludedScanDirectories(Collection<String> excludedScanDirectories) {
253         this.excludedScanDirectories = excludedScanDirectories;
254     }
255 
256     @Override
257     public boolean isExcludedScanDirectory(File sourceFile) {
258         return isExcluded(sourceFile.toPath(), getExcludedScanDirectories());
259     }
260 
261     /**
262      * Determines whether a source directory is covered by any of the configured exclusions.
263      * <p>
264      * Visible for testing: taking the directory as a {@link Path} keeps the whole decision tied to a single
265      * {@link FileSystem}, so the behaviour on Windows separators can be exercised without running on Windows.
266      *
267      * @param sourceDirectory the source directory to check
268      * @param excludedScanDirectories the configured exclusions, either plain directories or globs
269      * @return true if excluded, false otherwise
270      */
271     static boolean isExcluded(Path sourceDirectory, Collection<String> excludedScanDirectories) {
272         FileSystem fileSystem = sourceDirectory.getFileSystem();
273         boolean windowsSeparators = "\\".equals(fileSystem.getSeparator());
274         Path sourcePath = sourceDirectory.toAbsolutePath().normalize();
275         for (String excludedScanDirectory : excludedScanDirectories) {
276             if (excludedScanDirectory == null || excludedScanDirectory.isEmpty()) {
277                 // an empty entry would resolve to the working directory and exclude it silently
278                 continue;
279             }
280             // The documented form of this parameter is a plain directory, so compare as paths first. That
281             // is safe with respect to separators, trailing separators and case on every file system.
282             try {
283                 Path excludedPath = fileSystem
284                         .getPath(excludedScanDirectory)
285                         .toAbsolutePath()
286                         .normalize();
287                 if (sourcePath.equals(excludedPath)) {
288                     return true;
289                 }
290             } catch (InvalidPathException e) {
291                 // not a plain directory - the Windows parser rejects wildcards, for instance - so it can
292                 // only ever have been meant as a glob
293             }
294             if (fileSystem
295                     .getPathMatcher("glob:" + toGlobPattern(excludedScanDirectory, windowsSeparators))
296                     .matches(sourcePath)) {
297                 return true;
298             }
299         }
300         return false;
301     }
302 
303     /**
304      * Turns a configured exclusion into a glob pattern.
305      * <p>
306      * Configured values normally come from an interpolated property such as
307      * <code>${project.build.directory}/generated-sources/annotations</code> and therefore hold backslashes on
308      * Windows. A backslash is the escape character of the glob syntax and is swallowed by the pattern compiler
309      * on every platform, so such a value could never match a real path, and one ending in a separator even
310      * failed the build with a {@link java.util.regex.PatternSyntaxException}. A forward slash is compiled back
311      * into the Windows separator, so it is the portable way to spell a separator in a glob.
312      *
313      * @param excludedScanDirectory the configured exclusion
314      * @param windowsSeparators whether the file system separates names with a backslash
315      * @return the pattern to hand to {@link FileSystem#getPathMatcher(String)}
316      */
317     static String toGlobPattern(String excludedScanDirectory, boolean windowsSeparators) {
318         // On a file system that allows backslashes in names they may be a deliberate escape, so leave them be.
319         return windowsSeparators ? excludedScanDirectory.replace('\\', '/') : excludedScanDirectory;
320     }
321 }