View Javadoc
1   /**
2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3    */
4   package net.sourceforge.pmd.util;
5   
6   import java.io.File;
7   import java.io.FilenameFilter;
8   import java.io.IOException;
9   import java.util.ArrayList;
10  import java.util.Collections;
11  import java.util.Enumeration;
12  import java.util.List;
13  import java.util.Locale;
14  import java.util.regex.Matcher;
15  import java.util.regex.Pattern;
16  import java.util.zip.ZipEntry;
17  import java.util.zip.ZipFile;
18  
19  import net.sourceforge.pmd.util.datasource.DataSource;
20  import net.sourceforge.pmd.util.datasource.FileDataSource;
21  import net.sourceforge.pmd.util.datasource.ZipDataSource;
22  import net.sourceforge.pmd.util.filter.AndFilter;
23  import net.sourceforge.pmd.util.filter.Filter;
24  import net.sourceforge.pmd.util.filter.Filters;
25  import net.sourceforge.pmd.util.filter.OrFilter;
26  
27  /**
28   * This is a utility class for working with Files.
29   */
30  public final class FileUtil {
31  
32      private FileUtil() {}
33  
34      /**
35       * Helper method to get a filename without its extension
36       * @param fileName String
37       * @return String
38       */
39      public static String getFileNameWithoutExtension(String fileName) {
40          String name = fileName;
41  
42          int index = fileName.lastIndexOf('.');
43          if (index != -1) {
44              name = fileName.substring(0, index);
45          }
46  
47          return name;
48      }
49  
50      /**
51       * Normalizes the filename by taking the casing into account, e.g. on Windows,
52       * the filename is changed to lowercase only.
53       * @param fileName the file name
54       * @return the normalized file name
55       */
56      public static String normalizeFilename(String fileName) {
57          if (fileName != null && File.separatorChar == '\\') {
58              // windows
59              return fileName.toLowerCase(Locale.ROOT);
60          }
61          return fileName;
62      }
63  
64      /**
65       * Collects a list of DataSources using a comma separated list of input file
66       * locations to process.  If a file location is a directory, the directory
67       * hierarchy will be traversed to look for files.  If a file location is a
68       * ZIP or Jar the archive will be scanned looking for files.  If a file
69       * location is a file, it will be used.  For each located file, a
70       * FilenameFilter is used to decide whether to return a DataSource.
71       *
72       * @param fileLocations A comma-separated list of file locations.
73       * @param filenameFilter  The FilenameFilter to apply to files.
74       * @return A list of DataSources, one for each file collected.
75       */
76      public static List<DataSource> collectFiles(String fileLocations, FilenameFilter filenameFilter) {
77  	List<DataSource> dataSources = new ArrayList<>();
78  	for (String fileLocation : fileLocations.split(",")) {
79  	    collect(dataSources, fileLocation, filenameFilter);
80  	}
81  	return dataSources;
82      }
83  
84      private static List<DataSource> collect(List<DataSource> dataSources, String fileLocation,
85  	    FilenameFilter filenameFilter) {
86  	File file = new File(fileLocation);
87  	if (!file.exists()) {
88  	    throw new RuntimeException("File " + file.getName() + " doesn't exist");
89  	}
90  	if (!file.isDirectory()) {
91  	    if (fileLocation.endsWith(".zip") || fileLocation.endsWith(".jar")) {
92  		ZipFile zipFile;
93  		try {
94  		    zipFile = new ZipFile(fileLocation);
95  		    Enumeration<? extends ZipEntry> e = zipFile.entries();
96  		    while (e.hasMoreElements()) {
97  			ZipEntry zipEntry = e.nextElement();
98  			if (filenameFilter.accept(null, zipEntry.getName())) {
99  			    dataSources.add(new ZipDataSource(zipFile, zipEntry));
100 			}
101 		    }
102 		} catch (IOException ze) {
103 		    throw new RuntimeException("Archive file " + file.getName() + " can't be opened");
104 		}
105 	    } else {
106 		dataSources.add(new FileDataSource(file));
107 	    }
108 	} else {
109 	    // Match files, or directories which are not excluded.
110 	    // FUTURE Make the excluded directories be some configurable option
111 	    Filter<File> filter = new OrFilter<>(Filters.toFileFilter(filenameFilter), new AndFilter<>(Filters
112 		    .getDirectoryFilter(), Filters.toNormalizedFileFilter(Filters.buildRegexFilterExcludeOverInclude(
113 		    null, Collections.singletonList("SCCS")))));
114 	    FileFinder finder = new FileFinder();
115 	    List<File> files = finder.findFilesFrom(file, Filters.toFilenameFilter(filter), true);
116 	    for (File f : files) {
117 		dataSources.add(new FileDataSource(f));
118 	    }
119 	}
120 	return dataSources;
121     }
122 
123     /**
124      * Handy method to find a certain pattern into a file. While this method lives in the FileUtils, it was
125      * designed with with unit test in mind (to check result redirected into a file)
126      *
127      * @param file
128      * @param pattern
129      * @return
130      */
131     public static boolean findPatternInFile( final File file, final String pattern ) {
132 
133     	Pattern regexp = Pattern.compile(pattern);
134     	Matcher matcher = regexp.matcher("");
135 
136     	FileIterable it = new FileIterable(file);
137     	for ( String line : it ){
138     		matcher.reset( line ); //reset the input
139     		if ( matcher.find() ) {
140     			return true;
141     		}
142     	}
143     	return false;
144     }
145 }