001/*
002 * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.  Oracle designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Oracle in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
022 * or visit www.oracle.com if you need additional information or have any
023 * questions.
024 */
025
026package org.jdrupes.mdoclet.internal.doclets.toolkit;
027
028import java.io.ByteArrayOutputStream;
029import java.io.IOException;
030import java.io.OutputStream;
031import java.io.OutputStreamWriter;
032import java.io.UnsupportedEncodingException;
033import java.net.URI;
034import java.net.URISyntaxException;
035import java.nio.file.Path;
036import java.time.Instant;
037import java.time.ZoneOffset;
038import java.time.ZonedDateTime;
039import java.time.format.DateTimeFormatter;
040import java.time.format.DateTimeParseException;
041import java.time.temporal.ChronoUnit;
042import java.time.temporal.TemporalUnit;
043import java.util.ArrayList;
044import java.util.Arrays;
045import java.util.Calendar;
046import java.util.HashSet;
047import java.util.LinkedHashSet;
048import java.util.List;
049import java.util.Locale;
050import java.util.MissingResourceException;
051import java.util.Set;
052import java.util.StringTokenizer;
053import java.util.TreeSet;
054
055import org.jdrupes.mdoclet.internal.doclets.toolkit.util.DocletConstants;
056import org.jdrupes.mdoclet.internal.doclets.toolkit.util.Utils;
057
058import jdk.javadoc.doclet.Doclet;
059import jdk.javadoc.doclet.Reporter;
060
061import static javax.tools.Diagnostic.Kind.ERROR;
062
063/**
064 * Storage for the format-independent options supported by the toolkit.
065 * The objects to handle command-line options, and to initialize this
066 * object, are all subtypes of {@link BaseOptions.Option},
067 * returned by {@link BaseOptions#getSupportedOptions()}.
068 *
069 * <p>Some of the methods used to access the values of options
070 * have names that begin with a verb, such as {@link #copyDocfileSubdirs}
071 * or {@link #showVersion}. Unless otherwise stated,
072 * these methods should all be taken as just accessing the value
073 * of the associated option.
074 */
075public abstract class BaseOptions {
076
077    // <editor-fold desc="Option values">
078
079    /**
080     * Argument for command-line option {@code --allow-script-in-comments}.
081     * Allow JavaScript in doc comments.
082     */
083    private boolean allowScriptInComments = false;
084
085    /**
086     * Argument for command-line option {@code -docfilessubdirs}.
087     * True if we should recursively copy the doc-file subdirectories
088     */
089    private boolean copyDocfileSubdirs = false;
090
091    /**
092     * Arguments for command-line option {@code -tag} and {@code -taglet}.
093     */
094    private final LinkedHashSet<List<String>> customTagStrs
095        = new LinkedHashSet<>();
096
097    /**
098     * Argument for command-line option {@code --date}.
099     * {@code null} if option not given.
100     */
101    private ZonedDateTime date;
102
103    /**
104     * Argument for command-line option {@code -d}.
105     * Destination directory name, in which doclet will generate the entire
106     * documentation. Default is current directory.
107     */
108    private String destDirName = "";
109
110    /**
111     * Argument for command-line option {@code --disable-javafx-strict-checks}.
112     * Primarily used to disable strict checks in the regression
113     * tests allowing those tests to be executed successfully, for
114     * instance, with OpenJDK builds which may not contain FX libraries.
115     */
116    private boolean disableJavaFxStrictChecks = false;
117
118    /**
119     * Argument for command-line option {@code -docencoding}.
120     * Encoding for this document. Default is default encoding for this
121     * platform.
122     */
123    private String docEncoding = null;
124
125    /**
126     * Argument for command-line option {@code ???}.
127     * Destination directory name, in which doclet will copy the doc-files to.
128     */
129    private String docFileDestDirName = "";
130
131    /**
132     * Argument for hidden command-line option {@code --dump-on-error}.
133     */
134    private boolean dumpOnError = false;
135
136    /**
137     * Argument for command-line option {@code -encoding}.
138     * Encoding for this document. Default is default encoding for this
139     * platform.
140     */
141    private String encoding = null;
142
143    /**
144     * Argument for command-line option {@code -excludedocfilessubdir}.
145     * The set of doc-file subdirectories to exclude.
146     */
147    private Set<String> excludedDocFileDirs;
148
149    /**
150     * Argument for command-line option {@code -noqualifier}.
151     * The set of qualifiers to exclude.
152     */
153    private Set<String> excludedQualifiers;
154
155    /**
156     * Arguments for command-line option {@code -group}
157     */
158    private List<Utils.Pair<String, String>> groupPairs;
159
160    /**
161     * Argument for command-line option {@code --javafx} or {@code -javafx}.
162     * Generate documentation for JavaFX getters and setters automatically
163     * by copying it from the appropriate property definition.
164     */
165    private boolean javafx = false;
166
167    /**
168     * Argument for command-line option {@code -keywords}.
169     * True if user wants to add member names as meta keywords.
170     * Set to false because meta keywords are ignored in general
171     * by most Internet search engines.
172     */
173    private boolean keywords = false;
174
175    /**
176     * Arguments for command-line option {@code -link}.
177     */
178    // A list containing urls
179    private final List<String> linkList = new ArrayList<>();
180
181    /**
182     * Arguments for command-line option {@code -linkoffline}.
183     */
184    // A list of pairs containing urls and package list
185    private final List<Utils.Pair<String, String>> linkOfflineList
186        = new ArrayList<>();
187
188    /**
189     * An enum of policies for handling modularity mismatches in external documentation.
190     */
191    public enum ModularityMismatchPolicy {
192        INFO,
193        WARN
194    }
195
196    /**
197     * Argument for command-line option {@code --link-modularity-mismatch}.
198     * Describes how to handle external documentation with non-matching modularity.
199     */
200    private ModularityMismatchPolicy linkModularityMismatch
201        = ModularityMismatchPolicy.WARN;
202
203    /**
204     * Location of alternative platform link properties file.
205     */
206    private String linkPlatformProperties;
207
208    /**
209     * Argument for command-line option {@code -linksource}.
210     * True if we should generate browsable sources.
211     */
212    private boolean linkSource = false;
213
214    /**
215     * Argument for command-line option {@code -nocomment}.
216     * True if user wants to suppress descriptions and tags.
217     */
218    private boolean noComment = false;
219
220    /**
221     * Argument for command-line option {@code -nodeprecated}.
222     * Don't generate deprecated API information at all, if -nodeprecated
223     * option is used. <code>nodeprecated</code> is set to true if
224     * -nodeprecated option is used. Default is generate deprecated API
225     * information.
226     */
227    private boolean noDeprecated = false;
228
229    /**
230     * Argument for command-line option {@code --no-platform-links}.
231     * True if command-line option "--no-platform-links" is used. Default value is
232     * false.
233     */
234    private boolean noPlatformLinks = false;
235
236    /**
237     * Argument for command-line option {@code -nosince}.
238     * True if command-line option "-nosince" is used. Default value is
239     * false.
240     */
241    private boolean noSince = false;
242
243    /**
244     * Argument for command-line option {@code -notimestamp}.
245     * True if user wants to suppress time stamp in output.
246     * Default is false.
247     */
248    private boolean noTimestamp = false;
249
250    /**
251     * Argument for command-line option {@code -quiet}.
252     * Suppress all messages
253     */
254    private boolean quiet = false;
255
256    /**
257     * Argument for command-line option {@code -serialwarn}.
258     * This is true if option "-serialwarn" is used. Default value is false to
259     * suppress excessive warnings about serial tag.
260     */
261    private boolean serialWarn = false;
262
263    /**
264     * Argument for command-line option {@code -author}.
265     * Generate author specific information for all the classes if @author
266     * tag is used in the doc comment and if -author option is used.
267     * <code>showauthor</code> is set to true if -author option is used.
268     * Default is don't show author information.
269     */
270    private boolean showAuthor = false;
271
272    /**
273     * Argument for command-line option {@code --show-taglets}.
274     * Show taglets (internal debug switch)
275     */
276    private boolean showTaglets = false;
277
278    /**
279     * Argument for command-line option {@code -version}.
280     * Generate version specific information for the all the classes
281     * if @version tag is used in the doc comment and if -version option is
282     * used. {@code showVersion} is set to true if -version option is
283     * used. Default is don't show version information.
284     */
285    private boolean showVersion = false;
286
287    /**
288     * Argument for command line option {@code --since}.
289     * Specifies a list of release names for which to document API changes.
290     */
291    private List<String> since = List.of();
292
293    /**
294     * Argument for command line option {@code --since-label}.
295     * Specifies custom text to use as heading of New API page.
296     */
297    private String sinceLabel;
298
299    /**
300     * Argument for command-line option {@code -sourcetab}.
301     * The specified amount of space between tab stops.
302     */
303    private int sourceTabSize;
304
305    /**
306     * Argument for command-line option {@code --spec-base-url}.
307     * The base URL for relative URLs in {@code @spec} tags.
308     */
309    private URI specBaseURI;
310
311    /**
312     * Value for command-line option {@code --override-methods summary}
313     * or {@code --override-methods detail}.
314     * Specifies whether those methods that override a supertype's method
315     * with no changes to the API contract should be summarized in the
316     * footnote section.
317     */
318    private boolean summarizeOverriddenMethods = false;
319
320    /**
321     * Argument for command-line option {@code -tagletpath}.
322     * The path to Taglets
323     */
324    private String tagletPath = null;
325
326    /**
327     * Argument for command-line option {@code --snippet-path}.
328     * The path for external snippets.
329     */
330    private String snippetPath = null;
331
332    // </editor-fold>
333
334    private final BaseConfiguration config;
335
336    /**
337     * The default amount of space between tab stops.
338     */
339    public static final int DEFAULT_TAB_STOP_LENGTH = 8;
340
341    protected BaseOptions(BaseConfiguration config) {
342        this.config = config;
343
344        excludedDocFileDirs = new HashSet<>();
345        excludedQualifiers = new HashSet<>();
346        sourceTabSize = DEFAULT_TAB_STOP_LENGTH;
347        groupPairs = new ArrayList<>(0);
348    }
349
350    public Set<? extends Option> getSupportedOptions() {
351        Resources resources = config.getDocResources();
352        Messages messages = config.getMessages();
353        Reporter reporter = config.getReporter();
354
355        List<Option> options = List.of(
356            new Option(resources, "-author") {
357                @Override
358                public boolean process(String opt, List<String> args) {
359                    showAuthor = true;
360                    return true;
361                }
362            },
363
364            new Option(resources, "-d", 1) {
365                @Override
366                public boolean process(String opt, List<String> args) {
367                    destDirName = addTrailingFileSep(args.get(0));
368                    return true;
369                }
370            },
371
372            new XOption(resources, "--date", 1) {
373                // Valid --date range: within ten years of now
374                private static final ZonedDateTime now = ZonedDateTime.now();
375                static final ZonedDateTime DATE_MIN = now.minusYears(10);
376                static final ZonedDateTime DATE_MAX = now.plusYears(10);
377
378                @Override
379                public boolean process(String opt, List<String> args) {
380                    if (noTimestamp) {
381                        messages.error("doclet.Option_conflict", "--date",
382                            "-notimestamp");
383                        return false;
384                    }
385                    String arg = args.get(0);
386                    try {
387                        date = ZonedDateTime.parse(arg,
388                            DateTimeFormatter.ISO_ZONED_DATE_TIME);
389                        if (date.isBefore(DATE_MIN) || date.isAfter(DATE_MAX)) {
390                            messages.error("doclet.Option_date_out_of_range",
391                                arg);
392                            return false;
393                        }
394                        return true;
395                    } catch (DateTimeParseException x) {
396                        messages.error("doclet.Option_date_not_valid", arg);
397                        return false;
398                    }
399                }
400            },
401
402            new Option(resources, "-docencoding", 1) {
403                @Override
404                public boolean process(String opt, List<String> args) {
405                    docEncoding = args.get(0);
406                    return true;
407                }
408            },
409
410            new Option(resources, "-docfilessubdirs") {
411                @Override
412                public boolean process(String opt, List<String> args) {
413                    copyDocfileSubdirs = true;
414                    return true;
415                }
416            },
417
418            new Hidden(resources, "-encoding", 1) {
419                @Override
420                public boolean process(String opt, List<String> args) {
421                    encoding = args.get(0);
422                    return true;
423                }
424            },
425
426            new Option(resources, "-excludedocfilessubdir", 1) {
427                @Override
428                public boolean process(String opt, List<String> args) {
429                    excludedDocFileDirs
430                        .addAll(List.of(args.get(0).split("[,:]")));
431                    return true;
432                }
433            },
434
435            new Option(resources, "-group", 2) {
436                @Override
437                public boolean process(String opt, List<String> args) {
438                    groupPairs.add(new Utils.Pair<>(args.get(0), args.get(1)));
439                    return true;
440                }
441            },
442
443            new Option(resources, "--javafx -javafx") {
444                @Override
445                public boolean process(String opt, List<String> args) {
446                    javafx = true;
447                    return true;
448                }
449            },
450
451            new Option(resources, "-keywords") {
452                @Override
453                public boolean process(String opt, List<String> args) {
454                    keywords = true;
455                    return true;
456                }
457            },
458
459            new Option(resources, "-link", 1) {
460                @Override
461                public boolean process(String opt, List<String> args) {
462                    linkList.add(args.get(0));
463                    return true;
464                }
465            },
466
467            new Option(resources, "-linksource") {
468                @Override
469                public boolean process(String opt, List<String> args) {
470                    linkSource = true;
471                    return true;
472                }
473            },
474
475            new Option(resources, "-linkoffline", 2) {
476                @Override
477                public boolean process(String opt, List<String> args) {
478                    linkOfflineList
479                        .add(new Utils.Pair<>(args.get(0), args.get(1)));
480                    return true;
481                }
482            },
483
484            new Option(resources, "--link-modularity-mismatch", 1) {
485                @Override
486                public boolean process(String opt, List<String> args) {
487                    String s = args.get(0);
488                    switch (s) {
489                    case "warn", "info" -> linkModularityMismatch
490                        = ModularityMismatchPolicy
491                            .valueOf(s.toUpperCase(Locale.ROOT));
492                    default -> {
493                        reporter.print(ERROR, resources.getText(
494                            "doclet.Option_invalid", s,
495                            "--link-modularity-mismatch"));
496                        return false;
497                    }
498                    }
499                    return true;
500                }
501            },
502
503            new Option(resources, "--link-platform-properties", 1) {
504                @Override
505                public boolean process(String opt, List<String> args) {
506                    linkPlatformProperties = args.get(0);
507                    return true;
508                }
509            },
510
511            new Option(resources, "-nocomment") {
512                @Override
513                public boolean process(String opt, List<String> args) {
514                    noComment = true;
515                    return true;
516                }
517            },
518
519            new Option(resources, "-nodeprecated") {
520                @Override
521                public boolean process(String opt, List<String> args) {
522                    noDeprecated = true;
523                    return true;
524                }
525            },
526
527            new Option(resources, "-nosince") {
528                @Override
529                public boolean process(String opt, List<String> args) {
530                    noSince = true;
531                    return true;
532                }
533            },
534
535            new Option(resources, "-notimestamp") {
536                @Override
537                public boolean process(String opt, List<String> args) {
538                    noTimestamp = true;
539                    if (date != null) {
540                        messages.error("doclet.Option_conflict", "--date",
541                            "-notimestamp");
542                        return false;
543                    }
544                    return true;
545                }
546            },
547
548            new Option(resources, "-noqualifier", 1) {
549                @Override
550                public boolean process(String opt, List<String> args) {
551                    excludedQualifiers
552                        .addAll(List.of(args.get(0).split("[,:]")));
553                    return true;
554                }
555            },
556
557            new Option(resources, "--no-platform-links") {
558                @Override
559                public boolean process(String opt, List<String> args) {
560                    noPlatformLinks = true;
561                    return true;
562                }
563            },
564
565            new Option(resources, "--override-methods", 1) {
566                @Override
567                public boolean process(String opt, List<String> args) {
568                    String o = args.get(0);
569                    switch (o) {
570                    case "summary" -> summarizeOverriddenMethods = true;
571                    case "detail" -> summarizeOverriddenMethods = false;
572                    default -> {
573                        reporter.print(ERROR,
574                            resources.getText("doclet.Option_invalid", o,
575                                "--override-methods"));
576                        return false;
577                    }
578                    }
579                    return true;
580                }
581            },
582
583            new Hidden(resources, "-quiet") {
584                @Override
585                public boolean process(String opt, List<String> args) {
586                    quiet = true;
587                    return true;
588                }
589            },
590
591            new Option(resources, "-serialwarn") {
592                @Override
593                public boolean process(String opt, List<String> args) {
594                    serialWarn = true;
595                    return true;
596                }
597            },
598
599            new Option(resources, "--since", 1) {
600                @Override
601                public boolean process(String opt, List<String> args) {
602                    since = Arrays.stream(args.get(0).split(","))
603                        .map(String::trim).toList();
604                    return true;
605                }
606            },
607
608            new Option(resources, "--since-label", 1) {
609                @Override
610                public boolean process(String opt, List<String> args) {
611                    sinceLabel = args.get(0);
612                    return true;
613                }
614            },
615
616            new Option(resources, "-sourcetab", 1) {
617                @Override
618                public boolean process(String opt, List<String> args) {
619                    linkSource = true;
620                    try {
621                        sourceTabSize = Integer.parseInt(args.get(0));
622                    } catch (NumberFormatException e) {
623                        // Set to -1 so that warning will be printed
624                        // to indicate what is valid argument.
625                        sourceTabSize = -1;
626                    }
627                    if (sourceTabSize <= 0) {
628                        messages.warning("doclet.sourcetab_warning");
629                        sourceTabSize = DEFAULT_TAB_STOP_LENGTH;
630                    }
631                    return true;
632                }
633            },
634
635            new Option(resources, "-tag", 1) {
636                @Override
637                public boolean process(String opt, List<String> args) {
638                    ArrayList<String> list = new ArrayList<>();
639                    list.add(opt);
640                    list.add(args.get(0));
641                    customTagStrs.add(list);
642                    return true;
643                }
644            },
645
646            new Option(resources, "-taglet", 1) {
647                @Override
648                public boolean process(String opt, List<String> args) {
649                    ArrayList<String> list = new ArrayList<>();
650                    list.add(opt);
651                    list.add(args.get(0));
652                    customTagStrs.add(list);
653                    return true;
654                }
655            },
656
657            new Option(resources, "-tagletpath", 1) {
658                @Override
659                public boolean process(String opt, List<String> args) {
660                    tagletPath = args.get(0);
661                    return true;
662                }
663            },
664
665            new Option(resources, "--snippet-path", 1) {
666                @Override
667                public boolean process(String opt, List<String> args) {
668                    snippetPath = args.get(0);
669                    return true;
670                }
671            },
672
673            new Option(resources, "-version") {
674                @Override
675                public boolean process(String opt, List<String> args) {
676                    showVersion = true;
677                    return true;
678                }
679            },
680
681            new Hidden(resources, "--dump-on-error") {
682                @Override
683                public boolean process(String opt, List<String> args) {
684                    dumpOnError = true;
685                    return true;
686                }
687            },
688
689            new Option(resources, "--allow-script-in-comments") {
690                @Override
691                public boolean process(String opt, List<String> args) {
692                    allowScriptInComments = true;
693                    return true;
694                }
695            },
696
697            new Option(resources, "--spec-base-url", 1) {
698                @Override
699                public boolean process(String opt, List<String> args) {
700                    String arg = args.get(0);
701                    try {
702                        if (!arg.endsWith("/")) {
703                            // to ensure that URI.resolve works as expected
704                            arg += "/";
705                        }
706                        specBaseURI = new URI(arg);
707                        return true;
708                    } catch (URISyntaxException e) {
709                        config.reporter.print(ERROR,
710                            config.getDocResources().getText(
711                                "doclet.Invalid_URL",
712                                e.getMessage()));
713                        return false;
714                    }
715                }
716            },
717
718            new Hidden(resources, "--disable-javafx-strict-checks") {
719                @Override
720                public boolean process(String opt, List<String> args) {
721                    disableJavaFxStrictChecks = true;
722                    return true;
723                }
724            },
725
726            new Hidden(resources, "--show-taglets") {
727                @Override
728                public boolean process(String opt, List<String> args) {
729                    showTaglets = true;
730                    return true;
731                }
732            });
733        return new TreeSet<>(options);
734    }
735
736    /**
737     * This checks for the validity of the options used by the user.
738     * As of this writing, this checks only docencoding.
739     *
740     * @return true if all the options are valid.
741     */
742    protected boolean generalValidOptions() {
743        if (docEncoding != null) {
744            if (!checkOutputFileEncoding(docEncoding)) {
745                return false;
746            }
747        }
748        if (docEncoding == null && (encoding != null && !encoding.isEmpty())) {
749            if (!checkOutputFileEncoding(encoding)) {
750                return false;
751            }
752        }
753        return true;
754    }
755
756    /**
757     * Check the validity of the given Source or Output File encoding on this
758     * platform.
759     *
760     * @param docencoding output file encoding.
761     */
762    private boolean checkOutputFileEncoding(String docencoding) {
763        OutputStream ost = new ByteArrayOutputStream();
764        OutputStreamWriter osw = null;
765        try {
766            osw = new OutputStreamWriter(ost, docencoding);
767        } catch (UnsupportedEncodingException exc) {
768            config.reporter.print(ERROR,
769                config.getDocResources()
770                    .getText("doclet.Encoding_not_supported", docencoding));
771            return false;
772        } finally {
773            try {
774                if (osw != null) {
775                    osw.close();
776                }
777            } catch (IOException exc) {
778            }
779        }
780        return true;
781    }
782
783    /**
784     * Add a trailing file separator, if not found. Remove superfluous
785     * file separators if any. Preserve the front double file separator for
786     * UNC paths.
787     *
788     * @param path Path under consideration.
789     * @return String Properly constructed path string.
790     */
791    protected static String addTrailingFileSep(String path) {
792        String fs = System.getProperty("file.separator");
793        String dblfs = fs + fs;
794        int indexDblfs;
795        while ((indexDblfs = path.indexOf(dblfs, 1)) >= 0) {
796            path = path.substring(0, indexDblfs) +
797                path.substring(indexDblfs + fs.length());
798        }
799        if (!path.endsWith(fs))
800            path += fs;
801        return path;
802    }
803
804    /**
805     * Argument for command-line option {@code --allow-script-in-comments}.
806     * Allow JavaScript in doc comments.
807     */
808    boolean allowScriptInComments() {
809        return allowScriptInComments;
810    }
811
812    /**
813     * Argument for command-line option {@code -docfilessubdirs}.
814     * True if we should recursively copy the doc-file subdirectories
815     */
816    public boolean copyDocfileSubdirs() {
817        return copyDocfileSubdirs;
818    }
819
820    /**
821     * Arguments for command-line option {@code -tag} and {@code -taglet}.
822     */
823    LinkedHashSet<List<String>> customTagStrs() {
824        return customTagStrs;
825    }
826
827    /**
828     * Argument for command-line option {@code --date}.
829     */
830    public ZonedDateTime date() {
831        return date;
832    }
833
834    /**
835     * Argument for command-line option {@code -d}.
836     * Destination directory name, in which doclet will generate the entire
837     * documentation. Default is current directory.
838     */
839    String destDirName() {
840        return destDirName;
841    }
842
843    /**
844     * Argument for command-line option {@code --disable-javafx-strict-checks}.
845     * Primarily used to disable strict checks in the regression
846     * tests allowing those tests to be executed successfully, for
847     * instance, with OpenJDK builds which may not contain FX libraries.
848     */
849    boolean disableJavaFxStrictChecks() {
850        return disableJavaFxStrictChecks;
851    }
852
853    /**
854     * Argument for command-line option {@code -docencoding}.
855     * Encoding for this document. Default is default encoding for this
856     * platform.
857     */
858    public String docEncoding() {
859        return docEncoding;
860    }
861
862    public void setDocEncoding(String docEncoding) {
863        this.docEncoding = docEncoding;
864    }
865
866    /**
867     * Argument for command-line option {@code ???}.
868     * Destination directory name, in which doclet will copy the doc-files to.
869     */
870    String docFileDestDirName() {
871        return docFileDestDirName;
872    }
873
874    /**
875     * Argument for hidden command-line option {@code --dump-on-error}.
876     */
877    boolean dumpOnError() {
878        return dumpOnError;
879    }
880
881    /**
882     * Argument for command-line option {@code -encoding}.
883     * Encoding for this document. Default is default encoding for this
884     * platform.
885     */
886    public String encoding() {
887        return encoding;
888    }
889
890    /**
891     * Argument for command-line option {@code -excludedocfilessubdir}.
892     * The set of doc-file subdirectories to exclude.
893     */
894    Set<String> excludedDocFileDirs() {
895        return excludedDocFileDirs;
896    }
897
898    /**
899     * Argument for command-line option {@code -noqualifier}.
900     * The set of qualifiers to exclude.
901     */
902    Set<String> excludedQualifiers() {
903        return excludedQualifiers;
904    }
905
906    /**
907     * Arguments for command-line option {@code -group}
908     */
909    List<Utils.Pair<String, String>> groupPairs() {
910        return groupPairs;
911    }
912
913    /**
914     * Argument for command-line option {@code --javafx} or {@code -javafx}.
915     * Generate documentation for JavaFX getters and setters automatically
916     * by copying it from the appropriate property definition.
917     */
918    public boolean javafx() {
919        return javafx;
920    }
921
922    public void setJavaFX(boolean javafx) {
923        this.javafx = javafx;
924    }
925
926    /**
927     * Argument for command-line option {@code -keywords}.
928     * True if user wants to add member names as meta keywords.
929     * Set to false because meta keywords are ignored in general
930     * by most Internet search engines.
931     */
932    public boolean keywords() {
933        return keywords;
934    }
935
936    /**
937     * Arguments for command-line option {@code -link}.
938     */
939    List<String> linkList() {
940        return linkList;
941    }
942
943    /**
944     * Arguments for command-line option {@code -linkoffline}.
945     */
946    List<Utils.Pair<String, String>> linkOfflineList() {
947        return linkOfflineList;
948    }
949
950    /**
951     * Argument for command-line option {@code --link-modularity-mismatch}.
952     * Describes how to handle external documentation with non-matching modularity.
953     */
954    public ModularityMismatchPolicy linkModularityMismatch() {
955        return linkModularityMismatch;
956    }
957
958    /**
959     * Argument for command-line option {@code --link-platform-properties}.
960     */
961    String linkPlatformProperties() {
962        return linkPlatformProperties;
963    }
964
965    /**
966     * Argument for command-line option {@code -linksource}.
967     * True if we should generate browsable sources.
968     */
969    public boolean linkSource() {
970        return linkSource;
971    }
972
973    /**
974     * Argument for command-line option {@code -nocomment}.
975     * True if user wants to suppress descriptions and tags.
976     */
977    public boolean noComment() {
978        return noComment;
979    }
980
981    /**
982     * Argument for command-line option {@code -nodeprecated}.
983     * Don't generate deprecated API information at all if -nodeprecated
984     * option is used. {@code noDeprecated} is set to {@code true} if
985     * {@code -nodeprecated} option is used.
986     * Default is generate deprecated API information.
987     */
988    public boolean noDeprecated() {
989        return noDeprecated;
990    }
991
992    /**
993     * Argument for command-line option {@code --no-platform-links}.
994     * True if command-line option {@code --no-platform-links"} is used.
995     * Default value is false.
996     */
997    public boolean noPlatformLinks() {
998        return noPlatformLinks;
999    }
1000
1001    /**
1002     * Argument for command-line option {@code -nosince}.
1003     * True if command-line option {@code -nosince"} is used.
1004     * Default value is false.
1005     */
1006    public boolean noSince() {
1007        return noSince;
1008    }
1009
1010    /**
1011     * Argument for command-line option {@code -notimestamp}.
1012     * True if user wants to suppress time stamp in output.
1013     * Default is false.
1014     */
1015    public boolean noTimestamp() {
1016        return noTimestamp;
1017    }
1018
1019    /**
1020     * Argument for command-line option {@code -quiet}.
1021     * Suppress all messages
1022     */
1023    boolean quiet() {
1024        return quiet;
1025    }
1026
1027    /**
1028     * Argument for command-line option {@code -serialwarn}.
1029     * This is true if option "-serialwarn" is used. Default value is false to
1030     * suppress excessive warnings about serial tag.
1031     */
1032    public boolean serialWarn() {
1033        return serialWarn;
1034    }
1035
1036    /**
1037     * Argument for command-line option {@code -author}.
1038     * Generate author specific information for all the classes if @author
1039     * tag is used in the doc comment and if -author option is used.
1040     * <code>showauthor</code> is set to true if -author option is used.
1041     * Default is don't show author information.
1042     */
1043    public boolean showAuthor() {
1044        return showAuthor;
1045    }
1046
1047    /**
1048     * Argument for command-line option {@code --show-taglets}.
1049     * Show taglets (internal debug switch)
1050     */
1051    public boolean showTaglets() {
1052        return showTaglets;
1053    }
1054
1055    /**
1056     * Argument for command-line option {@code -version}.
1057     * Generate version specific information for the all the classes
1058     * if @version tag is used in the doc comment and if -version option is
1059     * used. {@code showVersion} is set to true if -version option is
1060     * used. Default is don't show version information.
1061     */
1062    public boolean showVersion() {
1063        return showVersion;
1064    }
1065
1066    /**
1067     * Arguments for command line option {@code --since}.
1068     */
1069    public List<String> since() {
1070        return List.copyOf(since);
1071    }
1072
1073    /**
1074     * Arguments for command line option {@code --since-label}.
1075     */
1076    public String sinceLabel() {
1077        return sinceLabel;
1078    }
1079
1080    /**
1081     * Argument for command-line option {@code -sourcetab}.
1082     * The specified amount of space between tab stops.
1083     */
1084    public int sourceTabSize() {
1085        return sourceTabSize;
1086    }
1087
1088    /**
1089     * Argument for command-line option {@code --spec-base-url}.
1090     * The base URL for relative URLs in {@code @spec} tags.
1091     */
1092    public URI specBaseURI() {
1093        return specBaseURI;
1094    }
1095
1096    /**
1097     * Value for command-line option {@code --override-methods summary}
1098     * or {@code --override-methods detail}.
1099     * Specifies whether those methods that override a supertype's method
1100     * with no changes to the API contract should be summarized in the
1101     * footnote section.
1102     */
1103    public boolean summarizeOverriddenMethods() {
1104        return summarizeOverriddenMethods;
1105    }
1106
1107    /**
1108     * Argument for command-line option {@code -tagletpath}.
1109     * The path to Taglets
1110     */
1111    public String tagletPath() {
1112        return tagletPath;
1113    }
1114
1115    /**
1116     * Argument for command-line option {@code --snippet-path}.
1117     * The path for external snippets.
1118     */
1119    public String snippetPath() {
1120        return snippetPath;
1121    }
1122
1123    protected abstract static class Option
1124            implements Doclet.Option, Comparable<Option> {
1125        private final String[] names;
1126        private final String parameters;
1127        private final String description;
1128        private final int argCount;
1129
1130        protected Option(Resources resources, String name, int argCount) {
1131            this(resources, null, name, argCount);
1132        }
1133
1134        protected Option(Resources resources, String keyBase, String name,
1135                int argCount) {
1136            this.names = name.trim().split("\\s+");
1137            if (keyBase == null) {
1138                keyBase = "doclet.usage."
1139                    + Utils.toLowerCase(names[0]).replaceAll("^-+", "");
1140            }
1141            String desc
1142                = getOptionsMessage(resources, keyBase + ".description");
1143            if (desc.isEmpty()) {
1144                this.description = "<MISSING KEY>";
1145                this.parameters = "<MISSING KEY>";
1146            } else {
1147                this.description = desc;
1148                this.parameters
1149                    = getOptionsMessage(resources, keyBase + ".parameters");
1150            }
1151            this.argCount = argCount;
1152        }
1153
1154        protected Option(Resources resources, String name) {
1155            this(resources, name, 0);
1156        }
1157
1158        private String getOptionsMessage(Resources resources, String key) {
1159            try {
1160                return resources.getText(key);
1161            } catch (MissingResourceException ignore) {
1162                return "";
1163            }
1164        }
1165
1166        @Override
1167        public String getDescription() {
1168            return description;
1169        }
1170
1171        @Override
1172        public Kind getKind() {
1173            return Kind.STANDARD;
1174        }
1175
1176        @Override
1177        public List<String> getNames() {
1178            return Arrays.asList(names);
1179        }
1180
1181        @Override
1182        public String getParameters() {
1183            return parameters;
1184        }
1185
1186        @Override
1187        public String toString() {
1188            return Arrays.toString(names);
1189        }
1190
1191        @Override
1192        public int getArgumentCount() {
1193            return argCount;
1194        }
1195
1196        public boolean matches(String option) {
1197            for (String name : names) {
1198                boolean matchCase = name.startsWith("--");
1199                if (option.startsWith("--") && option.contains("=")) {
1200                    return name
1201                        .equals(option.substring(option.indexOf("=") + 1));
1202                } else if (matchCase) {
1203                    return name.equals(option);
1204                }
1205                return name.equalsIgnoreCase(option);
1206            }
1207            return false;
1208        }
1209
1210        @Override
1211        public int compareTo(Option that) {
1212            return this.getNames().get(0).compareTo(that.getNames().get(0));
1213        }
1214    }
1215
1216    protected abstract static class XOption extends Option {
1217
1218        public XOption(Resources resources, String prefix, String name,
1219                int argCount) {
1220            super(resources, prefix, name, argCount);
1221        }
1222
1223        public XOption(Resources resources, String name, int argCount) {
1224            super(resources, name, argCount);
1225        }
1226
1227        public XOption(Resources resources, String name) {
1228            this(resources, name, 0);
1229        }
1230
1231        @Override
1232        public Option.Kind getKind() {
1233            return Kind.EXTENDED;
1234        }
1235    }
1236
1237    protected abstract static class Hidden extends Option {
1238
1239        public Hidden(Resources resources, String name, int argCount) {
1240            super(resources, name, argCount);
1241        }
1242
1243        public Hidden(Resources resources, String name) {
1244            this(resources, name, 0);
1245        }
1246
1247        @Override
1248        public Option.Kind getKind() {
1249            return Kind.OTHER;
1250        }
1251    }
1252}