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