1 /***
2 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3 */
4 package net.sourceforge.pmd;
5
6 import net.sourceforge.pmd.ast.ASTCompilationUnit;
7 import net.sourceforge.pmd.ast.JavaParser;
8 import net.sourceforge.pmd.ast.ParseException;
9 import net.sourceforge.pmd.cpd.FileFinder;
10 import net.sourceforge.pmd.cpd.JavaLanguage;
11 import net.sourceforge.pmd.dfa.DataFlowFacade;
12 import net.sourceforge.pmd.renderers.Renderer;
13 import net.sourceforge.pmd.symboltable.SymbolFacade;
14 import net.sourceforge.pmd.jaxen.AttributeAxisIterator;
15
16 import java.io.BufferedInputStream;
17 import java.io.File;
18 import java.io.FileNotFoundException;
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.io.InputStreamReader;
22 import java.io.Reader;
23 import java.io.UnsupportedEncodingException;
24 import java.util.ArrayList;
25 import java.util.Enumeration;
26 import java.util.Iterator;
27 import java.util.List;
28 import java.util.StringTokenizer;
29 import java.util.zip.ZipEntry;
30 import java.util.zip.ZipFile;
31
32 public class PMD {
33
34 public static final String EOL = System.getProperty("line.separator", "\n");
35 public static final String VERSION = "3.3";
36
37 private TargetJDKVersion targetJDKVersion;
38 private String excludeMarker = ExcludeLines.EXCLUDE_MARKER;
39
40 public PMD() {
41 this(new TargetJDK1_4());
42 }
43
44 public PMD(TargetJDKVersion targetJDKVersion) {
45 this.targetJDKVersion = targetJDKVersion;
46 }
47
48 /***
49 * Processes the file read by the reader agains the rule set.
50 *
51 * @param reader input stream reader
52 * @param ruleSet set of rules to process against the file
53 * @param ctx context in which PMD is operating. This contains the Renderer and whatnot
54 * @throws PMDException if the input could not be parsed or processed
55 */
56 public void processFile(Reader reader, RuleSet ruleSet, RuleContext ctx) throws PMDException {
57 try {
58 ExcludeLines excluder = new ExcludeLines(reader, excludeMarker);
59 ctx.excludeLines(excluder.getLinesToExclude());
60
61 JavaParser parser = targetJDKVersion.createParser(excluder.getCopyReader());
62 ASTCompilationUnit c = parser.CompilationUnit();
63 Thread.yield();
64
65
66 SymbolFacade stb = new SymbolFacade();
67 stb.initializeWith(c);
68
69 if (ruleSet.usesDFA()) {
70 DataFlowFacade dff = new DataFlowFacade();
71 dff.initializeWith(c);
72 }
73
74 List acus = new ArrayList();
75 acus.add(c);
76 ruleSet.apply(acus, ctx);
77 reader.close();
78 } catch (ParseException pe) {
79 throw new PMDException("Error while parsing " + ctx.getSourceCodeFilename(), pe);
80 } catch (Exception e) {
81 throw new PMDException("Error while processing " + ctx.getSourceCodeFilename(), e);
82 }
83 }
84
85 /***
86 * Processes the input stream agains a rule set using the given input
87 * encoding.
88 *
89 * @param fileContents an input stream to analyze
90 * @param encoding input stream's encoding
91 * @param ruleSet set of rules to process against the file
92 * @param ctx context in which PMD is operating. This contains the Report and whatnot
93 * @throws PMDException if the input encoding is unsupported or the input
94 * stream could not be parsed
95 * @see #processFile(Reader, RuleSet, RuleContext)
96 */
97 public void processFile(InputStream fileContents, String encoding, RuleSet ruleSet, RuleContext ctx) throws PMDException {
98 try {
99 processFile(new InputStreamReader(fileContents, encoding), ruleSet, ctx);
100 } catch (UnsupportedEncodingException uee) {
101 throw new PMDException("Unsupported encoding exception: " + uee.getMessage());
102 }
103 }
104
105 /***
106 * Processes the input stream against a rule set assuming the platform
107 * character set.
108 *
109 * @param fileContents input stream to check
110 * @param ruleSet the set of rules to process against the source code
111 * @param ctx the context in which PMD is operating. This contains the Report and whatnot
112 * @throws PMDException if the input encoding is unsupported or the input
113 * input stream could not be parsed
114 * @see #processFile(InputStream, String, RuleSet, RuleContext)
115 */
116 public void processFile(InputStream fileContents, RuleSet ruleSet, RuleContext ctx) throws PMDException {
117 processFile(fileContents, System.getProperty("file.encoding"), ruleSet, ctx);
118 }
119
120 public void setExcludeMarker(String marker) {
121 this.excludeMarker = marker;
122 }
123
124
125 public static void main(String[] args) {
126 CommandLineOptions opts = new CommandLineOptions(args);
127
128 List files;
129 if (opts.containsCommaSeparatedFileList()) {
130 files = collectFromCommaDelimitedString(opts.getInputPath());
131 } else {
132 files = collectFilesFromOneName(opts.getInputPath());
133 }
134
135 PMD pmd;
136 if (opts.getTargetJDK().equals("1.3")) {
137 if (opts.debugEnabled()) System.out.println("In JDK 1.3 mode");
138 pmd = new PMD(new TargetJDK1_3());
139 } else if (opts.getTargetJDK().equals("1.5")) {
140 if (opts.debugEnabled()) System.out.println("In JDK 1.5 mode");
141 pmd = new PMD(new TargetJDK1_5());
142 } else {
143 if (opts.debugEnabled()) System.out.println("In JDK 1.4 mode");
144 pmd = new PMD();
145 }
146 pmd.setExcludeMarker(opts.getExcludeMarker());
147
148 RuleContext ctx = new RuleContext();
149 Report report = new Report();
150 ctx.setReport(report);
151 report.start();
152
153 try {
154 RuleSetFactory ruleSetFactory = new RuleSetFactory();
155 RuleSet rules = ruleSetFactory.createRuleSet(opts.getRulesets());
156 if (opts.debugEnabled()) {
157 for (Iterator i = rules.getRules().iterator(); i.hasNext();) {
158 Rule r = (Rule)i.next();
159 System.out.println("Loaded rule " + r.getName());
160 }
161 }
162
163 for (Iterator i = files.iterator(); i.hasNext();) {
164 DataSource dataSource = (DataSource) i.next();
165 String niceFileName = dataSource.getNiceFileName(opts.shortNamesEnabled(), opts.getInputPath());
166 ctx.setSourceCodeFilename(niceFileName);
167 if (opts.debugEnabled()) {
168 System.out.println("Processing " + ctx.getSourceCodeFilename());
169 }
170 try {
171 pmd.processFile(new BufferedInputStream(dataSource.getInputStream()), opts.getEncoding(), rules, ctx);
172 } catch (PMDException pmde) {
173 if (opts.debugEnabled()) {
174 pmde.getReason().printStackTrace();
175 }
176 ctx.getReport().addError(new Report.ProcessingError(pmde.getMessage(), niceFileName));
177 }
178 }
179 } catch (FileNotFoundException fnfe) {
180 System.out.println(opts.usage());
181 fnfe.printStackTrace();
182 } catch (RuleSetNotFoundException rsnfe) {
183 System.out.println(opts.usage());
184 rsnfe.printStackTrace();
185 } catch (IOException ioe) {
186 System.out.println(opts.usage());
187 ioe.printStackTrace();
188 }
189 report.end();
190
191 try {
192 Renderer r = opts.createRenderer();
193 System.out.println(r.render(ctx.getReport()));
194 } catch (Exception e) {
195 System.out.println(e.getMessage());
196 System.out.println(opts.usage());
197 if (opts.debugEnabled()) {
198 e.printStackTrace();
199 }
200 }
201 }
202
203 /***
204 * Collects the given file into a list.
205 *
206 * @param inputFileName a file name
207 * @return the list of files collected from the <code>inputFileName</code>
208 * @see #collect(String)
209 */
210 private static List collectFilesFromOneName(String inputFileName) {
211 return collect(inputFileName);
212 }
213
214 /***
215 * Collects the files from the given comma-separated list.
216 *
217 * @param fileList comma-separated list of filenames
218 * @return list of files collected from the <code>fileList</code>
219 */
220 private static List collectFromCommaDelimitedString(String fileList) {
221 List files = new ArrayList();
222 for (StringTokenizer st = new StringTokenizer(fileList, ","); st.hasMoreTokens();) {
223 files.addAll(collect(st.nextToken()));
224 }
225 return files;
226 }
227
228 /***
229 * Collects the files from the given <code>filename</code>.
230 *
231 * @param filename the source from which to collect files
232 * @return a list of files found at the given <code>filename</code>
233 * @throws RuntimeException if <code>filename</code> is not found
234 */
235 private static List collect(String filename) {
236 File inputFile = new File(filename);
237 if (!inputFile.exists()) {
238 throw new RuntimeException("File " + inputFile.getName() + " doesn't exist");
239 }
240 List dataSources = new ArrayList();
241 if (!inputFile.isDirectory()) {
242 if (filename.endsWith(".zip") || filename.endsWith(".jar")) {
243 ZipFile zipFile;
244 try {
245 zipFile = new ZipFile(inputFile);
246 Enumeration e = zipFile.entries();
247 while (e.hasMoreElements()) {
248 ZipEntry zipEntry = (ZipEntry) e.nextElement();
249 if (zipEntry.getName().endsWith(".java")) {
250 dataSources.add(new ZipDataSource(zipFile, zipEntry));
251 }
252 }
253 } catch (IOException ze) {
254 throw new RuntimeException("Zip file " + inputFile.getName() + " can't be opened");
255 }
256 } else {
257 dataSources.add(new FileDataSource(inputFile));
258 }
259 } else {
260 FileFinder finder = new FileFinder();
261 List files = finder.findFilesFrom(inputFile.getAbsolutePath(), new JavaLanguage.JavaFileOrDirectoryFilter(), true);
262 for (Iterator i = files.iterator(); i.hasNext();) {
263 dataSources.add(new FileDataSource((File) i.next()));
264 }
265 }
266 return dataSources;
267 }
268
269 }