001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.apache.maven.tools.plugin; 020 021import java.io.File; 022import java.net.URI; 023import java.nio.file.FileSystem; 024import java.nio.file.InvalidPathException; 025import java.nio.file.Path; 026import java.util.Collection; 027import java.util.HashSet; 028import java.util.List; 029import java.util.Set; 030 031import org.apache.maven.artifact.Artifact; 032import org.apache.maven.plugin.descriptor.PluginDescriptor; 033import org.apache.maven.project.MavenProject; 034import org.apache.maven.settings.Settings; 035import org.codehaus.plexus.util.ReaderFactory; 036import org.eclipse.aether.RepositorySystemSession; 037 038/** 039 * Default implementation of {@link PluginToolsRequest}, which is used to pass parameters to components used to extract 040 * {@link org.apache.maven.plugin.descriptor.MojoDescriptor MojoDescriptor} instances from different types of metadata 041 * for a given plugin. 042 * 043 * @author jdcasey 044 * @since 2.5 045 */ 046public class DefaultPluginToolsRequest implements PluginToolsRequest { 047 048 private static final String DEFAULT_ENCODING = ReaderFactory.FILE_ENCODING; 049 050 private PluginDescriptor pluginDescriptor; 051 052 private MavenProject project; 053 054 private String encoding = DEFAULT_ENCODING; 055 056 private boolean skipErrorNoDescriptorsFound; 057 058 private Set<Artifact> dependencies; 059 060 private RepositorySystemSession repoSession; 061 062 private URI internalJavadocBaseUrl; 063 064 private String internalJavadocVersion; 065 066 private List<URI> externalJavadocBaseUrls; 067 068 private Settings settings; 069 070 private String requiredJavaVersion; 071 072 private String mavenApiVersion; 073 074 private Collection<String> excludedScanDirectories; 075 076 public DefaultPluginToolsRequest(MavenProject project, PluginDescriptor pluginDescriptor) { 077 this.project = project; 078 this.pluginDescriptor = pluginDescriptor; 079 } 080 081 /** 082 * {@inheritDoc} 083 */ 084 @Override 085 public PluginDescriptor getPluginDescriptor() { 086 return pluginDescriptor; 087 } 088 089 /** 090 * {@inheritDoc} 091 */ 092 @Override 093 public PluginToolsRequest setPluginDescriptor(PluginDescriptor pluginDescriptor) { 094 this.pluginDescriptor = pluginDescriptor; 095 return this; 096 } 097 098 /** 099 * {@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}