1/*
2 * Copyright (c) 2000, 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
26package com.sun.tools.javadoc.main;
27
28import java.lang.reflect.Modifier;
29import java.util.*;
30
31import javax.tools.JavaFileManager;
32
33import com.sun.javadoc.*;
34import com.sun.source.tree.CompilationUnitTree;
35import com.sun.source.util.JavacTask;
36import com.sun.source.util.TreePath;
37import com.sun.tools.doclint.DocLint;
38import com.sun.tools.javac.api.BasicJavacTask;
39import com.sun.tools.javac.code.*;
40import com.sun.tools.javac.code.Symbol.*;
41import com.sun.tools.javac.code.Symbol.ClassSymbol;
42import com.sun.tools.javac.code.Symbol.CompletionFailure;
43import com.sun.tools.javac.code.Symbol.MethodSymbol;
44import com.sun.tools.javac.code.Symbol.PackageSymbol;
45import com.sun.tools.javac.code.Symbol.VarSymbol;
46import com.sun.tools.javac.code.Type.ClassType;
47import com.sun.tools.javac.comp.Check;
48import com.sun.tools.javac.comp.Enter;
49import com.sun.tools.javac.file.JavacFileManager;
50import com.sun.tools.javac.tree.JCTree;
51import com.sun.tools.javac.tree.JCTree.JCClassDecl;
52import com.sun.tools.javac.tree.JCTree.JCCompilationUnit;
53import com.sun.tools.javac.tree.JCTree.JCPackageDecl;
54import com.sun.tools.javac.util.Context;
55import com.sun.tools.javac.util.Convert;
56import com.sun.tools.javac.util.Name;
57import com.sun.tools.javac.util.Names;
58
59/**
60 * Holds the environment for a run of javadoc.
61 * Holds only the information needed throughout the
62 * run and not the compiler info that could be GC'ed
63 * or ported.
64 *
65 *  <p><b>This is NOT part of any supported API.
66 *  If you write code that depends on this, you do so at your own risk.
67 *  This code and its internal interfaces are subject to change or
68 *  deletion without notice.</b>
69 *
70 * @since 1.4
71 * @author Robert Field
72 * @author Neal Gafter (rewrite)
73 * @author Scott Seligman (generics)
74 */
75@Deprecated
76public class DocEnv {
77    protected static final Context.Key<DocEnv> docEnvKey = new Context.Key<>();
78
79    public static DocEnv instance(Context context) {
80        DocEnv instance = context.get(docEnvKey);
81        if (instance == null)
82            instance = new DocEnv(context);
83        return instance;
84    }
85
86    DocLocale doclocale;
87
88    private final Messager messager;
89
90    /** Predefined symbols known to the compiler. */
91    final Symtab syms;
92
93    /** Referenced directly in RootDocImpl. */
94    private final ClassFinder finder;
95
96    /** Javadoc's own version of the compiler's enter phase. */
97    final Enter enter;
98
99    /** The name table. */
100    private final Names names;
101
102    /** The encoding name. */
103    private String encoding;
104
105    final Symbol externalizableSym;
106
107    /** Access filter (public, protected, ...).  */
108    protected ModifierFilter showAccess;
109
110    /** True if we are using a sentence BreakIterator. */
111    boolean breakiterator;
112
113    /**
114     * True if we do not want to print any notifications at all.
115     */
116    boolean quiet = false;
117
118    Check chk;
119    Types types;
120    JavaFileManager fileManager;
121    Context context;
122    DocLint doclint;
123    JavaScriptScanner javaScriptScanner;
124
125    WeakHashMap<JCTree, TreePath> treePaths = new WeakHashMap<>();
126
127    /** Allow documenting from class files? */
128    boolean docClasses = false;
129
130    /** Does the doclet only expect pre-1.5 doclet API? */
131    protected boolean legacyDoclet = true;
132
133    /**
134     * Set this to true if you would like to not emit any errors, warnings and
135     * notices.
136     */
137    private boolean silent = false;
138
139    /**
140     * The source language version.
141     */
142    protected Source source;
143
144    /**
145     * Constructor
146     *
147     * @param context      Context for this javadoc instance.
148     */
149    protected DocEnv(Context context) {
150        context.put(docEnvKey, this);
151        this.context = context;
152
153        messager = Messager.instance0(context);
154        syms = Symtab.instance(context);
155        finder = JavadocClassFinder.instance(context);
156        enter = JavadocEnter.instance(context);
157        names = Names.instance(context);
158        externalizableSym = syms.enterClass(syms.java_base, names.fromString("java.io.Externalizable"));
159        chk = Check.instance(context);
160        types = Types.instance(context);
161        fileManager = context.get(JavaFileManager.class);
162        if (fileManager instanceof JavacFileManager) {
163            ((JavacFileManager)fileManager).setSymbolFileEnabled(false);
164        }
165
166        // Default.  Should normally be reset with setLocale.
167        this.doclocale = new DocLocale(this, "", breakiterator);
168        source = Source.instance(context);
169    }
170
171    public void setSilent(boolean silent) {
172        this.silent = silent;
173    }
174
175    /**
176     * Look up ClassDoc by qualified name.
177     */
178    public ClassDocImpl lookupClass(String name) {
179        ClassSymbol c = getClassSymbol(name);
180        if (c != null) {
181            return getClassDoc(c);
182        } else {
183            return null;
184        }
185    }
186
187    /**
188     * Load ClassDoc by qualified name.
189     */
190    public ClassDocImpl loadClass(String name) {
191        try {
192            Name nameImpl = names.fromString(name);
193            ModuleSymbol mod = syms.inferModule(Convert.packagePart(nameImpl));
194            ClassSymbol c = finder.loadClass(mod != null ? mod : syms.errModule, nameImpl);
195            return getClassDoc(c);
196        } catch (CompletionFailure ex) {
197            chk.completionError(null, ex);
198            return null;
199        }
200    }
201
202    /**
203     * Look up PackageDoc by qualified name.
204     */
205    public PackageDocImpl lookupPackage(String name) {
206        //### Jing alleges that class check is needed
207        //### to avoid a compiler bug.  Most likely
208        //### instead a dummy created for error recovery.
209        //### Should investigate this.
210        Name nameImpl = names.fromString(name);
211        ModuleSymbol mod = syms.inferModule(nameImpl);
212        PackageSymbol p = mod != null ? syms.getPackage(mod, nameImpl) : null;
213        ClassSymbol c = getClassSymbol(name);
214        if (p != null && c == null) {
215            return getPackageDoc(p);
216        } else {
217            return null;
218        }
219    }
220        // where
221        /** Retrieve class symbol by fully-qualified name.
222         */
223        ClassSymbol getClassSymbol(String name) {
224            // Name may contain nested class qualification.
225            // Generate candidate flatnames with successively shorter
226            // package qualifiers and longer nested class qualifiers.
227            int nameLen = name.length();
228            char[] nameChars = name.toCharArray();
229            int idx = name.length();
230            for (;;) {
231                Name nameImpl = names.fromChars(nameChars, 0, nameLen);
232                ModuleSymbol mod = syms.inferModule(Convert.packagePart(nameImpl));
233                ClassSymbol s = mod != null ? syms.getClass(mod, nameImpl) : null;
234                if (s != null)
235                    return s; // found it!
236                idx = name.substring(0, idx).lastIndexOf('.');
237                if (idx < 0) break;
238                nameChars[idx] = '$';
239            }
240            return null;
241        }
242
243    /**
244     * Set the locale.
245     */
246    public void setLocale(String localeName) {
247        // create locale specifics
248        doclocale = new DocLocale(this, localeName, breakiterator);
249        // update Messager if locale has changed.
250        messager.setLocale(doclocale.locale);
251    }
252
253    /** Check whether this member should be documented. */
254    public boolean shouldDocument(VarSymbol sym) {
255        long mod = sym.flags();
256
257        if ((mod & Flags.SYNTHETIC) != 0) {
258            return false;
259        }
260
261        return showAccess.checkModifier(translateModifiers(mod));
262    }
263
264    /** Check whether this member should be documented. */
265    public boolean shouldDocument(MethodSymbol sym) {
266        long mod = sym.flags();
267
268        if ((mod & Flags.SYNTHETIC) != 0) {
269            return false;
270        }
271
272        return showAccess.checkModifier(translateModifiers(mod));
273    }
274
275    /** check whether this class should be documented. */
276    public boolean shouldDocument(ClassSymbol sym) {
277        return
278            (sym.flags_field&Flags.SYNTHETIC) == 0 && // no synthetics
279            (docClasses || getClassDoc(sym).tree != null) &&
280            isVisible(sym);
281    }
282
283    //### Comment below is inaccurate wrt modifier filter testing
284    /**
285     * Check the visibility if this is an nested class.
286     * if this is not a nested class, return true.
287     * if this is an static visible nested class,
288     *    return true.
289     * if this is an visible nested class
290     *    if the outer class is visible return true.
291     *    else return false.
292     * IMPORTANT: This also allows, static nested classes
293     * to be defined inside an nested class, which is not
294     * allowed by the compiler. So such an test case will
295     * not reach upto this method itself, but if compiler
296     * allows it, then that will go through.
297     */
298    protected boolean isVisible(ClassSymbol sym) {
299        long mod = sym.flags_field;
300        if (!showAccess.checkModifier(translateModifiers(mod))) {
301            return false;
302        }
303        ClassSymbol encl = sym.owner.enclClass();
304        return (encl == null || (mod & Flags.STATIC) != 0 || isVisible(encl));
305    }
306
307    //---------------- print forwarders ----------------//
308
309    /**
310     * Print error message, increment error count.
311     *
312     * @param msg message to print.
313     */
314    public void printError(String msg) {
315        if (silent)
316            return;
317        messager.printError(msg);
318    }
319
320    /**
321     * Print error message, increment error count.
322     *
323     * @param key selects message from resource
324     */
325    public void error(DocImpl doc, String key) {
326        if (silent)
327            return;
328        messager.error(doc==null ? null : doc.position(), key);
329    }
330
331    /**
332     * Print error message, increment error count.
333     *
334     * @param key selects message from resource
335     */
336    public void error(SourcePosition pos, String key) {
337        if (silent)
338            return;
339        messager.error(pos, key);
340    }
341
342    /**
343     * Print error message, increment error count.
344     *
345     * @param msg message to print.
346     */
347    public void printError(SourcePosition pos, String msg) {
348        if (silent)
349            return;
350        messager.printError(pos, msg);
351    }
352
353    /**
354     * Print error message, increment error count.
355     *
356     * @param key selects message from resource
357     * @param a1 first argument
358     */
359    public void error(DocImpl doc, String key, String a1) {
360        if (silent)
361            return;
362        messager.error(doc==null ? null : doc.position(), key, a1);
363    }
364
365    /**
366     * Print error message, increment error count.
367     *
368     * @param key selects message from resource
369     * @param a1 first argument
370     * @param a2 second argument
371     */
372    public void error(DocImpl doc, String key, String a1, String a2) {
373        if (silent)
374            return;
375        messager.error(doc==null ? null : doc.position(), key, a1, a2);
376    }
377
378    /**
379     * Print error message, increment error count.
380     *
381     * @param key selects message from resource
382     * @param a1 first argument
383     * @param a2 second argument
384     * @param a3 third argument
385     */
386    public void error(DocImpl doc, String key, String a1, String a2, String a3) {
387        if (silent)
388            return;
389        messager.error(doc==null ? null : doc.position(), key, a1, a2, a3);
390    }
391
392    /**
393     * Print warning message, increment warning count.
394     *
395     * @param msg message to print.
396     */
397    public void printWarning(String msg) {
398        if (silent)
399            return;
400        messager.printWarning(msg);
401    }
402
403    /**
404     * Print warning message, increment warning count.
405     *
406     * @param key selects message from resource
407     */
408    public void warning(DocImpl doc, String key) {
409        if (silent)
410            return;
411        messager.warning(doc==null ? null : doc.position(), key);
412    }
413
414    /**
415     * Print warning message, increment warning count.
416     *
417     * @param msg message to print.
418     */
419    public void printWarning(SourcePosition pos, String msg) {
420        if (silent)
421            return;
422        messager.printWarning(pos, msg);
423    }
424
425    /**
426     * Print warning message, increment warning count.
427     *
428     * @param key selects message from resource
429     * @param a1 first argument
430     */
431    public void warning(DocImpl doc, String key, String a1) {
432        if (silent)
433            return;
434        // suppress messages that have (probably) been covered by doclint
435        if (doclint != null && doc != null && key.startsWith("tag"))
436            return;
437        messager.warning(doc==null ? null : doc.position(), key, a1);
438    }
439
440    /**
441     * Print warning message, increment warning count.
442     *
443     * @param key selects message from resource
444     * @param a1 first argument
445     * @param a2 second argument
446     */
447    public void warning(DocImpl doc, String key, String a1, String a2) {
448        if (silent)
449            return;
450        messager.warning(doc==null ? null : doc.position(), key, a1, a2);
451    }
452
453    /**
454     * Print warning message, increment warning count.
455     *
456     * @param key selects message from resource
457     * @param a1 first argument
458     * @param a2 second argument
459     * @param a3 third argument
460     */
461    public void warning(DocImpl doc, String key, String a1, String a2, String a3) {
462        if (silent)
463            return;
464        messager.warning(doc==null ? null : doc.position(), key, a1, a2, a3);
465    }
466
467    /**
468     * Print warning message, increment warning count.
469     *
470     * @param key selects message from resource
471     * @param a1 first argument
472     * @param a2 second argument
473     * @param a3 third argument
474     */
475    public void warning(DocImpl doc, String key, String a1, String a2, String a3,
476                        String a4) {
477        if (silent)
478            return;
479        messager.warning(doc==null ? null : doc.position(), key, a1, a2, a3, a4);
480    }
481
482    /**
483     * Print a message.
484     *
485     * @param msg message to print.
486     */
487    public void printNotice(String msg) {
488        if (silent || quiet)
489            return;
490        messager.printNotice(msg);
491    }
492
493
494    /**
495     * Print a message.
496     *
497     * @param key selects message from resource
498     */
499    public void notice(String key) {
500        if (silent || quiet)
501            return;
502        messager.notice(key);
503    }
504
505    /**
506     * Print a message.
507     *
508     * @param msg message to print.
509     */
510    public void printNotice(SourcePosition pos, String msg) {
511        if (silent || quiet)
512            return;
513        messager.printNotice(pos, msg);
514    }
515
516    /**
517     * Print a message.
518     *
519     * @param key selects message from resource
520     * @param a1 first argument
521     */
522    public void notice(String key, String a1) {
523        if (silent || quiet)
524            return;
525        messager.notice(key, a1);
526    }
527
528    /**
529     * Print a message.
530     *
531     * @param key selects message from resource
532     * @param a1 first argument
533     * @param a2 second argument
534     */
535    public void notice(String key, String a1, String a2) {
536        if (silent || quiet)
537            return;
538        messager.notice(key, a1, a2);
539    }
540
541    /**
542     * Print a message.
543     *
544     * @param key selects message from resource
545     * @param a1 first argument
546     * @param a2 second argument
547     * @param a3 third argument
548     */
549    public void notice(String key, String a1, String a2, String a3) {
550        if (silent || quiet)
551            return;
552        messager.notice(key, a1, a2, a3);
553    }
554
555    /**
556     * Exit, reporting errors and warnings.
557     */
558    public void exit() {
559        // Messager should be replaced by a more general
560        // compilation environment.  This can probably
561        // subsume DocEnv as well.
562        messager.exit();
563    }
564
565    protected Map<PackageSymbol, PackageDocImpl> packageMap = new HashMap<>();
566    /**
567     * Return the PackageDoc of this package symbol.
568     */
569    public PackageDocImpl getPackageDoc(PackageSymbol pack) {
570        PackageDocImpl result = packageMap.get(pack);
571        if (result != null) return result;
572        result = new PackageDocImpl(this, pack);
573        packageMap.put(pack, result);
574        return result;
575    }
576
577    /**
578     * Create the PackageDoc (or a subtype) for a package symbol.
579     */
580    void makePackageDoc(PackageSymbol pack, TreePath treePath) {
581        PackageDocImpl result = packageMap.get(pack);
582        if (result != null) {
583            if (treePath != null) result.setTreePath(treePath);
584        } else {
585            result = new PackageDocImpl(this, pack, treePath);
586            packageMap.put(pack, result);
587        }
588    }
589
590
591    protected Map<ClassSymbol, ClassDocImpl> classMap = new HashMap<>();
592    /**
593     * Return the ClassDoc (or a subtype) of this class symbol.
594     */
595    public ClassDocImpl getClassDoc(ClassSymbol clazz) {
596        ClassDocImpl result = classMap.get(clazz);
597        if (result != null) return result;
598        if (isAnnotationType(clazz)) {
599            result = new AnnotationTypeDocImpl(this, clazz);
600        } else {
601            result = new ClassDocImpl(this, clazz);
602        }
603        classMap.put(clazz, result);
604        return result;
605    }
606
607    /**
608     * Create the ClassDoc (or a subtype) for a class symbol.
609     */
610    protected void makeClassDoc(ClassSymbol clazz, TreePath treePath) {
611        ClassDocImpl result = classMap.get(clazz);
612        if (result != null) {
613            if (treePath != null) result.setTreePath(treePath);
614            return;
615        }
616        if (isAnnotationType((JCClassDecl) treePath.getLeaf())) {   // flags of clazz may not yet be set
617            result = new AnnotationTypeDocImpl(this, clazz, treePath);
618        } else {
619            result = new ClassDocImpl(this, clazz, treePath);
620        }
621        classMap.put(clazz, result);
622    }
623
624    protected static boolean isAnnotationType(ClassSymbol clazz) {
625        return ClassDocImpl.isAnnotationType(clazz);
626    }
627
628    protected static boolean isAnnotationType(JCClassDecl tree) {
629        return (tree.mods.flags & Flags.ANNOTATION) != 0;
630    }
631
632    protected Map<VarSymbol, FieldDocImpl> fieldMap = new HashMap<>();
633    /**
634     * Return the FieldDoc of this var symbol.
635     */
636    public FieldDocImpl getFieldDoc(VarSymbol var) {
637        FieldDocImpl result = fieldMap.get(var);
638        if (result != null) return result;
639        result = new FieldDocImpl(this, var);
640        fieldMap.put(var, result);
641        return result;
642    }
643    /**
644     * Create a FieldDoc for a var symbol.
645     */
646    protected void makeFieldDoc(VarSymbol var, TreePath treePath) {
647        FieldDocImpl result = fieldMap.get(var);
648        if (result != null) {
649            if (treePath != null) result.setTreePath(treePath);
650        } else {
651            result = new FieldDocImpl(this, var, treePath);
652            fieldMap.put(var, result);
653        }
654    }
655
656    protected Map<MethodSymbol, ExecutableMemberDocImpl> methodMap = new HashMap<>();
657    /**
658     * Create a MethodDoc for this MethodSymbol.
659     * Should be called only on symbols representing methods.
660     */
661    protected void makeMethodDoc(MethodSymbol meth, TreePath treePath) {
662        MethodDocImpl result = (MethodDocImpl)methodMap.get(meth);
663        if (result != null) {
664            if (treePath != null) result.setTreePath(treePath);
665        } else {
666            result = new MethodDocImpl(this, meth, treePath);
667            methodMap.put(meth, result);
668        }
669    }
670
671    /**
672     * Return the MethodDoc for a MethodSymbol.
673     * Should be called only on symbols representing methods.
674     */
675    public MethodDocImpl getMethodDoc(MethodSymbol meth) {
676        assert !meth.isConstructor() : "not expecting a constructor symbol";
677        MethodDocImpl result = (MethodDocImpl)methodMap.get(meth);
678        if (result != null) return result;
679        result = new MethodDocImpl(this, meth);
680        methodMap.put(meth, result);
681        return result;
682    }
683
684    /**
685     * Create the ConstructorDoc for a MethodSymbol.
686     * Should be called only on symbols representing constructors.
687     */
688    protected void makeConstructorDoc(MethodSymbol meth, TreePath treePath) {
689        ConstructorDocImpl result = (ConstructorDocImpl)methodMap.get(meth);
690        if (result != null) {
691            if (treePath != null) result.setTreePath(treePath);
692        } else {
693            result = new ConstructorDocImpl(this, meth, treePath);
694            methodMap.put(meth, result);
695        }
696    }
697
698    /**
699     * Return the ConstructorDoc for a MethodSymbol.
700     * Should be called only on symbols representing constructors.
701     */
702    public ConstructorDocImpl getConstructorDoc(MethodSymbol meth) {
703        assert meth.isConstructor() : "expecting a constructor symbol";
704        ConstructorDocImpl result = (ConstructorDocImpl)methodMap.get(meth);
705        if (result != null) return result;
706        result = new ConstructorDocImpl(this, meth);
707        methodMap.put(meth, result);
708        return result;
709    }
710
711    /**
712     * Create the AnnotationTypeElementDoc for a MethodSymbol.
713     * Should be called only on symbols representing annotation type elements.
714     */
715    protected void makeAnnotationTypeElementDoc(MethodSymbol meth, TreePath treePath) {
716        AnnotationTypeElementDocImpl result =
717            (AnnotationTypeElementDocImpl)methodMap.get(meth);
718        if (result != null) {
719            if (treePath != null) result.setTreePath(treePath);
720        } else {
721            result =
722                new AnnotationTypeElementDocImpl(this, meth, treePath);
723            methodMap.put(meth, result);
724        }
725    }
726
727    /**
728     * Return the AnnotationTypeElementDoc for a MethodSymbol.
729     * Should be called only on symbols representing annotation type elements.
730     */
731    public AnnotationTypeElementDocImpl getAnnotationTypeElementDoc(
732            MethodSymbol meth) {
733
734        AnnotationTypeElementDocImpl result =
735            (AnnotationTypeElementDocImpl)methodMap.get(meth);
736        if (result != null) return result;
737        result = new AnnotationTypeElementDocImpl(this, meth);
738        methodMap.put(meth, result);
739        return result;
740    }
741
742//  private Map<ClassType, ParameterizedTypeImpl> parameterizedTypeMap =
743//          new HashMap<ClassType, ParameterizedTypeImpl>();
744    /**
745     * Return the ParameterizedType of this instantiation.
746//   * ### Could use Type.sameTypeAs() instead of equality matching in hashmap
747//   * ### to avoid some duplication.
748     */
749    ParameterizedTypeImpl getParameterizedType(ClassType t) {
750        return new ParameterizedTypeImpl(this, t);
751//      ParameterizedTypeImpl result = parameterizedTypeMap.get(t);
752//      if (result != null) return result;
753//      result = new ParameterizedTypeImpl(this, t);
754//      parameterizedTypeMap.put(t, result);
755//      return result;
756    }
757
758    TreePath getTreePath(JCCompilationUnit tree) {
759        TreePath p = treePaths.get(tree);
760        if (p == null)
761            treePaths.put(tree, p = new TreePath(tree));
762        return p;
763    }
764
765    TreePath getTreePath(JCCompilationUnit toplevel, JCPackageDecl tree) {
766        TreePath p = treePaths.get(tree);
767        if (p == null)
768            treePaths.put(tree, p = new TreePath(getTreePath(toplevel), tree));
769        return p;
770    }
771
772    TreePath getTreePath(JCCompilationUnit toplevel, JCClassDecl tree) {
773        TreePath p = treePaths.get(tree);
774        if (p == null)
775            treePaths.put(tree, p = new TreePath(getTreePath(toplevel), tree));
776        return p;
777    }
778
779    TreePath getTreePath(JCCompilationUnit toplevel, JCClassDecl cdecl, JCTree tree) {
780        return new TreePath(getTreePath(toplevel, cdecl), tree);
781    }
782
783    /**
784     * Set the encoding.
785     */
786    public void setEncoding(String encoding) {
787        this.encoding = encoding;
788    }
789
790    /**
791     * Get the encoding.
792     */
793    public String getEncoding() {
794        return encoding;
795    }
796
797    /**
798     * Convert modifier bits from private coding used by
799     * the compiler to that of java.lang.reflect.Modifier.
800     */
801    static int translateModifiers(long flags) {
802        int result = 0;
803        if ((flags & Flags.ABSTRACT) != 0)
804            result |= Modifier.ABSTRACT;
805        if ((flags & Flags.FINAL) != 0)
806            result |= Modifier.FINAL;
807        if ((flags & Flags.INTERFACE) != 0)
808            result |= Modifier.INTERFACE;
809        if ((flags & Flags.NATIVE) != 0)
810            result |= Modifier.NATIVE;
811        if ((flags & Flags.PRIVATE) != 0)
812            result |= Modifier.PRIVATE;
813        if ((flags & Flags.PROTECTED) != 0)
814            result |= Modifier.PROTECTED;
815        if ((flags & Flags.PUBLIC) != 0)
816            result |= Modifier.PUBLIC;
817        if ((flags & Flags.STATIC) != 0)
818            result |= Modifier.STATIC;
819        if ((flags & Flags.SYNCHRONIZED) != 0)
820            result |= Modifier.SYNCHRONIZED;
821        if ((flags & Flags.TRANSIENT) != 0)
822            result |= Modifier.TRANSIENT;
823        if ((flags & Flags.VOLATILE) != 0)
824            result |= Modifier.VOLATILE;
825        return result;
826    }
827
828    void initDoclint(Collection<String> opts, Collection<String> customTagNames, String htmlVersion) {
829        ArrayList<String> doclintOpts = new ArrayList<>();
830        boolean msgOptionSeen = false;
831
832        for (String opt : opts) {
833            if (opt.startsWith(DocLint.XMSGS_OPTION)) {
834                if (opt.equals(DocLint.XMSGS_CUSTOM_PREFIX + "none"))
835                    return;
836                msgOptionSeen = true;
837            }
838            doclintOpts.add(opt);
839        }
840
841        if (!msgOptionSeen) {
842            doclintOpts.add(DocLint.XMSGS_OPTION);
843        }
844
845        String sep = "";
846        StringBuilder customTags = new StringBuilder();
847        for (String customTag : customTagNames) {
848            customTags.append(sep);
849            customTags.append(customTag);
850            sep = DocLint.SEPARATOR;
851        }
852        doclintOpts.add(DocLint.XCUSTOM_TAGS_PREFIX + customTags.toString());
853        doclintOpts.add(DocLint.XHTML_VERSION_PREFIX + htmlVersion);
854
855        JavacTask t = BasicJavacTask.instance(context);
856        doclint = new DocLint();
857        // standard doclet normally generates H1, H2
858        doclintOpts.add(DocLint.XIMPLICIT_HEADERS + "2");
859        doclint.init(t, doclintOpts.toArray(new String[doclintOpts.size()]), false);
860    }
861
862    JavaScriptScanner initJavaScriptScanner(boolean allowScriptInComments) {
863        if (allowScriptInComments) {
864            javaScriptScanner = null;
865        } else {
866            javaScriptScanner = new JavaScriptScanner();
867        }
868        return javaScriptScanner;
869    }
870
871    boolean showTagMessages() {
872        return (doclint == null);
873    }
874
875    Map<CompilationUnitTree, Boolean> shouldCheck = new HashMap<>();
876
877    boolean shouldCheck(CompilationUnitTree unit) {
878        return shouldCheck.computeIfAbsent(unit, doclint :: shouldCheck);
879    }
880}
881