001 /**
002 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
003 */
004 package net.sourceforge.pmd;
005
006 import java.io.IOException;
007 import java.util.List;
008 import java.util.Map;
009 import java.util.Properties;
010
011 import net.sourceforge.pmd.lang.Language;
012 import net.sourceforge.pmd.lang.LanguageVersion;
013 import net.sourceforge.pmd.renderers.Renderer;
014 import net.sourceforge.pmd.renderers.RendererFactory;
015 import net.sourceforge.pmd.util.StringUtil;
016
017 /**
018 * Command line options parser class. Produces a Configuration instance to
019 * use with PMD processing.
020 */
021 public class CommandLineOptions {
022
023 private final static int LANGUAGE_NAME_INDEX = 1;
024 private final static int LANGUAGE_VERSION_INDEX = 2;
025
026 private final Configuration configuration = new Configuration();
027
028 private String[] args;
029 private int optionEndIndex;
030
031 public CommandLineOptions(String[] args) {
032
033 this.args = args;
034
035 if (args == null || args.length < 3) {
036 throw new IllegalArgumentException(usage());
037 }
038 int mandatoryIndex = 0;
039 int optionStartIndex = 3;
040 optionEndIndex = args.length;
041 if (args[0].charAt(0) == '-') {
042 mandatoryIndex = args.length - 3;
043 optionStartIndex = 0;
044 optionEndIndex = args.length - 3;
045 }
046
047 configuration.setInputPaths(args[mandatoryIndex]);
048 configuration.setReportFormat(args[mandatoryIndex + 1]);
049 if (StringUtil.isEmpty(configuration.getReportFormat())) {
050 throw new IllegalArgumentException("Report renderer is required.");
051 }
052 configuration.setRuleSets(StringUtil
053 .asString(RuleSetReferenceId.parse(args[mandatoryIndex + 2]).toArray(), ","));
054
055 for (int optionsIndex = optionStartIndex; optionsIndex < optionEndIndex; optionsIndex++) {
056 String opt = args[optionsIndex];
057 if ("-debug".equals(opt)) {
058 configuration.setDebug(true);
059 } else if ("-stress".equals(opt)) {
060 configuration.setStressTest(true);
061 } else if ("-shortnames".equals(opt)) {
062 configuration.setReportShortNames(true);
063 } else if ("-encoding".equals(opt)) {
064 checkOption(opt, optionsIndex, 1);
065 configuration.setSourceEncoding(args[++optionsIndex]);
066 } else if ("-threads".equals(opt)) {
067 checkOption(opt, optionsIndex, 1);
068 configuration.setThreads(parseInt(opt, args[++optionsIndex]));
069 } else if ("-suppressmarker".equals(opt)) {
070 checkOption(opt, optionsIndex, 1);
071 configuration.setSuppressMarker(args[++optionsIndex]);
072 } else if ("-version".equals(opt)) {
073 checkOption(opt, optionsIndex, 2);
074 configuration.setDefaultLanguageVersion(parseLanguageVersion(optionsIndex));
075 optionsIndex += 2;
076 } else if ("-minimumpriority".equals(opt)) {
077 checkOption(opt, optionsIndex, 1);
078 configuration.setMinimumPriority(parseMinimunPriority(args[++optionsIndex]));
079 } else if ("-showsuppressed".equals(opt)) {
080 configuration.setShowSuppressedViolations(true);
081 } else if ("-property".equals(opt)) {
082 checkOption(opt, optionsIndex, 2);
083 configuration.getReportProperties().put(args[++optionsIndex], args[++optionsIndex]);
084 } else if ("-reportfile".equals(opt)) {
085 checkOption(opt, optionsIndex, 1);
086 configuration.setReportFile(args[++optionsIndex]);
087 } else if ("-benchmark".equals(opt)) {
088 configuration.setBenchmark(true);
089 } else if ("-auxclasspath".equals(opt)) {
090 checkOption(opt, optionsIndex, 1);
091 try {
092 configuration.prependClasspath(args[++optionsIndex]);
093 } catch (IOException e) {
094 throw new IllegalArgumentException("Invalid auxiliary classpath: " + e.getMessage(), e);
095 }
096 } else {
097 throw new IllegalArgumentException("Unexpected command line argument: " + opt);
098 }
099 }
100 }
101
102 private void checkOption(String opt, int index, int count) {
103 boolean valid = true;
104 if (index + count >= optionEndIndex) {
105 valid = false;
106 } else {
107 for (int i = 1; i <= count; i++) {
108 if (args[index + i].charAt(0) == '-') {
109 valid = false;
110 break;
111 }
112 }
113 }
114 if (!valid) {
115 throw new IllegalArgumentException(opt + " requires " + count + " parameters.\n\n" + usage());
116 }
117 }
118
119 private RulePriority parseMinimunPriority(String priority) {
120 try {
121 return RulePriority.valueOf(Integer.parseInt(priority));
122 } catch (NumberFormatException e) {
123 throw new IllegalArgumentException("Minimum priority must be a whole number between " + RulePriority.HIGH
124 + " and " + RulePriority.LOW + ", " + priority + " received", e);
125 }
126 }
127
128 private int parseInt(String opt, String s) {
129 try {
130 return Integer.parseInt(s);
131 } catch (NumberFormatException e) {
132 throw new IllegalArgumentException(opt + " parameter must be a whole number, " + s + " received");
133 }
134 }
135
136 private LanguageVersion parseLanguageVersion(int optionsIndex) {
137 String languageName = args[optionsIndex + LANGUAGE_NAME_INDEX];
138 Language language = Language.findByTerseName(languageName);
139 if (language == null) {
140 throw new IllegalArgumentException("Unknown Language '" + languageName + "'. Available Languages are : "
141 + Language.commaSeparatedTerseNames(Language.findWithRuleSupport()));
142 } else {
143 if (args.length > (optionsIndex + LANGUAGE_VERSION_INDEX)) {
144 String version = args[optionsIndex + LANGUAGE_VERSION_INDEX];
145 List<LanguageVersion> languageVersions = LanguageVersion.findVersionsForLanguageTerseName(language
146 .getTerseName());
147 // If there is versions for this language, it should be a valid one...
148 if (!languageVersions.isEmpty()) {
149 for (LanguageVersion languageVersion : languageVersions) {
150 if (version.equals(languageVersion.getVersion())) {
151 return languageVersion;
152 }
153 }
154 throw new IllegalArgumentException("Language version '" + version
155 + "' is not availaible for Language '" + language.getName()
156 + "'.\nAvailable versions are :"
157 + LanguageVersion.commaSeparatedTerseNames(languageVersions));
158 }
159 }
160 return language.getDefaultVersion();
161 }
162 }
163
164 public Configuration getConfiguration() {
165 return configuration;
166 }
167
168 public static String usage() {
169 return PMD.EOL + PMD.EOL +
170 "Mandatory arguments:" + PMD.EOL +
171 "1) A java source code filename or directory" + PMD.EOL +
172 "2) A report format " + PMD.EOL +
173 "3) A ruleset filename or a comma-delimited string of ruleset filenames" + PMD.EOL +
174 PMD.EOL +
175 "For example: " + PMD.EOL +
176 "c:\\> java -jar pmd-" + PMD.VERSION + ".jar c:\\my\\source\\code html unusedcode" + PMD.EOL +
177 PMD.EOL +
178 "Optional arguments that may be put before or after the mandatory arguments: " + PMD.EOL +
179 "-version {name} {version}: specify version of a language PMD should use" + PMD.EOL +
180 "-debug: prints debugging information" + PMD.EOL +
181 "-threads: specifies the number of threads to create" + PMD.EOL +
182 "-encoding: specifies the character set encoding of the source code files PMD is reading (i.e., UTF-8)" + PMD.EOL +
183 "-suppressmarker: specifies the String that marks the a line which PMD should ignore; default is NOPMD" + PMD.EOL +
184 "-shortnames: prints shortened filenames in the report" + PMD.EOL +
185 "-minimumpriority: rule priority threshold; rules with lower priority than they will not be used" + PMD.EOL +
186 "-showsuppressed: report should show suppressed rule violations" + PMD.EOL +
187 "-property {name} {value}: define a property for the report" + PMD.EOL +
188 "-reportfile: send report output to a file; default to System.out" + PMD.EOL +
189 "-benchmark: output a benchmark report upon completion; default to System.err" + PMD.EOL +
190 "-auxclasspath: specifies the classpath for libraries used by the source code (used by type resolution)" + PMD.EOL +
191 " (alternatively, a 'file://' URL to a text file containing path elements on consecutive lines)" + PMD.EOL +
192 PMD.EOL +
193 "Available report formats and their configuration properties are:" + PMD.EOL +
194 getReports() +
195 PMD.EOL +
196 "For example on windows: " + PMD.EOL +
197 "c:\\> java -jar pmd-" + PMD.VERSION + ".jar c:\\my\\source\\code text unusedcode,imports -version java 1.5 -debug" + PMD.EOL +
198 "c:\\> java -jar pmd-" + PMD.VERSION + ".jar c:\\my\\source\\code xml basic,design -encoding UTF-8" + PMD.EOL +
199 "c:\\> java -jar pmd-" + PMD.VERSION + ".jar c:\\my\\source\\code html typeresolution -auxclasspath commons-collections.jar;derby.jar" + PMD.EOL +
200 "c:\\> java -jar pmd-" + PMD.VERSION + ".jar c:\\my\\source\\code html typeresolution -auxclasspath file:///C:/my/classpathfile" + PMD.EOL +
201 PMD.EOL +
202 "For example on *nix: " + PMD.EOL +
203 "$ java -jar pmd-" + PMD.VERSION + ".jar /home/workspace/src/main/java/code nicehtml basic,design" + PMD.EOL +
204 "$ java -jar pmd-" + PMD.VERSION + ".jar /home/workspace/src/main/java/code nicehtml basic,design -xslt my-own.xsl" + PMD.EOL +
205 "$ java -jar pmd-" + PMD.VERSION + ".jar /home/workspace/src/main/java/code nicehtml typeresolution -auxclasspath commons-collections.jar:derby.jar" + PMD.EOL +
206 PMD.EOL;
207 }
208
209 private static String getReports() {
210 StringBuilder buf = new StringBuilder();
211 for (String reportName : RendererFactory.REPORT_FORMAT_TO_RENDERER.keySet()) {
212 Renderer renderer = RendererFactory.createRenderer(reportName, new Properties());
213 buf.append(" ");
214 buf.append(reportName);
215 buf.append(": ");
216 if (!reportName.equals(renderer.getName())) {
217 buf.append(" Deprecated alias for '" + renderer.getName());
218 buf.append(PMD.EOL);
219 continue;
220 }
221 buf.append(renderer.getDescription());
222 buf.append(PMD.EOL);
223 for (Map.Entry<String, String> entry : renderer.getPropertyDefinitions().entrySet()) {
224 buf.append(" ");
225 buf.append(entry.getKey());
226 buf.append(" - ");
227 buf.append(entry.getValue());
228 buf.append(PMD.EOL);
229 }
230 }
231 return buf.toString();
232 }
233 }
|