1 /*
   2  * Copyright (c) 1998, 2015, 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 com.sun.tools.doclets.formats.html;
  27 
  28 import java.net.*;
  29 import java.util.*;
  30 
  31 import javax.tools.JavaFileManager;
  32 
  33 import com.sun.javadoc.*;
  34 import com.sun.tools.doclets.formats.html.markup.*;
  35 import com.sun.tools.doclets.internal.toolkit.*;
  36 import com.sun.tools.doclets.internal.toolkit.util.*;
  37 import com.sun.tools.doclint.DocLint;
  38 import com.sun.tools.javac.file.JavacFileManager;
  39 import com.sun.tools.javac.util.Context;
  40 import com.sun.tools.javac.util.StringUtils;
  41 import com.sun.tools.javadoc.RootDocImpl;
  42 
  43 /**
  44  * Configure the output based on the command line options.
  45  * <p>
  46  * Also determine the length of the command line option. For example,
  47  * for a option "-header" there will be a string argument associated, then the
  48  * the length of option "-header" is two. But for option "-nohelp" no argument
  49  * is needed so it's length is 1.
  50  * </p>
  51  * <p>
  52  * Also do the error checking on the options used. For example it is illegal to
  53  * use "-helpfile" option when already "-nohelp" option is used.
  54  * </p>
  55  *
  56  *  <p><b>This is NOT part of any supported API.
  57  *  If you write code that depends on this, you do so at your own risk.
  58  *  This code and its internal interfaces are subject to change or
  59  *  deletion without notice.</b>
  60  *
  61  * @author Robert Field.
  62  * @author Atul Dambalkar.
  63  * @author Jamie Ho
  64  * @author Bhavesh Patel (Modified)
  65  */
  66 public class ConfigurationImpl extends Configuration {
  67 
  68     /**
  69      * The build date.  Note: For now, we will use
  70      * a version number instead of a date.
  71      */
  72     public static final String BUILD_DATE = System.getProperty("java.version");
  73 
  74     /**
  75      * Argument for command line option "-header".
  76      */
  77     public String header = "";
  78 
  79     /**
  80      * Argument for command line option "-packagesheader".
  81      */
  82     public String packagesheader = "";
  83 
  84     /**
  85      * Argument for command line option "-footer".
  86      */
  87     public String footer = "";
  88 
  89     /**
  90      * Argument for command line option "-doctitle".
  91      */
  92     public String doctitle = "";
  93 
  94     /**
  95      * Argument for command line option "-windowtitle".
  96      */
  97     public String windowtitle = "";
  98 
  99     /**
 100      * Argument for command line option "-top".
 101      */
 102     public String top = "";
 103 
 104     /**
 105      * Argument for command line option "-bottom".
 106      */
 107     public String bottom = "";
 108 
 109     /**
 110      * Argument for command line option "-helpfile".
 111      */
 112     public String helpfile = "";
 113 
 114     /**
 115      * Argument for command line option "-stylesheetfile".
 116      */
 117     public String stylesheetfile = "";
 118 
 119     /**
 120      * Argument for command line option "-Xdocrootparent".
 121      */
 122     public String docrootparent = "";
 123 
 124     /**
 125      * True if command line option "-nohelp" is used. Default value is false.
 126      */
 127     public boolean nohelp = false;
 128 
 129     /**
 130      * True if command line option "-splitindex" is used. Default value is
 131      * false.
 132      */
 133     public boolean splitindex = false;
 134 
 135     /**
 136      * False if command line option "-noindex" is used. Default value is true.
 137      */
 138     public boolean createindex = true;
 139 
 140     /**
 141      * True if command line option "-use" is used. Default value is false.
 142      */
 143     public boolean classuse = false;
 144 
 145     /**
 146      * False if command line option "-notree" is used. Default value is true.
 147      */
 148     public boolean createtree = true;
 149 
 150     /**
 151      * True if command line option "-nodeprecated" is used. Default value is
 152      * false.
 153      */
 154     public boolean nodeprecatedlist = false;
 155 
 156     /**
 157      * True if command line option "-nonavbar" is used. Default value is false.
 158      */
 159     public boolean nonavbar = false;
 160 
 161     /**
 162      * True if command line option "-nooverview" is used. Default value is
 163      * false
 164      */
 165     private boolean nooverview = false;
 166 
 167     /**
 168      * True if command line option "-overview" is used. Default value is false.
 169      */
 170     public boolean overview = false;
 171 
 172     /**
 173      * This is true if option "-overview" is used or option "-overview" is not
 174      * used and number of packages is more than one.
 175      */
 176     public boolean createoverview = false;
 177 
 178     /**
 179      * This is the HTML version of the generated pages. HTML 4.01 is the default output version.
 180      */
 181     public HtmlVersion htmlVersion = HtmlVersion.HTML4;
 182 
 183     /**
 184      * Collected set of doclint options
 185      */
 186     public Set<String> doclintOpts = new LinkedHashSet<>();
 187 
 188     /**
 189      * Unique Resource Handler for this package.
 190      */
 191     public final MessageRetriever standardmessage;
 192 
 193     /**
 194      * First file to appear in the right-hand frame in the generated
 195      * documentation.
 196      */
 197     public DocPath topFile = DocPath.empty;
 198 
 199     /**
 200      * The classdoc for the class file getting generated.
 201      */
 202     public ClassDoc currentcd = null;  // Set this classdoc in the ClassWriter.
 203 
 204     protected List<SearchIndexItem> memberSearchIndex = new ArrayList<>();
 205 
 206     protected List<SearchIndexItem> packageSearchIndex = new ArrayList<>();
 207 
 208     protected List<SearchIndexItem> tagSearchIndex = new ArrayList<>();
 209 
 210     protected List<SearchIndexItem> typeSearchIndex = new ArrayList<>();
 211 
 212     protected Map<Character,List<SearchIndexItem>> tagSearchIndexMap = new HashMap<>();
 213 
 214     protected Set<Character> tagSearchIndexKeys;
 215 
 216     /**
 217      * Constructor. Initializes resource for the
 218      * {@link com.sun.tools.doclets.internal.toolkit.util.MessageRetriever MessageRetriever}.
 219      */
 220     public ConfigurationImpl() {
 221         standardmessage = new MessageRetriever(this,
 222             "com.sun.tools.doclets.formats.html.resources.standard");
 223     }
 224 
 225     private final String versionRBName = "com.sun.tools.javadoc.resources.version";
 226     private ResourceBundle versionRB;
 227 
 228     /**
 229      * Return the build date for the doclet.
 230      */
 231     @Override
 232     public String getDocletSpecificBuildDate() {
 233         if (versionRB == null) {
 234             try {
 235                 versionRB = ResourceBundle.getBundle(versionRBName);
 236             } catch (MissingResourceException e) {
 237                 return BUILD_DATE;
 238             }
 239         }
 240 
 241         try {
 242             return versionRB.getString("release");
 243         } catch (MissingResourceException e) {
 244             return BUILD_DATE;
 245         }
 246     }
 247 
 248     /**
 249      * Depending upon the command line options provided by the user, set
 250      * configure the output generation environment.
 251      *
 252      * @param options The array of option names and values.
 253      */
 254     @Override
 255     public void setSpecificDocletOptions(String[][] options) {
 256         for (int oi = 0; oi < options.length; ++oi) {
 257             String[] os = options[oi];
 258             String opt = StringUtils.toLowerCase(os[0]);
 259             if (opt.equals("-footer")) {
 260                 footer = os[1];
 261             } else if (opt.equals("-header")) {
 262                 header = os[1];
 263             } else if (opt.equals("-packagesheader")) {
 264                 packagesheader = os[1];
 265             } else if (opt.equals("-doctitle")) {
 266                 doctitle = os[1];
 267             } else if (opt.equals("-windowtitle")) {
 268                 windowtitle = os[1].replaceAll("\\<.*?>", "");
 269             } else if (opt.equals("-top")) {
 270                 top = os[1];
 271             } else if (opt.equals("-bottom")) {
 272                 bottom = os[1];
 273             } else if (opt.equals("-helpfile")) {
 274                 helpfile = os[1];
 275             } else if (opt.equals("-stylesheetfile")) {
 276                 stylesheetfile = os[1];
 277             } else if (opt.equals("-charset")) {
 278                 charset = os[1];
 279             } else if (opt.equals("-xdocrootparent")) {
 280                 docrootparent = os[1];
 281             } else if (opt.equals("-nohelp")) {
 282                 nohelp = true;
 283             } else if (opt.equals("-splitindex")) {
 284                 splitindex = true;
 285             } else if (opt.equals("-noindex")) {
 286                 createindex = false;
 287             } else if (opt.equals("-use")) {
 288                 classuse = true;
 289             } else if (opt.equals("-notree")) {
 290                 createtree = false;
 291             } else if (opt.equals("-nodeprecatedlist")) {
 292                 nodeprecatedlist = true;
 293             } else if (opt.equals("-nonavbar")) {
 294                 nonavbar = true;
 295             } else if (opt.equals("-nooverview")) {
 296                 nooverview = true;
 297             } else if (opt.equals("-overview")) {
 298                 overview = true;
 299             } else if (opt.equals("-html4")) {
 300                 htmlVersion = HtmlVersion.HTML4;
 301             } else if (opt.equals("-html5")) {
 302                 htmlVersion = HtmlVersion.HTML5;
 303             } else if (opt.equals("-xdoclint")) {
 304                 doclintOpts.add(DocLint.XMSGS_OPTION);
 305             } else if (opt.startsWith("-xdoclint:")) {
 306                 doclintOpts.add(DocLint.XMSGS_CUSTOM_PREFIX + opt.substring(opt.indexOf(":") + 1));
 307             } else if (opt.startsWith("-xdoclint/package:")) {
 308                 doclintOpts.add(DocLint.XCHECK_PACKAGE + opt.substring(opt.indexOf(":") + 1));
 309             }
 310         }
 311         if (root.specifiedClasses().length > 0) {
 312             Map<String,PackageDoc> map = new HashMap<>();
 313             PackageDoc pd;
 314             ClassDoc[] classes = root.classes();
 315             for (ClassDoc aClass : classes) {
 316                 pd = aClass.containingPackage();
 317                 if (!map.containsKey(pd.name())) {
 318                     map.put(pd.name(), pd);
 319                 }
 320             }
 321         }
 322         setCreateOverview();
 323         setTopFile(root);
 324 
 325         if (root instanceof RootDocImpl) {
 326             ((RootDocImpl) root).initDocLint(doclintOpts, tagletManager.getCustomTagNames(),
 327                     StringUtils.toLowerCase(htmlVersion.name()));
 328         }
 329     }
 330 
 331     /**
 332      * Returns the "length" of a given option. If an option takes no
 333      * arguments, its length is one. If it takes one argument, it's
 334      * length is two, and so on. This method is called by JavaDoc to
 335      * parse the options it does not recognize. It then calls
 336      * {@link #validOptions(String[][], DocErrorReporter)} to
 337      * validate them.
 338      * <b>Note:</b><br>
 339      * The options arrive as case-sensitive strings. For options that
 340      * are not case-sensitive, use toLowerCase() on the option string
 341      * before comparing it.
 342      *
 343      * @return number of arguments + 1 for a option. Zero return means
 344      * option not known.  Negative value means error occurred.
 345      */
 346     public int optionLength(String option) {
 347         int result = -1;
 348         if ((result = super.optionLength(option)) > 0) {
 349             return result;
 350         }
 351         // otherwise look for the options we have added
 352         option = StringUtils.toLowerCase(option);
 353         if (option.equals("-nodeprecatedlist") ||
 354             option.equals("-noindex") ||
 355             option.equals("-notree") ||
 356             option.equals("-nohelp") ||
 357             option.equals("-splitindex") ||
 358             option.equals("-serialwarn") ||
 359             option.equals("-use") ||
 360             option.equals("-nonavbar") ||
 361             option.equals("-nooverview") ||
 362             option.equals("-html4") ||
 363             option.equals("-html5") ||
 364             option.equals("-xdoclint") ||
 365             option.startsWith("-xdoclint:") ||
 366             option.startsWith("-xdoclint/package:")) {
 367             return 1;
 368         } else if (option.equals("-help")) {
 369             // Uugh: first, this should not be hidden inside optionLength,
 370             // and second, we should not be writing directly to stdout.
 371             // But we have no access to a DocErrorReporter, which would
 372             // allow use of reporter.printNotice
 373             System.out.println(getText("doclet.usage"));
 374             return 1;
 375         } else if (option.equals("-x")) {
 376             // Uugh: first, this should not be hidden inside optionLength,
 377             // and second, we should not be writing directly to stdout.
 378             // But we have no access to a DocErrorReporter, which would
 379             // allow use of reporter.printNotice
 380             System.out.println(getText("doclet.X.usage"));
 381             return 1;
 382         } else if (option.equals("-footer") ||
 383                    option.equals("-header") ||
 384                    option.equals("-packagesheader") ||
 385                    option.equals("-doctitle") ||
 386                    option.equals("-windowtitle") ||
 387                    option.equals("-top") ||
 388                    option.equals("-bottom") ||
 389                    option.equals("-helpfile") ||
 390                    option.equals("-stylesheetfile") ||
 391                    option.equals("-charset") ||
 392                    option.equals("-overview") ||
 393                    option.equals("-xdocrootparent")) {
 394             return 2;
 395         } else {
 396             return 0;
 397         }
 398     }
 399 
 400     /**
 401      * {@inheritDoc}
 402      */
 403     @Override
 404     public boolean validOptions(String options[][],
 405             DocErrorReporter reporter) {
 406         boolean helpfile = false;
 407         boolean nohelp = false;
 408         boolean overview = false;
 409         boolean nooverview = false;
 410         boolean splitindex = false;
 411         boolean noindex = false;
 412         // check shared options
 413         if (!generalValidOptions(options, reporter)) {
 414             return false;
 415         }
 416         // otherwise look at our options
 417         for (int oi = 0; oi < options.length; ++oi) {
 418             String[] os = options[oi];
 419             String opt = StringUtils.toLowerCase(os[0]);
 420             if (opt.equals("-helpfile")) {
 421                 if (nohelp == true) {
 422                     reporter.printError(getText("doclet.Option_conflict",
 423                         "-helpfile", "-nohelp"));
 424                     return false;
 425                 }
 426                 if (helpfile == true) {
 427                     reporter.printError(getText("doclet.Option_reuse",
 428                         "-helpfile"));
 429                     return false;
 430                 }
 431                 DocFile help = DocFile.createFileForInput(this, os[1]);
 432                 if (!help.exists()) {
 433                     reporter.printError(getText("doclet.File_not_found", os[1]));
 434                     return false;
 435                 }
 436                 helpfile = true;
 437             } else  if (opt.equals("-nohelp")) {
 438                 if (helpfile == true) {
 439                     reporter.printError(getText("doclet.Option_conflict",
 440                         "-nohelp", "-helpfile"));
 441                     return false;
 442                 }
 443                 nohelp = true;
 444             } else if (opt.equals("-xdocrootparent")) {
 445                 try {
 446                     new URL(os[1]);
 447                 } catch (MalformedURLException e) {
 448                     reporter.printError(getText("doclet.MalformedURL", os[1]));
 449                     return false;
 450                 }
 451             } else if (opt.equals("-overview")) {
 452                 if (nooverview == true) {
 453                     reporter.printError(getText("doclet.Option_conflict",
 454                         "-overview", "-nooverview"));
 455                     return false;
 456                 }
 457                 if (overview == true) {
 458                     reporter.printError(getText("doclet.Option_reuse",
 459                         "-overview"));
 460                     return false;
 461                 }
 462                 overview = true;
 463             } else  if (opt.equals("-nooverview")) {
 464                 if (overview == true) {
 465                     reporter.printError(getText("doclet.Option_conflict",
 466                         "-nooverview", "-overview"));
 467                     return false;
 468                 }
 469                 nooverview = true;
 470             } else if (opt.equals("-splitindex")) {
 471                 if (noindex == true) {
 472                     reporter.printError(getText("doclet.Option_conflict",
 473                         "-splitindex", "-noindex"));
 474                     return false;
 475                 }
 476                 splitindex = true;
 477             } else if (opt.equals("-noindex")) {
 478                 if (splitindex == true) {
 479                     reporter.printError(getText("doclet.Option_conflict",
 480                         "-noindex", "-splitindex"));
 481                     return false;
 482                 }
 483                 noindex = true;
 484             } else if (opt.startsWith("-xdoclint:")) {
 485                 if (opt.contains("/")) {
 486                     reporter.printError(getText("doclet.Option_doclint_no_qualifiers"));
 487                     return false;
 488                 }
 489                 if (!DocLint.isValidOption(
 490                         opt.replace("-xdoclint:", DocLint.XMSGS_CUSTOM_PREFIX))) {
 491                     reporter.printError(getText("doclet.Option_doclint_invalid_arg"));
 492                     return false;
 493                 }
 494             } else if (opt.startsWith("-xdoclint/package:")) {
 495                 if (!DocLint.isValidOption(
 496                         opt.replace("-xdoclint/package:", DocLint.XCHECK_PACKAGE))) {
 497                     reporter.printError(getText("doclet.Option_doclint_package_invalid_arg"));
 498                     return false;
 499                 }
 500             }
 501         }
 502         return true;
 503     }
 504 
 505     /**
 506      * Return true if the generated output is HTML5.
 507      */
 508     public boolean isOutputHtml5() {
 509         return htmlVersion == HtmlVersion.HTML5;
 510     }
 511 
 512     /**
 513      * Return true if the tag is allowed for this specific version of HTML.
 514      */
 515     public boolean allowTag(HtmlTag htmlTag) {
 516         return htmlTag.allowTag(this.htmlVersion);
 517     }
 518 
 519     /**
 520      * {@inheritDoc}
 521      */
 522     @Override
 523     public MessageRetriever getDocletSpecificMsg() {
 524         return standardmessage;
 525     }
 526 
 527     /**
 528      * Decide the page which will appear first in the right-hand frame. It will
 529      * be "overview-summary.html" if "-overview" option is used or no
 530      * "-overview" but the number of packages is more than one. It will be
 531      * "package-summary.html" of the respective package if there is only one
 532      * package to document. It will be a class page(first in the sorted order),
 533      * if only classes are provided on the command line.
 534      *
 535      * @param root Root of the program structure.
 536      */
 537     protected void setTopFile(RootDoc root) {
 538         if (!checkForDeprecation(root)) {
 539             return;
 540         }
 541         if (createoverview) {
 542             topFile = DocPaths.OVERVIEW_SUMMARY;
 543         } else {
 544             if (packages.size() == 1 && packages.first().name().equals("")) {
 545                 if (root.classes().length > 0) {
 546                     ClassDoc[] classarr = root.classes();
 547                     Arrays.sort(classarr);
 548                     ClassDoc cd = getValidClass(classarr);
 549                     topFile = DocPath.forClass(cd);
 550                 }
 551             } else if (!packages.isEmpty()) {
 552                 topFile = DocPath.forPackage(packages.first()).resolve(DocPaths.PACKAGE_SUMMARY);
 553             }
 554         }
 555     }
 556 
 557     protected ClassDoc getValidClass(ClassDoc[] classarr) {
 558         if (!nodeprecated) {
 559             return classarr[0];
 560         }
 561         for (ClassDoc cd : classarr) {
 562             if (cd.tags("deprecated").length == 0) {
 563                 return cd;
 564             }
 565         }
 566         return null;
 567     }
 568 
 569     protected boolean checkForDeprecation(RootDoc root) {
 570         for (ClassDoc cd : root.classes()) {
 571             if (isGeneratedDoc(cd)) {
 572                 return true;
 573             }
 574         }
 575         return false;
 576     }
 577 
 578     /**
 579      * Generate "overview.html" page if option "-overview" is used or number of
 580      * packages is more than one. Sets {@link #createoverview} field to true.
 581      */
 582     protected void setCreateOverview() {
 583         if ((overview || packages.size() > 1) && !nooverview) {
 584             createoverview = true;
 585         }
 586     }
 587 
 588     /**
 589      * {@inheritDoc}
 590      */
 591     @Override
 592     public WriterFactory getWriterFactory() {
 593         return new WriterFactoryImpl(this);
 594     }
 595 
 596     /**
 597      * {@inheritDoc}
 598      */
 599     @Override
 600     public Comparator<ProgramElementDoc> getMemberComparator() {
 601         return null;
 602     }
 603 
 604     /**
 605      * {@inheritDoc}
 606      */
 607     @Override
 608     public Locale getLocale() {
 609         if (root instanceof RootDocImpl)
 610             return ((RootDocImpl)root).getLocale();
 611         else
 612             return Locale.getDefault();
 613     }
 614 
 615     /**
 616      * {@inheritDoc}
 617      */
 618     @Override
 619     public JavaFileManager getFileManager() {
 620         if (fileManager == null) {
 621             if (root instanceof RootDocImpl)
 622                 fileManager = ((RootDocImpl) root).getFileManager();
 623             else
 624                 fileManager = new JavacFileManager(new Context(), false, null);
 625         }
 626         return fileManager;
 627     }
 628 
 629     private JavaFileManager fileManager;
 630 
 631     @Override
 632     public boolean showMessage(SourcePosition pos, String key) {
 633         if (root instanceof RootDocImpl) {
 634             return pos == null || ((RootDocImpl) root).showTagMessages();
 635         }
 636         return true;
 637     }
 638 
 639     @Override
 640     public Content newContent() {
 641         return new ContentBuilder();
 642     }
 643 
 644     protected void buildSearchTagIndex() {
 645         for (SearchIndexItem sii : tagSearchIndex) {
 646             String tagLabel = sii.getLabel();
 647             char ch = (tagLabel.length() == 0)
 648                     ? '*'
 649                     : Character.toUpperCase(tagLabel.charAt(0));
 650             Character unicode = ch;
 651             List<SearchIndexItem> list = tagSearchIndexMap.get(unicode);
 652             if (list == null) {
 653                 list = new ArrayList<>();
 654                 tagSearchIndexMap.put(unicode, list);
 655             }
 656             list.add(sii);
 657         }
 658         tagSearchIndexKeys = tagSearchIndexMap.keySet();
 659     }
 660 }