Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/jdk17u
Path: blob/master/src/jdk.javadoc/share/classes/jdk/javadoc/internal/tool/Start.java
66655 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 JavadocLog log;
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 log
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.log = (outWriter == null && errWriter == null)
124
? new JavadocLog(this.context, pname)
125
: new JavadocLog(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 JavadocLog l){
144
this.log = l;
145
} else {
146
PrintWriter out = context.get(Log.errKey);
147
this.log = (out == null)
148
? new JavadocLog(context, ProgramName)
149
: new JavadocLog(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, log, 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
log.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
log.noticeUsingKey(footerKey);
209
}
210
211
private void showVersion(String labelKey, String value) {
212
log.noticeUsingKey(labelKey, log.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(log);
242
} else {
243
parameters = "";
244
}
245
String description = option.getDescription(log);
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
log.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
log.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
log.notice(SMALL_INDENT + synopses);
318
} else {
319
for (String name: names) {
320
log.notice(SMALL_INDENT + name + parameters);
321
}
322
}
323
324
// Finally, show the description
325
log.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
log.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
log.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
log.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
log.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
log.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() && log.hasWarnings()) {
445
error("main.warnings.Werror");
446
}
447
boolean haveErrors = log.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
log.printErrorWarningCounts();
456
log.flush();
457
}
458
return result;
459
}
460
461
private void reportInternalError(Throwable t) {
462
log.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, log);
497
int beforeCount = log.nerrors;
498
boolean success = parseArgs(argList, javaNames);
499
int afterCount = log.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 (!log.hasErrors() && !log.hasWarnings())
509
log.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 (!log.hasErrors() && !log.hasWarnings())
518
log.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 = log.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
JavadocLog.printRawLines(log.getDiagnosticWriter(),
564
log.getText("main.done_in", Long.toString(elapsedMillis)));
565
}
566
567
return returnStatus;
568
}
569
570
boolean matches(List<String> names, String arg) {
571
for (String name : names) {
572
if (StringUtils.toLowerCase(name).equals(StringUtils.toLowerCase(arg)))
573
return true;
574
}
575
return false;
576
}
577
578
boolean matches(Doclet.Option option, String arg) {
579
if (matches(option.getNames(), arg))
580
return true;
581
int sep = arg.indexOf(':');
582
String targ = arg.substring(0, sep + 1);
583
return matches(option.getNames(), targ);
584
}
585
586
private Set<? extends Doclet.Option> docletOptions = null;
587
588
/*
589
* Consumes an option along with its arguments. Returns an advanced index
590
* modulo the sign. If the value is negative, it means there was a failure
591
* processing one or more options.
592
*/
593
int consumeDocletOption(int idx, List<String> args, boolean isToolOption) throws OptionException {
594
if (docletOptions == null) {
595
docletOptions = getSupportedOptionsOf(doclet);
596
}
597
String arg = args.get(idx);
598
String argBase, argVal;
599
if (arg.startsWith("--") && arg.contains("=")) {
600
int sep = arg.indexOf("=");
601
argBase = arg.substring(0, sep);
602
argVal = arg.substring(sep + 1);
603
} else {
604
argBase = arg;
605
argVal = null;
606
}
607
int m = 1;
608
String text = null;
609
for (Doclet.Option opt : docletOptions) {
610
if (matches(opt, argBase)) {
611
if (argVal != null) {
612
switch (opt.getArgumentCount()) {
613
case 0:
614
text = log.getText("main.unnecessary_arg_provided", argBase);
615
throw new OptionException(ERROR, this::showUsage, text);
616
case 1:
617
if (!opt.process(arg, Collections.singletonList(argVal))) {
618
m = -1;
619
}
620
break;
621
default:
622
text = log.getText("main.only_one_argument_with_equals", argBase);
623
throw new OptionException(ERROR, this::showUsage, text);
624
}
625
} else {
626
if (args.size() - idx - 1 < opt.getArgumentCount()) {
627
text = log.getText("main.requires_argument", arg);
628
throw new OptionException(ERROR, this::showUsage, text);
629
}
630
if (!opt.process(arg, args.subList(idx + 1, idx + 1 + opt.getArgumentCount()))) {
631
m = -1;
632
}
633
idx += opt.getArgumentCount();
634
}
635
return m * idx;
636
}
637
}
638
// check if arg is accepted by the tool before emitting error
639
if (!isToolOption) {
640
text = log.getText("main.invalid_flag", arg);
641
throw new OptionException(ERROR, this::showUsage, text);
642
}
643
return m * idx;
644
}
645
646
private static Set<? extends Option> getSupportedOptionsOf(Doclet doclet) {
647
Set<? extends Option> options = doclet.getSupportedOptions();
648
return options == null ? Set.of() : options;
649
}
650
651
/**
652
* Performs an initial pass over the options, primarily to determine
653
* the doclet to be used (if any), so that it may participate in the
654
* main round of option decoding. This avoids having to specify that
655
* the options to specify the doclet should appear before any options
656
* that are handled by the doclet.
657
*
658
* The downside of this initial phase is that we have to skip over
659
* unknown options, and assume that we can reliably detect the options
660
* we need to handle.
661
*
662
* @param argv the arguments to be processed
663
* @return the doclet
664
* @throws ToolException if an error occurs initializing the doclet
665
* @throws OptionException if an error occurs while processing an option
666
*/
667
private Doclet preprocess(List<String> argv) throws ToolException, OptionException {
668
// doclet specifying arguments
669
String userDocletPath = null;
670
String userDocletName = null;
671
672
// Step 1: loop through the args, set locale early on, if found.
673
for (int i = 0; i < argv.size(); i++) {
674
String arg = argv.get(i);
675
if (arg.equals(ToolOptions.DUMP_ON_ERROR)) {
676
// although this option is not needed in order to initialize the doclet,
677
// it is helpful if it is set before trying to initialize the doclet
678
options.setDumpOnError(true);
679
} else if (arg.equals(ToolOptions.LOCALE)) {
680
checkOneArg(argv, i++);
681
String lname = argv.get(i);
682
locale = getLocale(lname);
683
} else if (arg.equals(ToolOptions.DOCLET)) {
684
checkOneArg(argv, i++);
685
if (userDocletName != null) {
686
if (apiMode) {
687
throw new IllegalArgumentException("More than one doclet specified (" +
688
userDocletName + " and " + argv.get(i) + ").");
689
}
690
String text = log.getText("main.more_than_one_doclet_specified_0_and_1",
691
userDocletName, argv.get(i));
692
throw new ToolException(CMDERR, text);
693
}
694
if (docletName != null) {
695
if (apiMode) {
696
throw new IllegalArgumentException("More than one doclet specified (" +
697
docletName + " and " + argv.get(i) + ").");
698
}
699
String text = log.getText("main.more_than_one_doclet_specified_0_and_1",
700
docletName, argv.get(i));
701
throw new ToolException(CMDERR, text);
702
}
703
userDocletName = argv.get(i);
704
} else if (arg.equals(ToolOptions.DOCLET_PATH)) {
705
checkOneArg(argv, i++);
706
if (userDocletPath == null) {
707
userDocletPath = argv.get(i);
708
} else {
709
userDocletPath += File.pathSeparator + argv.get(i);
710
}
711
}
712
}
713
714
// Step 3: doclet name specified ? if so find a ClassLoader,
715
// and load it.
716
if (docletClass == null) {
717
if (userDocletName != null) {
718
ClassLoader cl = classLoader;
719
if (cl == null) {
720
if (!fileManager.hasLocation(DOCLET_PATH)) {
721
List<File> paths = new ArrayList<>();
722
if (userDocletPath != null) {
723
for (String pathname : userDocletPath.split(File.pathSeparator)) {
724
paths.add(new File(pathname));
725
}
726
}
727
try {
728
((StandardJavaFileManager)fileManager).setLocation(DOCLET_PATH, paths);
729
} catch (IOException ioe) {
730
if (apiMode) {
731
throw new IllegalArgumentException("Could not set location for " +
732
userDocletPath, ioe);
733
}
734
String text = log.getText("main.doclet_could_not_set_location",
735
userDocletPath);
736
throw new ToolException(CMDERR, text, ioe);
737
}
738
}
739
cl = fileManager.getClassLoader(DOCLET_PATH);
740
if (cl == null) {
741
// despite doclet specified on cmdline no classloader found!
742
if (apiMode) {
743
throw new IllegalArgumentException("Could not obtain classloader to load "
744
745
+ userDocletPath);
746
}
747
String text = log.getText("main.doclet_no_classloader_found",
748
userDocletName);
749
throw new ToolException(CMDERR, text);
750
}
751
}
752
docletClass = loadDocletClass(userDocletName, cl);
753
} else if (docletName != null){
754
docletClass = loadDocletClass(docletName, getClass().getClassLoader());
755
} else {
756
docletClass = StandardDoclet.class;
757
}
758
}
759
760
if (Doclet.class.isAssignableFrom(docletClass)) {
761
log.setLocale(Locale.getDefault()); // use default locale for console messages
762
try {
763
Object o = docletClass.getConstructor().newInstance();
764
doclet = (Doclet) o;
765
} catch (ReflectiveOperationException exc) {
766
if (apiMode) {
767
throw new ClientCodeException(exc);
768
}
769
String text = log.getText("main.could_not_instantiate_class", docletClass.getName());
770
throw new ToolException(ERROR, text);
771
}
772
} else {
773
String text = log.getText("main.not_a_doclet", docletClass.getName());
774
throw new ToolException(ERROR, text);
775
}
776
return doclet;
777
}
778
779
private Class<?> loadDocletClass(String docletName, ClassLoader classLoader) throws ToolException {
780
try {
781
return classLoader == null ? Class.forName(docletName) : classLoader.loadClass(docletName);
782
} catch (ClassNotFoundException cnfe) {
783
if (apiMode) {
784
throw new IllegalArgumentException("Cannot find doclet class " + docletName);
785
}
786
String text = log.getText("main.doclet_class_not_found", docletName);
787
throw new ToolException(CMDERR, text, cnfe);
788
}
789
}
790
791
private boolean parseArgs(List<String> args, List<String> javaNames)
792
throws OptionException, com.sun.tools.javac.main.Option.InvalidValueException
793
{
794
boolean success = true;
795
for (int i = 0; i < args.size(); i++) {
796
String arg = args.get(i);
797
ToolOption o = options.getOption(arg);
798
if (o != null) {
799
// handle a doclet argument that may be needed however
800
// don't increment the index, and allow the tool to consume args
801
if (consumeDocletOption(i, args, true) < 0) {
802
success = false;
803
}
804
if (o.hasArg) {
805
if (arg.startsWith("--") && arg.contains("=")) {
806
o.process(arg.substring(arg.indexOf('=') + 1));
807
} else {
808
checkOneArg(args, i++);
809
o.process(args.get(i));
810
}
811
} else if (o.hasSuffix) {
812
o.process(arg);
813
} else {
814
o.process();
815
}
816
} else if (arg.startsWith("-XD")) {
817
// hidden javac options
818
String s = arg.substring("-XD".length());
819
int eq = s.indexOf('=');
820
String key = (eq < 0) ? s : s.substring(0, eq);
821
String value = (eq < 0) ? s : s.substring(eq + 1);
822
options.compilerOptions().put(key, value);
823
} else if (arg.startsWith("-")) {
824
i = consumeDocletOption(i, args, false);
825
if (i < 0) {
826
i = -i;
827
success = false;
828
}
829
} else {
830
javaNames.add(arg);
831
}
832
}
833
return success;
834
}
835
836
private <T> boolean isEmpty(Iterable<T> iter) {
837
return !iter.iterator().hasNext();
838
}
839
840
/**
841
* Check the one arg option.
842
* Error and exit if one argument is not provided.
843
*/
844
private void checkOneArg(List<String> args, int index) throws OptionException {
845
if ((index + 1) >= args.size() || args.get(index + 1).startsWith("-d")) {
846
String text = log.getText("main.requires_argument", args.get(index));
847
throw new OptionException(CMDERR, this::showUsage, text);
848
}
849
}
850
851
void error(String key, Object... args) {
852
log.printErrorUsingKey(key, args);
853
}
854
855
/**
856
* Get the locale if specified on the command line
857
* else return null and if locale option is not used
858
* then return default locale.
859
*/
860
private Locale getLocale(String localeName) throws ToolException {
861
try {
862
// Tolerate, at least for a while, the older syntax accepted by javadoc,
863
// using _ as the separator
864
localeName = localeName.replace("_", "-");
865
Locale l = new Locale.Builder().setLanguageTag(localeName).build();
866
// Ensure that a non-empty language is available for the <HTML lang=...> element
867
return (l.getLanguage().isEmpty()) ? Locale.ENGLISH : l;
868
} catch (IllformedLocaleException e) {
869
String text = log.getText("main.malformed_locale_name", localeName);
870
throw new ToolException(CMDERR, text);
871
}
872
}
873
874
}
875
876