1 package org.apache.maven.plugin.checkstyle;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.io.ByteArrayInputStream;
23 import java.io.File;
24 import java.io.FileInputStream;
25 import java.io.IOException;
26 import java.net.MalformedURLException;
27 import java.net.URL;
28 import java.net.URLClassLoader;
29 import java.util.ArrayList;
30 import java.util.Arrays;
31 import java.util.Collections;
32 import java.util.List;
33 import java.util.Properties;
34
35 import org.apache.commons.io.IOUtils;
36 import org.apache.maven.artifact.DependencyResolutionRequiredException;
37 import org.apache.maven.project.MavenProject;
38 import org.codehaus.plexus.logging.AbstractLogEnabled;
39 import org.codehaus.plexus.resource.ResourceManager;
40 import org.codehaus.plexus.resource.loader.FileResourceCreationException;
41 import org.codehaus.plexus.resource.loader.FileResourceLoader;
42 import org.codehaus.plexus.resource.loader.ResourceNotFoundException;
43 import org.codehaus.plexus.util.FileUtils;
44 import org.codehaus.plexus.util.StringUtils;
45
46 import com.puppycrawl.tools.checkstyle.Checker;
47 import com.puppycrawl.tools.checkstyle.ConfigurationLoader;
48 import com.puppycrawl.tools.checkstyle.DefaultConfiguration;
49 import com.puppycrawl.tools.checkstyle.PackageNamesLoader;
50 import com.puppycrawl.tools.checkstyle.PropertiesExpander;
51 import com.puppycrawl.tools.checkstyle.api.AuditListener;
52 import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
53 import com.puppycrawl.tools.checkstyle.api.Configuration;
54 import com.puppycrawl.tools.checkstyle.api.FilterSet;
55 import com.puppycrawl.tools.checkstyle.filters.SuppressionsLoader;
56
57
58
59
60
61
62
63 public class DefaultCheckstyleExecutor
64 extends AbstractLogEnabled
65 implements CheckstyleExecutor
66 {
67
68
69
70
71 private ResourceManager locator;
72
73 private static final File[] EMPTY_FILE_ARRAY = new File[0];
74
75 public CheckstyleResults executeCheckstyle( CheckstyleExecutorRequest request )
76 throws CheckstyleExecutorException, CheckstyleException
77 {
78
79
80
81
82 ClassLoader checkstyleClassLoader = PackageNamesLoader.class.getClassLoader();
83 Thread.currentThread().setContextClassLoader( checkstyleClassLoader );
84
85 if ( getLogger().isDebugEnabled() )
86 {
87 getLogger().debug( "executeCheckstyle start headerLocation : " + request.getHeaderLocation() );
88 }
89 locator.setOutputDirectory( new File( request.getProject().getBuild().getDirectory() ) );
90 File[] files;
91 try
92 {
93 files = getFilesToProcess( request );
94 }
95 catch ( IOException e )
96 {
97 throw new CheckstyleExecutorException( "Error getting files to process", e );
98 }
99
100 FilterSet filterSet = getSuppressions( request );
101
102 Checker checker = new Checker();
103
104
105
106 List<String> classPathStrings = new ArrayList<String>();
107 List<String> outputDirectories = new ArrayList<String>();
108 try
109 {
110 classPathStrings = request.getProject().getCompileClasspathElements();
111 outputDirectories.add( request.getProject().getBuild().getOutputDirectory() );
112
113 if ( request.isIncludeTestSourceDirectory() && ( request.getSourceDirectory() != null )
114 && ( request.getTestSourceDirectory().exists() ) && ( request.getTestSourceDirectory().isDirectory() ) )
115 {
116 classPathStrings = request.getProject().getTestClasspathElements();
117 outputDirectories.add( request.getProject().getBuild().getTestOutputDirectory() );
118 }
119 }
120 catch ( DependencyResolutionRequiredException e )
121 {
122 throw new CheckstyleExecutorException( e.getMessage(), e );
123 }
124
125 if ( classPathStrings == null )
126 {
127 classPathStrings = Collections.EMPTY_LIST;
128 }
129
130 List<URL> urls = new ArrayList<URL>( classPathStrings.size() );
131
132 for ( String path : classPathStrings )
133 {
134 try
135 {
136 urls.add( new File( path ).toURL() );
137 }
138 catch ( MalformedURLException e )
139 {
140 throw new CheckstyleExecutorException( e.getMessage(), e );
141 }
142 }
143
144 for ( String outputDirectoryString : outputDirectories )
145 {
146 try
147 {
148 if ( outputDirectoryString != null )
149 {
150 File outputDirectoryFile = new File( outputDirectoryString );
151 if ( outputDirectoryFile.exists() )
152 {
153 URL outputDirectoryUrl = outputDirectoryFile.toURL();
154 request.getLog().debug(
155 "Adding the outputDirectory " + outputDirectoryUrl.toString()
156 + " to the Checkstyle class path" );
157 urls.add( outputDirectoryUrl );
158 }
159 }
160 }
161 catch ( MalformedURLException e )
162 {
163 throw new CheckstyleExecutorException( e.getMessage(), e );
164 }
165 }
166
167 URLClassLoader projectClassLoader = new URLClassLoader( (URL[]) urls.toArray( new URL[urls.size()] ), null );
168 checker.setClassloader( projectClassLoader );
169
170 checker.setModuleClassLoader( Thread.currentThread().getContextClassLoader() );
171
172 if ( filterSet != null )
173 {
174 checker.addFilter( filterSet );
175 }
176 Configuration configuration = getConfiguration( request );
177 checker.configure( configuration );
178
179 AuditListener listener = request.getListener();
180
181 if ( listener != null )
182 {
183 checker.addListener( listener );
184 }
185
186 if ( request.isConsoleOutput() )
187 {
188 checker.addListener( request.getConsoleListener() );
189 }
190
191 CheckstyleReportListener sinkListener = new CheckstyleReportListener( request.getSourceDirectory(), configuration );
192 if ( request.isIncludeTestSourceDirectory() && ( request.getTestSourceDirectory() != null )
193 && ( request.getTestSourceDirectory().exists() ) && ( request.getTestSourceDirectory().isDirectory() ) )
194 {
195 sinkListener.addSourceDirectory( request.getTestSourceDirectory() );
196 }
197
198 checker.addListener( sinkListener );
199
200 List<File> filesList = Arrays.asList( files );
201 int nbErrors = checker.process( filesList );
202
203 checker.destroy();
204
205 if ( request.getStringOutputStream() != null )
206 {
207 request.getLog().info( request.getStringOutputStream().toString() );
208 }
209
210 if ( request.isFailsOnError() && nbErrors > 0 )
211 {
212
213
214
215 throw new CheckstyleExecutorException( "There are " + nbErrors + " checkstyle errors." );
216 }
217 else if ( nbErrors > 0 )
218 {
219 request.getLog().info( "There are " + nbErrors + " checkstyle errors." );
220 }
221
222 return sinkListener.getResults();
223 }
224
225 public Configuration getConfiguration( CheckstyleExecutorRequest request )
226 throws CheckstyleExecutorException
227 {
228 try
229 {
230
231
232
233 ClassLoader checkstyleClassLoader = PackageNamesLoader.class.getClassLoader();
234 Thread.currentThread().setContextClassLoader( checkstyleClassLoader );
235 String configFile = getConfigFile( request );
236 Properties overridingProperties = getOverridingProperties( request );
237 Configuration config = ConfigurationLoader
238 .loadConfiguration( configFile, new PropertiesExpander( overridingProperties ) );
239 String effectiveEncoding = StringUtils.isNotEmpty( request.getEncoding() ) ? request.getEncoding() : System
240 .getProperty( "file.encoding", "UTF-8" );
241 if ( StringUtils.isEmpty( request.getEncoding() ) )
242 {
243 request.getLog().warn(
244 "File encoding has not been set, using platform encoding " + effectiveEncoding
245 + ", i.e. build is platform dependent!" );
246 }
247 Configuration[] modules = config.getChildren();
248 for ( int i = 0; i < modules.length; i++ )
249 {
250 Configuration module = modules[i];
251 if ( "Checker".equals( module.getName() )
252 || "com.puppycrawl.tools.checkstyle.Checker".equals( module.getName() ) )
253 {
254 if ( module instanceof DefaultConfiguration )
255 {
256 ( (DefaultConfiguration) module ).addAttribute( "charset", effectiveEncoding );
257 }
258 else
259 {
260 request.getLog().warn( "Failed to configure file encoding on module " + module );
261 }
262 }
263 if ( "TreeWalker".equals( module.getName() )
264 || "com.puppycrawl.tools.checkstyle.TreeWalker".equals( module.getName() ) )
265 {
266 if ( module instanceof DefaultConfiguration )
267 {
268
269 if ( ( (DefaultConfiguration) module ).getAttribute( "cacheFile" ) == null )
270 {
271 ( (DefaultConfiguration) module ).addAttribute( "cacheFile", request.getCacheFile() );
272 }
273 }
274 else
275 {
276 request.getLog().warn( "Failed to configure cache file on module " + module );
277 }
278 }
279 }
280 return config;
281 }
282 catch ( CheckstyleException e )
283 {
284 throw new CheckstyleExecutorException( "Failed during checkstyle configuration", e );
285 }
286 }
287
288 private Properties getOverridingProperties( CheckstyleExecutorRequest request )
289 throws CheckstyleExecutorException
290 {
291 Properties p = new Properties();
292
293 try
294 {
295 if ( request.getPropertiesLocation() != null )
296 {
297 if ( getLogger().isDebugEnabled() )
298 {
299 getLogger().debug( "request.getPropertiesLocation() " + request.getPropertiesLocation() );
300 }
301
302 File propertiesFile = locator.getResourceAsFile( request.getPropertiesLocation(),
303 "checkstyle-checker.properties" );
304
305 FileInputStream properties = new FileInputStream( propertiesFile );
306 try
307 {
308 if ( propertiesFile != null )
309 {
310 p.load( properties );
311 }
312 } finally {
313 IOUtils.closeQuietly( properties );
314 }
315 }
316
317 if ( StringUtils.isNotEmpty( request.getPropertyExpansion() ) )
318 {
319 String propertyExpansion = request.getPropertyExpansion();
320
321 propertyExpansion = StringUtils.replace( propertyExpansion, "\\", "\\\\" );
322 p.load( new ByteArrayInputStream( propertyExpansion.getBytes() ) );
323 }
324
325
326
327
328 String headerLocation = request.getHeaderLocation();
329 if ( "config/maven_checks.xml".equals( request.getConfigLocation() ) )
330 {
331
332 if ( "LICENSE.txt".equals( request.getHeaderLocation() ) )
333 {
334 headerLocation = "config/maven-header.txt";
335 }
336 }
337 if ( getLogger().isDebugEnabled() )
338 {
339 getLogger().debug( "headerLocation " + headerLocation );
340 }
341
342 if ( StringUtils.isNotEmpty( headerLocation ) )
343 {
344 try
345 {
346 File headerFile = locator.getResourceAsFile( headerLocation, "checkstyle-header.txt" );
347
348 if ( headerFile != null )
349 {
350 p.setProperty( "checkstyle.header.file", headerFile.getAbsolutePath() );
351 }
352 }
353 catch ( FileResourceCreationException e )
354 {
355 throw new CheckstyleExecutorException( "Unable to process header location: " + headerLocation, e );
356 }
357 catch ( ResourceNotFoundException e )
358 {
359 throw new CheckstyleExecutorException( "Unable to process header location: " + headerLocation, e );
360 }
361 }
362
363 if ( request.getCacheFile() != null )
364 {
365 p.setProperty( "checkstyle.cache.file", request.getCacheFile() );
366 }
367 }
368 catch ( IOException e )
369 {
370 throw new CheckstyleExecutorException( "Failed to get overriding properties", e );
371 }
372 catch ( FileResourceCreationException e )
373 {
374 throw new CheckstyleExecutorException( "Failed to get overriding properties", e );
375 }
376 catch ( ResourceNotFoundException e )
377 {
378 throw new CheckstyleExecutorException( "Failed to get overriding properties", e );
379 }
380 if ( request.getSuppressionsFileExpression() != null )
381 {
382 String suppresionFile = request.getSuppressionsLocation();
383
384 if ( suppresionFile != null )
385 {
386 p.setProperty( request.getSuppressionsFileExpression(), suppresionFile );
387 }
388 }
389
390 return p;
391 }
392
393 private File[] getFilesToProcess( CheckstyleExecutorRequest request )
394 throws IOException
395 {
396 StringBuffer excludesStr = new StringBuffer();
397
398 if ( StringUtils.isNotEmpty( request.getExcludes() ) )
399 {
400 excludesStr.append( request.getExcludes() );
401 }
402
403 String[] defaultExcludes = FileUtils.getDefaultExcludes();
404 for ( int i = 0; i < defaultExcludes.length; i++ )
405 {
406 if ( excludesStr.length() > 0 )
407 {
408 excludesStr.append( "," );
409 }
410
411 excludesStr.append( defaultExcludes[i] );
412 }
413
414 if ( request.getSourceDirectory() == null || !request.getSourceDirectory().exists() )
415 {
416 return EMPTY_FILE_ARRAY;
417 }
418
419 List<File> files =
420 FileUtils.getFiles( request.getSourceDirectory(), request.getIncludes(), excludesStr.toString() );
421 if ( request.isIncludeTestSourceDirectory() && ( request.getTestSourceDirectory() != null )
422 && ( request.getTestSourceDirectory().exists() ) && ( request.getTestSourceDirectory().isDirectory() ) )
423 {
424 files.addAll( FileUtils.getFiles( request.getTestSourceDirectory(), request.getIncludes(),
425 excludesStr.toString() ) );
426 }
427
428 return (File[]) files.toArray( EMPTY_FILE_ARRAY );
429 }
430
431 private FilterSet getSuppressions( CheckstyleExecutorRequest request )
432 throws CheckstyleExecutorException
433 {
434 try
435 {
436 File suppressionsFile = locator.resolveLocation( request.getSuppressionsLocation(),
437 "checkstyle-suppressions.xml" );
438
439 if ( suppressionsFile == null )
440 {
441 return null;
442 }
443
444 return SuppressionsLoader.loadSuppressions( suppressionsFile.getAbsolutePath() );
445 }
446 catch ( CheckstyleException ce )
447 {
448 throw new CheckstyleExecutorException( "failed to load suppressions location: "
449 + request.getSuppressionsLocation(), ce );
450 }
451 catch ( IOException e )
452 {
453 throw new CheckstyleExecutorException( "Failed to process supressions location: "
454 + request.getSuppressionsLocation(), e );
455 }
456 }
457
458 private String getConfigFile( CheckstyleExecutorRequest request )
459 throws CheckstyleExecutorException
460 {
461 try
462 {
463 if ( getLogger().isDebugEnabled() )
464 {
465 getLogger().debug( "request.getConfigLocation() " + request.getConfigLocation() );
466 }
467
468 MavenProject parent = request.getProject();
469 while ( parent != null && parent.getFile() != null )
470 {
471
472
473
474 File dir = parent.getFile().getParentFile();
475 locator.addSearchPath( FileResourceLoader.ID, dir.getAbsolutePath() );
476 parent = parent.getParent();
477 }
478 locator.addSearchPath( "url", "" );
479
480 File configFile = locator.getResourceAsFile( request.getConfigLocation(), "checkstyle-checker.xml" );
481 if ( configFile == null )
482 {
483 throw new CheckstyleExecutorException( "Unable to process config location: "
484 + request.getConfigLocation() );
485 }
486 return configFile.getAbsolutePath();
487 }
488 catch ( org.codehaus.plexus.resource.loader.ResourceNotFoundException e )
489 {
490 throw new CheckstyleExecutorException( "Unable to find configuration file at location "
491 + request.getConfigLocation(), e );
492 }
493 catch ( FileResourceCreationException e )
494 {
495 throw new CheckstyleExecutorException( "Unable to process configuration file location "
496 + request.getConfigLocation(), e );
497 }
498
499 }
500 }