/* * Copyright (c) 2000, 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.tools.javadoc.main; import java.lang.reflect.Modifier; import java.util.*; import javax.tools.JavaFileManager; import com.sun.javadoc.*; import com.sun.source.tree.CompilationUnitTree; import com.sun.source.util.JavacTask; import com.sun.source.util.TreePath; import com.sun.tools.doclint.DocLint; import com.sun.tools.javac.api.BasicJavacTask; import com.sun.tools.javac.code.*; import com.sun.tools.javac.code.Symbol.*; import com.sun.tools.javac.code.Symbol.ClassSymbol; import com.sun.tools.javac.code.Symbol.CompletionFailure; import com.sun.tools.javac.code.Symbol.MethodSymbol; import com.sun.tools.javac.code.Symbol.PackageSymbol; import com.sun.tools.javac.code.Symbol.VarSymbol; import com.sun.tools.javac.code.Type.ClassType; import com.sun.tools.javac.comp.Check; import com.sun.tools.javac.comp.Enter; import com.sun.tools.javac.file.JavacFileManager; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCClassDecl; import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; import com.sun.tools.javac.tree.JCTree.JCPackageDecl; import com.sun.tools.javac.util.Context; import com.sun.tools.javac.util.Convert; import com.sun.tools.javac.util.Name; import com.sun.tools.javac.util.Names; /** * Holds the environment for a run of javadoc. * Holds only the information needed throughout the * run and not the compiler info that could be GC'ed * or ported. * * <p><b>This is NOT part of any supported API. * If you write code that depends on this, you do so at your own risk. * This code and its internal interfaces are subject to change or * deletion without notice.</b> * * @since 1.4 * @author Robert Field * @author Neal Gafter (rewrite) * @author Scott Seligman (generics) */ @Deprecated public class DocEnv { protected static final Context.Key<DocEnv> docEnvKey = new Context.Key<>(); public static DocEnv instance(Context context) { DocEnv instance = context.get(docEnvKey); if (instance == null) instance = new DocEnv(context); return instance; } DocLocale doclocale; private final Messager messager; /** Predefined symbols known to the compiler. */ final Symtab syms; /** Referenced directly in RootDocImpl. */ private final ClassFinder finder; /** Javadoc's own version of the compiler's enter phase. */ final Enter enter; /** The name table. */ private Names names; /** The encoding name. */ private String encoding; final Symbol externalizableSym; /** Access filter (public, protected, ...). */ protected ModifierFilter showAccess; /** True if we are using a sentence BreakIterator. */ boolean breakiterator; /** * True if we do not want to print any notifications at all. */ boolean quiet = false; Check chk; Types types; JavaFileManager fileManager; Context context; DocLint doclint; WeakHashMap<JCTree, TreePath> treePaths = new WeakHashMap<>(); /** Allow documenting from class files? */ boolean docClasses = false; /** Does the doclet only expect pre-1.5 doclet API? */ protected boolean legacyDoclet = true; /** * Set this to true if you would like to not emit any errors, warnings and * notices. */ private boolean silent = false; /** * The source language version. */ protected Source source; /** * Constructor * * @param context Context for this javadoc instance. */ protected DocEnv(Context context) { context.put(docEnvKey, this); this.context = context; messager = Messager.instance0(context); syms = Symtab.instance(context); finder = JavadocClassFinder.instance(context); enter = JavadocEnter.instance(context); names = Names.instance(context); externalizableSym = syms.enterClass(syms.java_base, names.fromString("java.io.Externalizable")); chk = Check.instance(context); types = Types.instance(context); fileManager = context.get(JavaFileManager.class); if (fileManager instanceof JavacFileManager) { ((JavacFileManager)fileManager).setSymbolFileEnabled(false); } // Default. Should normally be reset with setLocale. this.doclocale = new DocLocale(this, "", breakiterator); source = Source.instance(context); } public void setSilent(boolean silent) { this.silent = silent; } /** * Look up ClassDoc by qualified name. */ public ClassDocImpl lookupClass(String name) { ClassSymbol c = getClassSymbol(name); if (c != null) { return getClassDoc(c); } else { return null; } } /** * Load ClassDoc by qualified name. */ public ClassDocImpl loadClass(String name) { try { Name nameImpl = names.fromString(name); ModuleSymbol mod = syms.inferModule(Convert.packagePart(nameImpl)); ClassSymbol c = finder.loadClass(mod != null ? mod : syms.errModule, nameImpl); return getClassDoc(c); } catch (CompletionFailure ex) { chk.completionError(null, ex); return null; } } /** * Look up PackageDoc by qualified name. */ public PackageDocImpl lookupPackage(String name) { //### Jing alleges that class check is needed //### to avoid a compiler bug. Most likely //### instead a dummy created for error recovery. //### Should investigate this. Name nameImpl = names.fromString(name); ModuleSymbol mod = syms.inferModule(nameImpl); PackageSymbol p = mod != null ? syms.getPackage(mod, nameImpl) : null; ClassSymbol c = getClassSymbol(name); if (p != null && c == null) { return getPackageDoc(p); } else { return null; } } // where /** Retrieve class symbol by fully-qualified name. */ ClassSymbol getClassSymbol(String name) { // Name may contain nested class qualification. // Generate candidate flatnames with successively shorter // package qualifiers and longer nested class qualifiers. int nameLen = name.length(); char[] nameChars = name.toCharArray(); int idx = name.length(); for (;;) { Name nameImpl = names.fromChars(nameChars, 0, nameLen); ModuleSymbol mod = syms.inferModule(Convert.packagePart(nameImpl)); ClassSymbol s = mod != null ? syms.getClass(mod, nameImpl) : null; if (s != null) return s; // found it! idx = name.substring(0, idx).lastIndexOf('.'); if (idx < 0) break; nameChars[idx] = '$'; } return null; } /** * Set the locale. */ public void setLocale(String localeName) { // create locale specifics doclocale = new DocLocale(this, localeName, breakiterator); // update Messager if locale has changed. messager.setLocale(doclocale.locale); } /** Check whether this member should be documented. */ public boolean shouldDocument(VarSymbol sym) { long mod = sym.flags(); if ((mod & Flags.SYNTHETIC) != 0) { return false; } return showAccess.checkModifier(translateModifiers(mod)); } /** Check whether this member should be documented. */ public boolean shouldDocument(MethodSymbol sym) { long mod = sym.flags(); if ((mod & Flags.SYNTHETIC) != 0) { return false; } return showAccess.checkModifier(translateModifiers(mod)); } /** check whether this class should be documented. */ public boolean shouldDocument(ClassSymbol sym) { return (sym.flags_field&Flags.SYNTHETIC) == 0 && // no synthetics (docClasses || getClassDoc(sym).tree != null) && isVisible(sym); } //### Comment below is inaccurate wrt modifier filter testing /** * Check the visibility if this is an nested class. * if this is not a nested class, return true. * if this is an static visible nested class, * return true. * if this is an visible nested class * if the outer class is visible return true. * else return false. * IMPORTANT: This also allows, static nested classes * to be defined inside an nested class, which is not * allowed by the compiler. So such an test case will * not reach upto this method itself, but if compiler * allows it, then that will go through. */ protected boolean isVisible(ClassSymbol sym) { long mod = sym.flags_field; if (!showAccess.checkModifier(translateModifiers(mod))) { return false; } ClassSymbol encl = sym.owner.enclClass(); return (encl == null || (mod & Flags.STATIC) != 0 || isVisible(encl)); } //---------------- print forwarders ----------------// /** * Print error message, increment error count. * * @param msg message to print. */ public void printError(String msg) { if (silent) return; messager.printError(msg); } /** * Print error message, increment error count. * * @param key selects message from resource */ public void error(DocImpl doc, String key) { if (silent) return; messager.error(doc==null ? null : doc.position(), key); } /** * Print error message, increment error count. * * @param key selects message from resource */ public void error(SourcePosition pos, String key) { if (silent) return; messager.error(pos, key); } /** * Print error message, increment error count. * * @param msg message to print. */ public void printError(SourcePosition pos, String msg) { if (silent) return; messager.printError(pos, msg); } /** * Print error message, increment error count. * * @param key selects message from resource * @param a1 first argument */ public void error(DocImpl doc, String key, String a1) { if (silent) return; messager.error(doc==null ? null : doc.position(), key, a1); } /** * Print error message, increment error count. * * @param key selects message from resource * @param a1 first argument * @param a2 second argument */ public void error(DocImpl doc, String key, String a1, String a2) { if (silent) return; messager.error(doc==null ? null : doc.position(), key, a1, a2); } /** * Print error message, increment error count. * * @param key selects message from resource * @param a1 first argument * @param a2 second argument * @param a3 third argument */ public void error(DocImpl doc, String key, String a1, String a2, String a3) { if (silent) return; messager.error(doc==null ? null : doc.position(), key, a1, a2, a3); } /** * Print warning message, increment warning count. * * @param msg message to print. */ public void printWarning(String msg) { if (silent) return; messager.printWarning(msg); } /** * Print warning message, increment warning count. * * @param key selects message from resource */ public void warning(DocImpl doc, String key) { if (silent) return; messager.warning(doc==null ? null : doc.position(), key); } /** * Print warning message, increment warning count. * * @param msg message to print. */ public void printWarning(SourcePosition pos, String msg) { if (silent) return; messager.printWarning(pos, msg); } /** * Print warning message, increment warning count. * * @param key selects message from resource * @param a1 first argument */ public void warning(DocImpl doc, String key, String a1) { if (silent) return; // suppress messages that have (probably) been covered by doclint if (doclint != null && doc != null && key.startsWith("tag")) return; messager.warning(doc==null ? null : doc.position(), key, a1); } /** * Print warning message, increment warning count. * * @param key selects message from resource * @param a1 first argument * @param a2 second argument */ public void warning(DocImpl doc, String key, String a1, String a2) { if (silent) return; messager.warning(doc==null ? null : doc.position(), key, a1, a2); } /** * Print warning message, increment warning count. * * @param key selects message from resource * @param a1 first argument * @param a2 second argument * @param a3 third argument */ public void warning(DocImpl doc, String key, String a1, String a2, String a3) { if (silent) return; messager.warning(doc==null ? null : doc.position(), key, a1, a2, a3); } /** * Print warning message, increment warning count. * * @param key selects message from resource * @param a1 first argument * @param a2 second argument * @param a3 third argument */ public void warning(DocImpl doc, String key, String a1, String a2, String a3, String a4) { if (silent) return; messager.warning(doc==null ? null : doc.position(), key, a1, a2, a3, a4); } /** * Print a message. * * @param msg message to print. */ public void printNotice(String msg) { if (silent || quiet) return; messager.printNotice(msg); } /** * Print a message. * * @param key selects message from resource */ public void notice(String key) { if (silent || quiet) return; messager.notice(key); } /** * Print a message. * * @param msg message to print. */ public void printNotice(SourcePosition pos, String msg) { if (silent || quiet) return; messager.printNotice(pos, msg); } /** * Print a message. * * @param key selects message from resource * @param a1 first argument */ public void notice(String key, String a1) { if (silent || quiet) return; messager.notice(key, a1); } /** * Print a message. * * @param key selects message from resource * @param a1 first argument * @param a2 second argument */ public void notice(String key, String a1, String a2) { if (silent || quiet) return; messager.notice(key, a1, a2); } /** * Print a message. * * @param key selects message from resource * @param a1 first argument * @param a2 second argument * @param a3 third argument */ public void notice(String key, String a1, String a2, String a3) { if (silent || quiet) return; messager.notice(key, a1, a2, a3); } /** * Exit, reporting errors and warnings. */ public void exit() { // Messager should be replaced by a more general // compilation environment. This can probably // subsume DocEnv as well. messager.exit(); } protected Map<PackageSymbol, PackageDocImpl> packageMap = new HashMap<>(); /** * Return the PackageDoc of this package symbol. */ public PackageDocImpl getPackageDoc(PackageSymbol pack) { PackageDocImpl result = packageMap.get(pack); if (result != null) return result; result = new PackageDocImpl(this, pack); packageMap.put(pack, result); return result; } /** * Create the PackageDoc (or a subtype) for a package symbol. */ void makePackageDoc(PackageSymbol pack, TreePath treePath) { PackageDocImpl result = packageMap.get(pack); if (result != null) { if (treePath != null) result.setTreePath(treePath); } else { result = new PackageDocImpl(this, pack, treePath); packageMap.put(pack, result); } } protected Map<ClassSymbol, ClassDocImpl> classMap = new HashMap<>(); /** * Return the ClassDoc (or a subtype) of this class symbol. */ public ClassDocImpl getClassDoc(ClassSymbol clazz) { ClassDocImpl result = classMap.get(clazz); if (result != null) return result; if (isAnnotationType(clazz)) { result = new AnnotationTypeDocImpl(this, clazz); } else { result = new ClassDocImpl(this, clazz); } classMap.put(clazz, result); return result; } /** * Create the ClassDoc (or a subtype) for a class symbol. */ protected void makeClassDoc(ClassSymbol clazz, TreePath treePath) { ClassDocImpl result = classMap.get(clazz); if (result != null) { if (treePath != null) result.setTreePath(treePath); return; } if (isAnnotationType((JCClassDecl) treePath.getLeaf())) { // flags of clazz may not yet be set result = new AnnotationTypeDocImpl(this, clazz, treePath); } else { result = new ClassDocImpl(this, clazz, treePath); } classMap.put(clazz, result); } protected static boolean isAnnotationType(ClassSymbol clazz) { return ClassDocImpl.isAnnotationType(clazz); } protected static boolean isAnnotationType(JCClassDecl tree) { return (tree.mods.flags & Flags.ANNOTATION) != 0; } protected Map<VarSymbol, FieldDocImpl> fieldMap = new HashMap<>(); /** * Return the FieldDoc of this var symbol. */ public FieldDocImpl getFieldDoc(VarSymbol var) { FieldDocImpl result = fieldMap.get(var); if (result != null) return result; result = new FieldDocImpl(this, var); fieldMap.put(var, result); return result; } /** * Create a FieldDoc for a var symbol. */ protected void makeFieldDoc(VarSymbol var, TreePath treePath) { FieldDocImpl result = fieldMap.get(var); if (result != null) { if (treePath != null) result.setTreePath(treePath); } else { result = new FieldDocImpl(this, var, treePath); fieldMap.put(var, result); } } protected Map<MethodSymbol, ExecutableMemberDocImpl> methodMap = new HashMap<>(); /** * Create a MethodDoc for this MethodSymbol. * Should be called only on symbols representing methods. */ protected void makeMethodDoc(MethodSymbol meth, TreePath treePath) { MethodDocImpl result = (MethodDocImpl)methodMap.get(meth); if (result != null) { if (treePath != null) result.setTreePath(treePath); } else { result = new MethodDocImpl(this, meth, treePath); methodMap.put(meth, result); } } /** * Return the MethodDoc for a MethodSymbol. * Should be called only on symbols representing methods. */ public MethodDocImpl getMethodDoc(MethodSymbol meth) { assert !meth.isConstructor() : "not expecting a constructor symbol"; MethodDocImpl result = (MethodDocImpl)methodMap.get(meth); if (result != null) return result; result = new MethodDocImpl(this, meth); methodMap.put(meth, result); return result; } /** * Create the ConstructorDoc for a MethodSymbol. * Should be called only on symbols representing constructors. */ protected void makeConstructorDoc(MethodSymbol meth, TreePath treePath) { ConstructorDocImpl result = (ConstructorDocImpl)methodMap.get(meth); if (result != null) { if (treePath != null) result.setTreePath(treePath); } else { result = new ConstructorDocImpl(this, meth, treePath); methodMap.put(meth, result); } } /** * Return the ConstructorDoc for a MethodSymbol. * Should be called only on symbols representing constructors. */ public ConstructorDocImpl getConstructorDoc(MethodSymbol meth) { assert meth.isConstructor() : "expecting a constructor symbol"; ConstructorDocImpl result = (ConstructorDocImpl)methodMap.get(meth); if (result != null) return result; result = new ConstructorDocImpl(this, meth); methodMap.put(meth, result); return result; } /** * Create the AnnotationTypeElementDoc for a MethodSymbol. * Should be called only on symbols representing annotation type elements. */ protected void makeAnnotationTypeElementDoc(MethodSymbol meth, TreePath treePath) { AnnotationTypeElementDocImpl result = (AnnotationTypeElementDocImpl)methodMap.get(meth); if (result != null) { if (treePath != null) result.setTreePath(treePath); } else { result = new AnnotationTypeElementDocImpl(this, meth, treePath); methodMap.put(meth, result); } } /** * Return the AnnotationTypeElementDoc for a MethodSymbol. * Should be called only on symbols representing annotation type elements. */ public AnnotationTypeElementDocImpl getAnnotationTypeElementDoc( MethodSymbol meth) { AnnotationTypeElementDocImpl result = (AnnotationTypeElementDocImpl)methodMap.get(meth); if (result != null) return result; result = new AnnotationTypeElementDocImpl(this, meth); methodMap.put(meth, result); return result; } // private Map<ClassType, ParameterizedTypeImpl> parameterizedTypeMap = // new HashMap<ClassType, ParameterizedTypeImpl>(); /** * Return the ParameterizedType of this instantiation. // * ### Could use Type.sameTypeAs() instead of equality matching in hashmap // * ### to avoid some duplication. */ ParameterizedTypeImpl getParameterizedType(ClassType t) { return new ParameterizedTypeImpl(this, t); // ParameterizedTypeImpl result = parameterizedTypeMap.get(t); // if (result != null) return result; // result = new ParameterizedTypeImpl(this, t); // parameterizedTypeMap.put(t, result); // return result; } TreePath getTreePath(JCCompilationUnit tree) { TreePath p = treePaths.get(tree); if (p == null) treePaths.put(tree, p = new TreePath(tree)); return p; } TreePath getTreePath(JCCompilationUnit toplevel, JCPackageDecl tree) { TreePath p = treePaths.get(tree); if (p == null) treePaths.put(tree, p = new TreePath(getTreePath(toplevel), tree)); return p; } TreePath getTreePath(JCCompilationUnit toplevel, JCClassDecl tree) { TreePath p = treePaths.get(tree); if (p == null) treePaths.put(tree, p = new TreePath(getTreePath(toplevel), tree)); return p; } TreePath getTreePath(JCCompilationUnit toplevel, JCClassDecl cdecl, JCTree tree) { return new TreePath(getTreePath(toplevel, cdecl), tree); } /** * Set the encoding. */ public void setEncoding(String encoding) { this.encoding = encoding; } /** * Get the encoding. */ public String getEncoding() { return encoding; } /** * Convert modifier bits from private coding used by * the compiler to that of java.lang.reflect.Modifier. */ static int translateModifiers(long flags) { int result = 0; if ((flags & Flags.ABSTRACT) != 0) result |= Modifier.ABSTRACT; if ((flags & Flags.FINAL) != 0) result |= Modifier.FINAL; if ((flags & Flags.INTERFACE) != 0) result |= Modifier.INTERFACE; if ((flags & Flags.NATIVE) != 0) result |= Modifier.NATIVE; if ((flags & Flags.PRIVATE) != 0) result |= Modifier.PRIVATE; if ((flags & Flags.PROTECTED) != 0) result |= Modifier.PROTECTED; if ((flags & Flags.PUBLIC) != 0) result |= Modifier.PUBLIC; if ((flags & Flags.STATIC) != 0) result |= Modifier.STATIC; if ((flags & Flags.SYNCHRONIZED) != 0) result |= Modifier.SYNCHRONIZED; if ((flags & Flags.TRANSIENT) != 0) result |= Modifier.TRANSIENT; if ((flags & Flags.VOLATILE) != 0) result |= Modifier.VOLATILE; return result; } void initDoclint(Collection<String> opts, Collection<String> customTagNames, String htmlVersion) { ArrayList<String> doclintOpts = new ArrayList<>(); boolean msgOptionSeen = false; for (String opt : opts) { if (opt.startsWith(DocLint.XMSGS_OPTION)) { if (opt.equals(DocLint.XMSGS_CUSTOM_PREFIX + "none")) return; msgOptionSeen = true; } doclintOpts.add(opt); } if (!msgOptionSeen) { doclintOpts.add(DocLint.XMSGS_OPTION); } String sep = ""; StringBuilder customTags = new StringBuilder(); for (String customTag : customTagNames) { customTags.append(sep); customTags.append(customTag); sep = DocLint.SEPARATOR; } doclintOpts.add(DocLint.XCUSTOM_TAGS_PREFIX + customTags.toString()); doclintOpts.add(DocLint.XHTML_VERSION_PREFIX + htmlVersion); JavacTask t = BasicJavacTask.instance(context); doclint = new DocLint(); // standard doclet normally generates H1, H2 doclintOpts.add(DocLint.XIMPLICIT_HEADERS + "2"); doclint.init(t, doclintOpts.toArray(new String[doclintOpts.size()]), false); } boolean showTagMessages() { return (doclint == null); } Map<CompilationUnitTree, Boolean> shouldCheck = new HashMap<>(); boolean shouldCheck(CompilationUnitTree unit) { return shouldCheck.computeIfAbsent(unit, doclint :: shouldCheck); } }