29 import java.io.FileNotFoundException; |
29 import java.io.FileNotFoundException; |
30 import java.io.IOException; |
30 import java.io.IOException; |
31 import java.io.PrintWriter; |
31 import java.io.PrintWriter; |
32 import java.nio.file.Path; |
32 import java.nio.file.Path; |
33 import java.text.BreakIterator; |
33 import java.text.BreakIterator; |
|
34 import java.text.Collator; |
34 import java.util.ArrayList; |
35 import java.util.ArrayList; |
35 import java.util.Arrays; |
36 import java.util.Arrays; |
36 import java.util.Collection; |
37 import java.util.Collection; |
37 import java.util.Collections; |
38 import java.util.Collections; |
|
39 import java.util.Comparator; |
38 import java.util.List; |
40 import java.util.List; |
39 import java.util.Locale; |
41 import java.util.Locale; |
40 import java.util.Objects; |
42 import java.util.Objects; |
41 import java.util.Set; |
43 import java.util.Set; |
|
44 import java.util.stream.Collectors; |
|
45 import java.util.stream.Stream; |
42 |
46 |
43 import javax.tools.JavaFileManager; |
47 import javax.tools.JavaFileManager; |
44 import javax.tools.JavaFileObject; |
48 import javax.tools.JavaFileObject; |
45 import javax.tools.StandardJavaFileManager; |
49 import javax.tools.StandardJavaFileManager; |
46 import javax.tools.StandardLocation; |
50 import javax.tools.StandardLocation; |
166 void usage() { |
171 void usage() { |
167 usage(true); |
172 usage(true); |
168 } |
173 } |
169 |
174 |
170 void usage(boolean exit) { |
175 void usage(boolean exit) { |
171 usage("main.usage", "-help", "main.usage.foot", exit); |
176 usage("main.usage", "-help", "main.usage.foot"); |
|
177 |
|
178 if (exit) |
|
179 throw new Messager.ExitJavadoc(); |
172 } |
180 } |
173 |
181 |
174 @Override |
182 @Override |
175 void Xusage() { |
183 void Xusage() { |
176 Xusage(true); |
184 Xusage(true); |
177 } |
185 } |
178 |
186 |
179 void Xusage(boolean exit) { |
187 void Xusage(boolean exit) { |
180 usage("main.Xusage", "-X", "main.Xusage.foot", exit); |
188 usage("main.Xusage", "-X", "main.Xusage.foot"); |
181 } |
189 |
182 |
190 if (exit) |
183 private void usage(String main, String option, String foot, boolean exit) { |
191 throw new Messager.ExitJavadoc(); |
184 messager.notice(main); |
192 } |
185 // let doclet print usage information (does nothing on error) |
193 |
|
194 private void usage(String header, String option, String footer) { |
|
195 messager.notice(header); |
|
196 showToolOptions(option.equals("-X") ? OptionKind.EXTENDED : OptionKind.STANDARD); |
|
197 |
|
198 // let doclet print usage information |
186 if (docletClass != null) { |
199 if (docletClass != null) { |
187 String name = doclet.getName(); |
200 String name = doclet.getName(); |
188 Set<Option> supportedOptions = doclet.getSupportedOptions(); |
|
189 messager.notice("main.doclet.usage.header", name); |
201 messager.notice("main.doclet.usage.header", name); |
190 Option.Kind myKind = option.equals("-X") |
202 showDocletOptions(option.equals("-X") ? Option.Kind.EXTENDED : Option.Kind.STANDARD); |
191 ? Option.Kind.EXTENDED |
203 } |
192 : Option.Kind.STANDARD; |
204 |
193 supportedOptions.stream() |
205 if (footer != null) |
194 .filter(opt -> opt.getKind() == myKind) |
206 messager.notice(footer); |
195 .forEach(opt -> messager.printNotice(opt.toString())); |
207 } |
196 } |
208 |
197 if (foot != null) |
209 void showToolOptions(OptionKind kind) { |
198 messager.notice(foot); |
210 Comparator<ToolOption> comp = new Comparator<ToolOption>() { |
199 |
211 final Collator collator = Collator.getInstance(Locale.US); |
200 if (exit) |
212 { collator.setStrength(Collator.PRIMARY); } |
201 throw new Messager.ExitJavadoc(); |
213 |
|
214 @Override |
|
215 public int compare(ToolOption o1, ToolOption o2) { |
|
216 return collator.compare(o1.primaryName, o2.primaryName); |
|
217 } |
|
218 }; |
|
219 |
|
220 Stream.of(ToolOption.values()) |
|
221 .filter(opt -> opt.kind == kind) |
|
222 .sorted(comp) |
|
223 .forEach(opt -> showToolOption(opt)); |
|
224 } |
|
225 |
|
226 void showToolOption(ToolOption option) { |
|
227 List<String> names = option.getNames(); |
|
228 String parameters; |
|
229 if (option.hasArg || option.primaryName.endsWith(":")) { |
|
230 String sep = (option == ToolOption.J) || option.primaryName.endsWith(":") ? "" : " "; |
|
231 parameters = sep + option.getParameters(messager); |
|
232 } else { |
|
233 parameters = ""; |
|
234 } |
|
235 String description = option.getDescription(messager); |
|
236 showUsage(names, parameters, description); |
|
237 } |
|
238 |
|
239 void showDocletOptions(Option.Kind kind) { |
|
240 Comparator<Doclet.Option> comp = new Comparator<Doclet.Option>() { |
|
241 final Collator collator = Collator.getInstance(Locale.US); |
|
242 { collator.setStrength(Collator.PRIMARY); } |
|
243 |
|
244 @Override |
|
245 public int compare(Doclet.Option o1, Doclet.Option o2) { |
|
246 return collator.compare(o1.getName(), o2.getName()); |
|
247 } |
|
248 }; |
|
249 |
|
250 doclet.getSupportedOptions().stream() |
|
251 .filter(opt -> opt.getKind() == kind) |
|
252 .sorted(comp) |
|
253 .forEach(opt -> showDocletOption(opt)); |
|
254 } |
|
255 |
|
256 void showDocletOption(Doclet.Option option) { |
|
257 List<String> names = Arrays.asList(option.getName()); |
|
258 String parameters; |
|
259 if (option.getArgumentCount() > 0 || option.getName().endsWith(":")) { |
|
260 String sep = option.getName().endsWith(":") ? "" : " "; |
|
261 parameters = sep + option.getParameters(); |
|
262 } else { |
|
263 parameters = ""; |
|
264 } |
|
265 String description = option.getDescription(); |
|
266 showUsage(names, parameters, description); |
|
267 } |
|
268 |
|
269 // The following constants are intended to format the output to |
|
270 // be similar to that of the java launcher: i.e. "java -help". |
|
271 |
|
272 /** The indent for the option synopsis. */ |
|
273 private static final String SMALL_INDENT = " "; |
|
274 /** The automatic indent for the description. */ |
|
275 private static final String LARGE_INDENT = " "; |
|
276 /** The space allowed for the synopsis, if the description is to be shown on the same line. */ |
|
277 private static final int DEFAULT_SYNOPSIS_WIDTH = 13; |
|
278 /** The nominal maximum line length, when seeing if text will fit on a line. */ |
|
279 private static final int DEFAULT_MAX_LINE_LENGTH = 80; |
|
280 /** The format for a single-line help entry. */ |
|
281 private static final String COMPACT_FORMAT = SMALL_INDENT + "%-" + DEFAULT_SYNOPSIS_WIDTH + "s %s"; |
|
282 |
|
283 void showUsage(List<String> names, String parameters, String description) { |
|
284 String synopses = names.stream() |
|
285 .map(s -> s + parameters) |
|
286 .collect(Collectors.joining(", ")); |
|
287 // If option synopses and description fit on a single line of reasonable length, |
|
288 // display using COMPACT_FORMAT |
|
289 if (synopses.length() < DEFAULT_SYNOPSIS_WIDTH |
|
290 && !description.contains("\n") |
|
291 && (SMALL_INDENT.length() + DEFAULT_SYNOPSIS_WIDTH + 1 + description.length() <= DEFAULT_MAX_LINE_LENGTH)) { |
|
292 messager.printNotice(String.format(COMPACT_FORMAT, synopses, description)); |
|
293 return; |
|
294 } |
|
295 |
|
296 // If option synopses fit on a single line of reasonable length, show that; |
|
297 // otherwise, show 1 per line |
|
298 if (synopses.length() <= DEFAULT_MAX_LINE_LENGTH) { |
|
299 messager.printNotice(SMALL_INDENT + synopses); |
|
300 } else { |
|
301 for (String name: names) { |
|
302 messager.printNotice(SMALL_INDENT + name + parameters); |
|
303 } |
|
304 } |
|
305 |
|
306 // Finally, show the description |
|
307 messager.printNotice(LARGE_INDENT + description.replace("\n", "\n" + LARGE_INDENT)); |
202 } |
308 } |
203 |
309 |
204 |
310 |
205 /** |
311 /** |
206 * Main program - external wrapper. In order to maintain backward |
312 * Main program - external wrapper. In order to maintain backward |
431 int handleDocletOptions(int idx, List<String> args, boolean isToolOption) { |
537 int handleDocletOptions(int idx, List<String> args, boolean isToolOption) { |
432 if (docletOptions == null) { |
538 if (docletOptions == null) { |
433 docletOptions = doclet.getSupportedOptions(); |
539 docletOptions = doclet.getSupportedOptions(); |
434 } |
540 } |
435 String arg = args.get(idx); |
541 String arg = args.get(idx); |
|
542 String argBase, argVal; |
|
543 if (arg.startsWith("--") && arg.contains("=")) { |
|
544 int sep = arg.indexOf("="); |
|
545 argBase = arg.substring(0, sep); |
|
546 argVal = arg.substring(sep + 1); |
|
547 } else { |
|
548 argBase = arg; |
|
549 argVal = null; |
|
550 } |
436 |
551 |
437 for (Doclet.Option opt : docletOptions) { |
552 for (Doclet.Option opt : docletOptions) { |
438 if (opt.matches(arg)) { |
553 if (opt.matches(argBase)) { |
439 if (args.size() - idx < opt.getArgumentCount()) { |
554 if (argVal != null) { |
440 usageError("main.requires_argument", arg); |
555 switch (opt.getArgumentCount()) { |
441 } |
556 case 0: |
442 opt.process(arg, args.listIterator(idx + 1)); |
557 usageError("main.unnecessary_arg_provided", argBase); |
443 idx += opt.getArgumentCount(); |
558 break; |
|
559 case 1: |
|
560 opt.process(arg, Arrays.asList(argVal).listIterator()); |
|
561 break; |
|
562 default: |
|
563 usageError("main.only_one_argument_with_equals", argBase); |
|
564 break; |
|
565 } |
|
566 } else { |
|
567 if (args.size() - idx -1 < opt.getArgumentCount()) { |
|
568 usageError("main.requires_argument", arg); |
|
569 } |
|
570 opt.process(arg, args.listIterator(idx + 1)); |
|
571 idx += opt.getArgumentCount(); |
|
572 } |
444 return idx; |
573 return idx; |
445 } |
574 } |
446 } |
575 } |
447 // check if arg is accepted by the tool before emitting error |
576 // check if arg is accepted by the tool before emitting error |
448 if (!isToolOption) |
577 if (!isToolOption) |
461 List<String> userTagletNames = new ArrayList<>(); |
590 List<String> userTagletNames = new ArrayList<>(); |
462 |
591 |
463 // Step 1: loop through the args, set locale early on, if found. |
592 // Step 1: loop through the args, set locale early on, if found. |
464 for (int i = 0 ; i < argv.size() ; i++) { |
593 for (int i = 0 ; i < argv.size() ; i++) { |
465 String arg = argv.get(i); |
594 String arg = argv.get(i); |
466 if (arg.equals(ToolOption.LOCALE.opt)) { |
595 if (arg.equals(ToolOption.LOCALE.primaryName)) { |
467 checkOneArg(argv, i++); |
596 checkOneArg(argv, i++); |
468 String lname = argv.get(i); |
597 String lname = argv.get(i); |
469 locale = getLocale(lname); |
598 locale = getLocale(lname); |
470 } else if (arg.equals(ToolOption.DOCLET.opt)) { |
599 } else if (arg.equals(ToolOption.DOCLET.primaryName)) { |
471 checkOneArg(argv, i++); |
600 checkOneArg(argv, i++); |
472 if (userDocletName != null) { |
601 if (userDocletName != null) { |
473 usageError("main.more_than_one_doclet_specified_0_and_1", |
602 usageError("main.more_than_one_doclet_specified_0_and_1", |
474 userDocletName, argv.get(i)); |
603 userDocletName, argv.get(i)); |
475 } |
604 } |
476 if (docletName != null) { |
605 if (docletName != null) { |
477 usageError("main.more_than_one_doclet_specified_0_and_1", |
606 usageError("main.more_than_one_doclet_specified_0_and_1", |
478 docletName, argv.get(i)); |
607 docletName, argv.get(i)); |
479 } |
608 } |
480 userDocletName = argv.get(i); |
609 userDocletName = argv.get(i); |
481 } else if (arg.equals(ToolOption.DOCLETPATH.opt)) { |
610 } else if (arg.equals(ToolOption.DOCLETPATH.primaryName)) { |
482 checkOneArg(argv, i++); |
611 checkOneArg(argv, i++); |
483 if (userDocletPath == null) { |
612 if (userDocletPath == null) { |
484 userDocletPath = argv.get(i); |
613 userDocletPath = argv.get(i); |
485 } else { |
614 } else { |
486 userDocletPath += File.pathSeparator + argv.get(i); |
615 userDocletPath += File.pathSeparator + argv.get(i); |