Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/Start.java
41161 views
1
/*
2
* Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved.
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
*
5
* This code is free software; you can redistribute it and/or modify it
6
* under the terms of the GNU General Public License version 2 only, as
7
* published by the Free Software Foundation. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
26
package jdk.javadoc.internal.tool;
27
28
import java.io.File;
29
import java.io.IOException;
30
import java.io.PrintWriter;
31
import java.text.BreakIterator;
32
import java.text.Collator;
33
import java.util.ArrayList;
34
import java.util.Collections;
35
import java.util.Comparator;
36
import java.util.IllformedLocaleException;
37
import java.util.List;
38
import java.util.Locale;
39
import java.util.Objects;
40
import java.util.Set;
41
import java.util.function.Supplier;
42
import java.util.stream.Collectors;
43
44
import javax.tools.JavaFileManager;
45
import javax.tools.JavaFileObject;
46
import javax.tools.StandardJavaFileManager;
47
48
import com.sun.tools.javac.api.JavacTrees;
49
import com.sun.tools.javac.file.BaseFileManager;
50
import com.sun.tools.javac.file.JavacFileManager;
51
import com.sun.tools.javac.jvm.Target;
52
import com.sun.tools.javac.main.Arguments;
53
import com.sun.tools.javac.main.CommandLine;
54
import com.sun.tools.javac.util.ClientCodeException;
55
import com.sun.tools.javac.util.Context;
56
import com.sun.tools.javac.util.Log;
57
import com.sun.tools.javac.util.StringUtils;
58
59
import jdk.javadoc.doclet.Doclet;
60
import jdk.javadoc.doclet.Doclet.Option;
61
import jdk.javadoc.doclet.DocletEnvironment;
62
import jdk.javadoc.doclet.StandardDoclet;
63
import jdk.javadoc.internal.Versions;
64
import jdk.javadoc.internal.tool.Main.Result;
65
import jdk.javadoc.internal.tool.ToolOptions.ToolOption;
66
67
import static javax.tools.DocumentationTool.Location.*;
68
69
import static jdk.javadoc.internal.tool.Main.Result.*;
70
71
/**
72
* Main program of Javadoc.
73
* Previously named "Main".
74
*
75
* <p><b>This is NOT part of any supported API.
76
* If you write code that depends on this, you do so at your own risk.
77
* This code and its internal interfaces are subject to change or
78
* deletion without notice.</b>
79
*/
80
public class Start {
81
82
/** Context for this invocation. */
83
private final Context context;
84
85
private static final String ProgramName = "javadoc";
86
87
private Messager messager;
88
89
private final String docletName;
90
91
private final ClassLoader classLoader;
92
93
private Class<?> docletClass;
94
95
private Doclet doclet;
96
97
// used to determine the locale for the messager
98
private Locale locale;
99
100
/**
101
* In API mode, exceptions thrown while calling the doclet are
102
* propagated using ClientCodeException.
103
*/
104
private boolean apiMode;
105
106
private JavaFileManager fileManager;
107
108
private final ToolOptions options;
109
110
Start() {
111
this(null, null, null, null, null, null);
112
}
113
114
Start(PrintWriter outWriter, PrintWriter errWriter) {
115
this(null, null, outWriter, errWriter, null, null);
116
}
117
118
Start(Context context, String programName,
119
PrintWriter outWriter, PrintWriter errWriter,
120
String docletName, ClassLoader classLoader) {
121
this.context = context == null ? new Context() : context;
122
String pname = programName == null ? ProgramName : programName;
123
this.messager = (outWriter == null && errWriter == null)
124
? new Messager(this.context, pname)
125
: new Messager(this.context, pname, outWriter, errWriter);
126
this.docletName = docletName;
127
this.classLoader = classLoader;
128
this.docletClass = null;
129
this.locale = Locale.getDefault();
130
131
options = getToolOptions();
132
}
133
134
public Start(Context context) {
135
this.docletClass = null;
136
this.context = Objects.requireNonNull(context);
137
this.apiMode = true;
138
this.docletName = null;
139
this.classLoader = null;
140
this.locale = Locale.getDefault();
141
142
Log log = context.get(Log.logKey);
143
if (log instanceof Messager m){
144
messager = m;
145
} else {
146
PrintWriter out = context.get(Log.errKey);
147
messager = (out == null)
148
? new Messager(context, ProgramName)
149
: new Messager(context, ProgramName, out, out);
150
}
151
152
options = getToolOptions();
153
}
154
155
private ToolOptions getToolOptions() {
156
ToolOptions.ShowHelper helper = new ToolOptions.ShowHelper() {
157
@Override
158
public void usage() {
159
showUsage("main.usage", ToolOption.Kind.STANDARD, "main.usage.foot");
160
}
161
162
@Override
163
public void Xusage() {
164
showUsage("main.Xusage", ToolOption.Kind.EXTENDED, "main.Xusage.foot");
165
}
166
167
@Override
168
public void version() {
169
showVersion("javadoc.version", orDefault(() -> Versions.shortVersionStringOf(toolVersion())));
170
}
171
172
@Override
173
public void fullVersion() {
174
showVersion("javadoc.fullversion", orDefault(() -> Versions.fullVersionStringOf(toolVersion())));
175
}
176
177
private String orDefault(Supplier<String> s) {
178
try {
179
return s.get();
180
} catch (RuntimeException e) {
181
assert false : e;
182
return Log.getLocalizedString("version.not.available");
183
}
184
}
185
};
186
return new ToolOptions(context, messager, helper);
187
}
188
189
private Runtime.Version toolVersion() {
190
return Versions.javadocVersion();
191
}
192
193
private void showUsage() {
194
showUsage("main.usage", ToolOption.Kind.STANDARD, "main.usage.foot");
195
}
196
197
private void showUsage(String headerKey, ToolOption.Kind kind, String footerKey) {
198
messager.noticeUsingKey(headerKey);
199
showToolOptions(kind);
200
201
// let doclet print usage information
202
if (docletClass != null) {
203
showDocletOptions(kind == ToolOption.Kind.EXTENDED
204
? Option.Kind.EXTENDED
205
: Option.Kind.STANDARD);
206
}
207
if (footerKey != null)
208
messager.noticeUsingKey(footerKey);
209
}
210
211
private void showVersion(String labelKey, String value) {
212
messager.noticeUsingKey(labelKey, messager.programName, value);
213
}
214
215
private void showToolOptions(ToolOption.Kind kind) {
216
Comparator<ToolOption> comp = new Comparator<ToolOption>() {
217
final Collator collator = Collator.getInstance(Locale.US);
218
{ collator.setStrength(Collator.PRIMARY); }
219
220
@Override
221
public int compare(ToolOption o1, ToolOption o2) {
222
return collator.compare(o1.primaryName, o2.primaryName);
223
}
224
};
225
226
options.getSupportedOptions().stream()
227
.filter(opt -> opt.kind == kind)
228
.sorted(comp)
229
.forEach(this::showToolOption);
230
}
231
232
private void showToolOption(ToolOption option) {
233
List<String> names = option.getNames();
234
String primaryName = option.primaryName;
235
String parameters;
236
if (option.hasArg || primaryName.endsWith(":")) {
237
String sep = primaryName.endsWith(":")
238
|| primaryName.equals(ToolOptions.AT)
239
|| primaryName.equals(ToolOptions.J)
240
? "" : " ";
241
parameters = sep + option.getParameters(messager);
242
} else {
243
parameters = "";
244
}
245
String description = option.getDescription(messager);
246
showOption(names, parameters, description);
247
}
248
249
private void showDocletOptions(Option.Kind kind) {
250
String name = doclet.getName();
251
Set<? extends Option> options = getSupportedOptionsOf(doclet);
252
if (options.isEmpty()) {
253
return;
254
}
255
messager.noticeUsingKey("main.doclet.usage.header", name);
256
257
Comparator<Doclet.Option> comp = new Comparator<Doclet.Option>() {
258
final Collator collator = Collator.getInstance(Locale.US);
259
{ collator.setStrength(Collator.PRIMARY); }
260
261
@Override
262
public int compare(Doclet.Option o1, Doclet.Option o2) {
263
return collator.compare(o1.getNames().get(0), o2.getNames().get(0));
264
}
265
};
266
267
options.stream()
268
.filter(opt -> opt.getKind() == kind)
269
.sorted(comp)
270
.forEach(this::showDocletOption);
271
}
272
273
private void showDocletOption(Doclet.Option option) {
274
List<String> names = option.getNames();
275
String parameters;
276
String primaryName = names.get(0);
277
if (option.getArgumentCount() > 0 || primaryName.endsWith(":")) {
278
String sep = primaryName.endsWith(":") ? "" : " ";
279
parameters = sep + option.getParameters();
280
} else {
281
parameters = "";
282
}
283
String description = option.getDescription();
284
showOption(names, parameters, description);
285
}
286
287
// The following constants are intended to format the output to
288
// be similar to that of the java launcher: i.e. "java -help".
289
290
/** The indent for the option synopsis. */
291
private static final String SMALL_INDENT = " ".repeat(4);
292
/** The automatic indent for the description. */
293
private static final String LARGE_INDENT = " ".repeat(18);
294
/** The space allowed for the synopsis, if the description is to be shown on the same line. */
295
private static final int DEFAULT_SYNOPSIS_WIDTH = 13;
296
/** The nominal maximum line length, when seeing if text will fit on a line. */
297
private static final int DEFAULT_MAX_LINE_LENGTH = 80;
298
/** The format for a single-line help entry. */
299
private static final String COMPACT_FORMAT = SMALL_INDENT + "%-" + DEFAULT_SYNOPSIS_WIDTH + "s %s";
300
301
void showOption(List<String> names, String parameters, String description) {
302
String synopses = names.stream()
303
.map(s -> s + parameters)
304
.collect(Collectors.joining(", "));
305
// If option synopses and description fit on a single line of reasonable length,
306
// display using COMPACT_FORMAT
307
if (synopses.length() < DEFAULT_SYNOPSIS_WIDTH
308
&& !description.contains("\n")
309
&& (SMALL_INDENT.length() + DEFAULT_SYNOPSIS_WIDTH + 1 + description.length() <= DEFAULT_MAX_LINE_LENGTH)) {
310
messager.notice(String.format(COMPACT_FORMAT, synopses, description));
311
return;
312
}
313
314
// If option synopses fit on a single line of reasonable length, show that;
315
// otherwise, show 1 per line
316
if (synopses.length() <= DEFAULT_MAX_LINE_LENGTH) {
317
messager.notice(SMALL_INDENT + synopses);
318
} else {
319
for (String name: names) {
320
messager.notice(SMALL_INDENT + name + parameters);
321
}
322
}
323
324
// Finally, show the description
325
messager.notice(LARGE_INDENT + description.replace("\n", "\n" + LARGE_INDENT));
326
}
327
328
329
/**
330
* Main program - external wrapper.
331
*/
332
@SuppressWarnings("deprecation")
333
Result begin(String... argv) {
334
// Preprocess @file arguments
335
List<String> allArgs;
336
try {
337
allArgs = CommandLine.parse(List.of(argv));
338
} catch (IOException e) {
339
error("main.cant.read", e.getMessage());
340
return ERROR;
341
}
342
return begin(allArgs, Collections.emptySet());
343
}
344
345
// Called by the JSR 199 API
346
public boolean begin(Class<?> docletClass,
347
Iterable<String> options,
348
Iterable<? extends JavaFileObject> fileObjects)
349
{
350
this.docletClass = docletClass;
351
List<String> opts = new ArrayList<>();
352
for (String opt: options)
353
opts.add(opt);
354
355
return begin(opts, fileObjects).isOK();
356
}
357
358
private Result begin(List<String> options, Iterable<? extends JavaFileObject> fileObjects) {
359
fileManager = context.get(JavaFileManager.class);
360
if (fileManager == null) {
361
JavacFileManager.preRegister(context);
362
fileManager = context.get(JavaFileManager.class);
363
if (fileManager instanceof BaseFileManager bfm) {
364
bfm.autoClose = true;
365
}
366
}
367
368
// Perform an initial scan of the options to determine the doclet to be used (if any),
369
// so that it may participate in the main round of option processing.
370
try {
371
doclet = preprocess(options);
372
} catch (ToolException te) {
373
if (!te.result.isOK()) {
374
if (te.message != null) {
375
messager.printError(te.message);
376
}
377
Throwable t = te.getCause();
378
dumpStack(t == null ? te : t);
379
}
380
return te.result;
381
} catch (OptionException oe) {
382
if (oe.message != null) {
383
messager.printError(oe.message);
384
}
385
oe.m.run();
386
Throwable t = oe.getCause();
387
dumpStack(t == null ? oe : t);
388
return oe.result;
389
}
390
391
Result result = OK;
392
try {
393
result = parseAndExecute(options, fileObjects);
394
} catch (com.sun.tools.javac.main.Option.InvalidValueException e) {
395
// The detail message from javac already includes a localized "error: " prefix,
396
// so print the message directly.
397
// It would be even better to rethrow this as IllegalArgumentException
398
// when invoked via the API.
399
// See javac Arguments.error(InvalidValueException) for an example
400
messager.printRawLines(e.getMessage());
401
Throwable t = e.getCause();
402
dumpStack(t == null ? e : t);
403
return ERROR;
404
} catch (OptionException oe) {
405
// It would be even better to rethrow this as IllegalArgumentException
406
// when invoked via the API.
407
// See javac Arguments.error(InvalidValueException) for an example
408
if (oe.message != null)
409
messager.printError(oe.message);
410
411
oe.m.run();
412
Throwable t = oe.getCause();
413
dumpStack(t == null ? oe : t);
414
return oe.result;
415
} catch (ToolException exc) {
416
if (exc.message != null) {
417
messager.printError(exc.message);
418
}
419
Throwable t = exc.getCause();
420
if (result == ABNORMAL) {
421
reportInternalError(t == null ? exc : t);
422
} else {
423
dumpStack(t == null ? exc : t);
424
}
425
return exc.result;
426
} catch (OutOfMemoryError ee) {
427
error("main.out.of.memory");
428
result = SYSERR;
429
dumpStack(ee);
430
} catch (ClientCodeException e) {
431
// simply rethrow these exceptions, to be caught and handled by JavadocTaskImpl
432
throw e;
433
} catch (Error | Exception ee) {
434
error("main.fatal.error", ee);
435
reportInternalError(ee);
436
result = ABNORMAL;
437
} finally {
438
if (fileManager instanceof BaseFileManager bfm
439
&& bfm.autoClose) {
440
try {
441
fileManager.close();
442
} catch (IOException ignore) {}
443
}
444
if (this.options.rejectWarnings() && messager.hasWarnings()) {
445
error("main.warnings.Werror");
446
}
447
boolean haveErrors = messager.hasErrors();
448
if (!result.isOK() && !haveErrors) {
449
// the doclet failed, but nothing reported, flag it!.
450
error("main.unknown.error");
451
}
452
if (haveErrors && result.isOK()) {
453
result = ERROR;
454
}
455
messager.printErrorWarningCounts();
456
messager.flush();
457
}
458
return result;
459
}
460
461
private void reportInternalError(Throwable t) {
462
messager.printErrorUsingKey("doclet.internal.report.bug");
463
dumpStack(true, t);
464
}
465
466
private void dumpStack(Throwable t) {
467
dumpStack(false, t);
468
}
469
470
private void dumpStack(boolean enabled, Throwable t) {
471
if (t != null && (enabled || options.dumpOnError())) {
472
t.printStackTrace(System.err);
473
}
474
}
475
476
/**
477
* Main program - internal
478
*/
479
private Result parseAndExecute(List<String> argList, Iterable<? extends JavaFileObject> fileObjects)
480
throws ToolException, OptionException, com.sun.tools.javac.main.Option.InvalidValueException
481
{
482
final long startNanos = System.nanoTime();
483
484
List<String> javaNames = new ArrayList<>();
485
486
// Make sure no obsolete source/target messages are reported
487
try {
488
options.processCompilerOption(com.sun.tools.javac.main.Option.XLINT_CUSTOM, "-Xlint:-options");
489
} catch (com.sun.tools.javac.main.Option.InvalidValueException ignore) {
490
}
491
492
Arguments arguments = Arguments.instance(context);
493
arguments.init(ProgramName);
494
arguments.allowEmpty();
495
496
doclet.init(locale, messager);
497
int beforeCount = messager.nerrors;
498
boolean success = parseArgs(argList, javaNames);
499
int afterCount = messager.nerrors;
500
if (!success && beforeCount == afterCount) { // if there were failures but they have not been reported
501
return CMDERR;
502
}
503
504
if (!arguments.handleReleaseOptions(extra -> true)) {
505
// Arguments does not always increase the error count in the
506
// case of errors, so increment the error count only if it has
507
// not been updated previously, preventing complaints by callers
508
if (!messager.hasErrors() && !messager.hasWarnings())
509
messager.nerrors++;
510
return CMDERR;
511
}
512
513
if (!arguments.validate()) {
514
// Arguments does not always increase the error count in the
515
// case of errors, so increment the error count only if it has
516
// not been updated previously, preventing complaints by callers
517
if (!messager.hasErrors() && !messager.hasWarnings())
518
messager.nerrors++;
519
return CMDERR;
520
}
521
522
if (fileManager instanceof BaseFileManager bfm) {
523
bfm.handleOptions(options.fileManagerOptions());
524
}
525
526
String mr = com.sun.tools.javac.main.Option.MULTIRELEASE.primaryName;
527
if (fileManager.isSupportedOption(mr) == 1) {
528
Target target = Target.instance(context);
529
List<String> list = List.of(target.multiReleaseValue());
530
fileManager.handleOption(mr, list.iterator());
531
}
532
options.compilerOptions().notifyListeners();
533
534
if (options.modules().isEmpty()) {
535
if (options.subpackages().isEmpty()) {
536
if (javaNames.isEmpty() && isEmpty(fileObjects)) {
537
String text = messager.getText("main.No_modules_packages_or_classes_specified");
538
throw new ToolException(CMDERR, text);
539
}
540
}
541
}
542
543
JavadocTool comp = JavadocTool.make0(context);
544
if (comp == null) return ABNORMAL;
545
546
DocletEnvironment docEnv = comp.getEnvironment(options, javaNames, fileObjects);
547
548
// release resources
549
comp = null;
550
551
if (options.breakIterator() || !locale.getLanguage().equals(Locale.ENGLISH.getLanguage())) {
552
JavacTrees trees = JavacTrees.instance(context);
553
trees.setBreakIterator(BreakIterator.getSentenceInstance(locale));
554
}
555
// pass off control to the doclet
556
Result returnStatus = docEnv != null && doclet.run(docEnv)
557
? OK
558
: ERROR;
559
560
// We're done.
561
if (options.verbose()) {
562
long elapsedMillis = (System.nanoTime() - startNanos) / 1_000_000;
563
messager.noticeUsingKey("main.done_in", Long.toString(elapsedMillis));
564
}
565
566
return returnStatus;
567
}
568
569
boolean matches(List<String> names, String arg) {
570
for (String name : names) {
571
if (StringUtils.toLowerCase(name).equals(StringUtils.toLowerCase(arg)))
572
return true;
573
}
574
return false;
575
}
576
577
boolean matches(Doclet.Option option, String arg) {
578
if (matches(option.getNames(), arg))
579
return true;
580
int sep = arg.indexOf(':');
581
String targ = arg.substring(0, sep + 1);
582
return matches(option.getNames(), targ);
583
}
584
585
private Set<? extends Doclet.Option> docletOptions = null;
586
587
/*
588
* Consumes an option along with its arguments. Returns an advanced index
589
* modulo the sign. If the value is negative, it means there was a failure
590
* processing one or more options.
591
*/
592
int consumeDocletOption(int idx, List<String> args, boolean isToolOption) throws OptionException {
593
if (docletOptions == null) {
594
docletOptions = getSupportedOptionsOf(doclet);
595
}
596
String arg = args.get(idx);
597
String argBase, argVal;
598
if (arg.startsWith("--") && arg.contains("=")) {
599
int sep = arg.indexOf("=");
600
argBase = arg.substring(0, sep);
601
argVal = arg.substring(sep + 1);
602
} else {
603
argBase = arg;
604
argVal = null;
605
}
606
int m = 1;
607
String text = null;
608
for (Doclet.Option opt : docletOptions) {
609
if (matches(opt, argBase)) {
610
if (argVal != null) {
611
switch (opt.getArgumentCount()) {
612
case 0:
613
text = messager.getText("main.unnecessary_arg_provided", argBase);
614
throw new OptionException(ERROR, this::showUsage, text);
615
case 1:
616
if (!opt.process(arg, Collections.singletonList(argVal))) {
617
m = -1;
618
}
619
break;
620
default:
621
text = messager.getText("main.only_one_argument_with_equals", argBase);
622
throw new OptionException(ERROR, this::showUsage, text);
623
}
624
} else {
625
if (args.size() - idx - 1 < opt.getArgumentCount()) {
626
text = messager.getText("main.requires_argument", arg);
627
throw new OptionException(ERROR, this::showUsage, text);
628
}
629
if (!opt.process(arg, args.subList(idx + 1, idx + 1 + opt.getArgumentCount()))) {
630
m = -1;
631
}
632
idx += opt.getArgumentCount();
633
}
634
return m * idx;
635
}
636
}
637
// check if arg is accepted by the tool before emitting error
638
if (!isToolOption) {
639
text = messager.getText("main.invalid_flag", arg);
640
throw new OptionException(ERROR, this::showUsage, text);
641
}
642
return m * idx;
643
}
644
645
private static Set<? extends Option> getSupportedOptionsOf(Doclet doclet) {
646
Set<? extends Option> options = doclet.getSupportedOptions();
647
return options == null ? Set.of() : options;
648
}
649
650
/**
651
* Performs an initial pass over the options, primarily to determine
652
* the doclet to be used (if any), so that it may participate in the
653
* main round of option decoding. This avoids having to specify that
654
* the options to specify the doclet should appear before any options
655
* that are handled by the doclet.
656
*
657
* The downside of this initial phase is that we have to skip over
658
* unknown options, and assume that we can reliably detect the options
659
* we need to handle.
660
*
661
* @param argv the arguments to be processed
662
* @return the doclet
663
* @throws ToolException if an error occurs initializing the doclet
664
* @throws OptionException if an error occurs while processing an option
665
*/
666
private Doclet preprocess(List<String> argv) throws ToolException, OptionException {
667
// doclet specifying arguments
668
String userDocletPath = null;
669
String userDocletName = null;
670
671
// Step 1: loop through the args, set locale early on, if found.
672
for (int i = 0; i < argv.size(); i++) {
673
String arg = argv.get(i);
674
if (arg.equals(ToolOptions.DUMP_ON_ERROR)) {
675
// although this option is not needed in order to initialize the doclet,
676
// it is helpful if it is set before trying to initialize the doclet
677
options.setDumpOnError(true);
678
} else if (arg.equals(ToolOptions.LOCALE)) {
679
checkOneArg(argv, i++);
680
String lname = argv.get(i);
681
locale = getLocale(lname);
682
} else if (arg.equals(ToolOptions.DOCLET)) {
683
checkOneArg(argv, i++);
684
if (userDocletName != null) {
685
if (apiMode) {
686
throw new IllegalArgumentException("More than one doclet specified (" +
687
userDocletName + " and " + argv.get(i) + ").");
688
}
689
String text = messager.getText("main.more_than_one_doclet_specified_0_and_1",
690
userDocletName, argv.get(i));
691
throw new ToolException(CMDERR, text);
692
}
693
if (docletName != null) {
694
if (apiMode) {
695
throw new IllegalArgumentException("More than one doclet specified (" +
696
docletName + " and " + argv.get(i) + ").");
697
}
698
String text = messager.getText("main.more_than_one_doclet_specified_0_and_1",
699
docletName, argv.get(i));
700
throw new ToolException(CMDERR, text);
701
}
702
userDocletName = argv.get(i);
703
} else if (arg.equals(ToolOptions.DOCLET_PATH)) {
704
checkOneArg(argv, i++);
705
if (userDocletPath == null) {
706
userDocletPath = argv.get(i);
707
} else {
708
userDocletPath += File.pathSeparator + argv.get(i);
709
}
710
}
711
}
712
713
// Step 3: doclet name specified ? if so find a ClassLoader,
714
// and load it.
715
if (docletClass == null) {
716
if (userDocletName != null) {
717
ClassLoader cl = classLoader;
718
if (cl == null) {
719
if (!fileManager.hasLocation(DOCLET_PATH)) {
720
List<File> paths = new ArrayList<>();
721
if (userDocletPath != null) {
722
for (String pathname : userDocletPath.split(File.pathSeparator)) {
723
paths.add(new File(pathname));
724
}
725
}
726
try {
727
((StandardJavaFileManager)fileManager).setLocation(DOCLET_PATH, paths);
728
} catch (IOException ioe) {
729
if (apiMode) {
730
throw new IllegalArgumentException("Could not set location for " +
731
userDocletPath, ioe);
732
}
733
String text = messager.getText("main.doclet_could_not_set_location",
734
userDocletPath);
735
throw new ToolException(CMDERR, text, ioe);
736
}
737
}
738
cl = fileManager.getClassLoader(DOCLET_PATH);
739
if (cl == null) {
740
// despite doclet specified on cmdline no classloader found!
741
if (apiMode) {
742
throw new IllegalArgumentException("Could not obtain classloader to load "
743
744
+ userDocletPath);
745
}
746
String text = messager.getText("main.doclet_no_classloader_found",
747
userDocletName);
748
throw new ToolException(CMDERR, text);
749
}
750
}
751
docletClass = loadDocletClass(userDocletName, cl);
752
} else if (docletName != null){
753
docletClass = loadDocletClass(docletName, getClass().getClassLoader());
754
} else {
755
docletClass = StandardDoclet.class;
756
}
757
}
758
759
if (Doclet.class.isAssignableFrom(docletClass)) {
760
messager.setLocale(Locale.getDefault()); // use default locale for console messages
761
try {
762
Object o = docletClass.getConstructor().newInstance();
763
doclet = (Doclet) o;
764
} catch (ReflectiveOperationException exc) {
765
if (apiMode) {
766
throw new ClientCodeException(exc);
767
}
768
String text = messager.getText("main.could_not_instantiate_class", docletClass.getName());
769
throw new ToolException(ERROR, text);
770
}
771
} else {
772
String text = messager.getText("main.not_a_doclet", docletClass.getName());
773
throw new ToolException(ERROR, text);
774
}
775
return doclet;
776
}
777
778
private Class<?> loadDocletClass(String docletName, ClassLoader classLoader) throws ToolException {
779
try {
780
return classLoader == null ? Class.forName(docletName) : classLoader.loadClass(docletName);
781
} catch (ClassNotFoundException cnfe) {
782
if (apiMode) {
783
throw new IllegalArgumentException("Cannot find doclet class " + docletName);
784
}
785
String text = messager.getText("main.doclet_class_not_found", docletName);
786
throw new ToolException(CMDERR, text, cnfe);
787
}
788
}
789
790
private boolean parseArgs(List<String> args, List<String> javaNames)
791
throws OptionException, com.sun.tools.javac.main.Option.InvalidValueException
792
{
793
boolean success = true;
794
for (int i = 0; i < args.size(); i++) {
795
String arg = args.get(i);
796
ToolOption o = options.getOption(arg);
797
if (o != null) {
798
// handle a doclet argument that may be needed however
799
// don't increment the index, and allow the tool to consume args
800
if (consumeDocletOption(i, args, true) < 0) {
801
success = false;
802
}
803
if (o.hasArg) {
804
if (arg.startsWith("--") && arg.contains("=")) {
805
o.process(arg.substring(arg.indexOf('=') + 1));
806
} else {
807
checkOneArg(args, i++);
808
o.process(args.get(i));
809
}
810
} else if (o.hasSuffix) {
811
o.process(arg);
812
} else {
813
o.process();
814
}
815
} else if (arg.startsWith("-XD")) {
816
// hidden javac options
817
String s = arg.substring("-XD".length());
818
int eq = s.indexOf('=');
819
String key = (eq < 0) ? s : s.substring(0, eq);
820
String value = (eq < 0) ? s : s.substring(eq + 1);
821
options.compilerOptions().put(key, value);
822
} else if (arg.startsWith("-")) {
823
i = consumeDocletOption(i, args, false);
824
if (i < 0) {
825
i = -i;
826
success = false;
827
}
828
} else {
829
javaNames.add(arg);
830
}
831
}
832
return success;
833
}
834
835
private <T> boolean isEmpty(Iterable<T> iter) {
836
return !iter.iterator().hasNext();
837
}
838
839
/**
840
* Check the one arg option.
841
* Error and exit if one argument is not provided.
842
*/
843
private void checkOneArg(List<String> args, int index) throws OptionException {
844
if ((index + 1) >= args.size() || args.get(index + 1).startsWith("-d")) {
845
String text = messager.getText("main.requires_argument", args.get(index));
846
throw new OptionException(CMDERR, this::showUsage, text);
847
}
848
}
849
850
void error(String key, Object... args) {
851
messager.printErrorUsingKey(key, args);
852
}
853
854
/**
855
* Get the locale if specified on the command line
856
* else return null and if locale option is not used
857
* then return default locale.
858
*/
859
private Locale getLocale(String localeName) throws ToolException {
860
try {
861
// Tolerate, at least for a while, the older syntax accepted by javadoc,
862
// using _ as the separator
863
localeName = localeName.replace("_", "-");
864
Locale l = new Locale.Builder().setLanguageTag(localeName).build();
865
// Ensure that a non-empty language is available for the <HTML lang=...> element
866
return (l.getLanguage().isEmpty()) ? Locale.ENGLISH : l;
867
} catch (IllformedLocaleException e) {
868
String text = messager.getText("main.malformed_locale_name", localeName);
869
throw new ToolException(CMDERR, text);
870
}
871
}
872
873
}
874
875