langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/WorkArounds.java
changeset 35426 374342e56a56
child 36526 3b41f1c69604
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/src/jdk.javadoc/share/classes/jdk/javadoc/internal/doclets/toolkit/WorkArounds.java	Sat Nov 28 18:52:17 2015 -0800
@@ -0,0 +1,535 @@
+/*
+ * Copyright (c) 2015, 2016, 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 jdk.javadoc.internal.doclets.toolkit;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.PackageElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+import javax.lang.model.type.TypeMirror;
+import javax.lang.model.util.Elements;
+import javax.tools.JavaFileObject;
+
+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.Attribute;
+import com.sun.tools.javac.code.Flags;
+import com.sun.tools.javac.code.Scope;
+import com.sun.tools.javac.code.Symbol;
+import com.sun.tools.javac.code.Symbol.ClassSymbol;
+import com.sun.tools.javac.code.Symbol.MethodSymbol;
+import com.sun.tools.javac.code.Symbol.VarSymbol;
+import com.sun.tools.javac.comp.AttrContext;
+import com.sun.tools.javac.comp.Env;
+import com.sun.tools.javac.model.JavacTypes;
+import com.sun.tools.javac.util.Names;
+
+import jdk.javadoc.internal.doclets.toolkit.util.Utils;
+import jdk.javadoc.internal.tool.DocEnv;
+import jdk.javadoc.internal.tool.RootDocImpl;
+
+import static com.sun.tools.javac.code.Kinds.Kind.*;
+import static com.sun.tools.javac.code.Scope.LookupKind.NON_RECURSIVE;
+import static javax.lang.model.element.ElementKind.*;
+
+/**
+ * A quarantine class to isolate all the workarounds and bridges to
+ * a locality. This class should eventually disappear once all the
+ * standard APIs support the needed interfaces.
+ *
+ *
+ *  <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>
+ */
+public class WorkArounds {
+
+    public final Configuration configuration;
+    public final DocEnv env;
+    public final Utils utils;
+
+    private DocLint doclint;
+
+    public WorkArounds(Configuration configuration) {
+        this.configuration = configuration;
+        this.utils = this.configuration.utils;
+        this.env = ((RootDocImpl)this.configuration.root).env;
+    }
+
+    Map<CompilationUnitTree, Boolean> shouldCheck = new HashMap<>();
+    // TODO: fix this up correctly
+    public void runDocLint(TreePath path) {
+        CompilationUnitTree unit = path.getCompilationUnit();
+        if (doclint != null && shouldCheck.computeIfAbsent(unit, doclint::shouldCheck)) {
+            doclint.scan(path);
+        }
+    }
+
+    // TODO: fix this up correctly
+    public 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(env.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);
+    }
+
+    // TODO: fix this up correctly
+    public boolean haveDocLint() {
+        return (doclint == null);
+    }
+
+    // TODO: jx.l.m directSuperTypes don't work for things like Enum,
+    // so we use javac directly, investigate why jx.l.m is not cutting it.
+    public List<TypeMirror> interfaceTypesOf(TypeMirror type) {
+        com.sun.tools.javac.util.List<com.sun.tools.javac.code.Type> interfaces =
+                ((RootDocImpl)configuration.root).env.getTypes().interfaces((com.sun.tools.javac.code.Type)type);
+        if (interfaces.isEmpty()) {
+            return Collections.emptyList();
+        }
+        List<TypeMirror> list = new ArrayList<>(interfaces.size());
+        for (com.sun.tools.javac.code.Type t : interfaces) {
+            list.add((TypeMirror)t);
+        }
+        return list;
+    }
+
+    /*
+     * TODO: This method exists because of a bug in javac which does not
+     * handle "@deprecated tag in package-info.java", when this issue
+     * is fixed this method and its uses must be jettisoned.
+     */
+    public boolean isDeprecated0(Element e) {
+        if (!utils.getDeprecatedTrees(e).isEmpty()) {
+            return true;
+        }
+        JavacTypes jctypes = ((RootDocImpl)configuration.root).env.typeutils;
+        TypeMirror deprecatedType = utils.getDeprecatedType();
+        for (AnnotationMirror anno : e.getAnnotationMirrors()) {
+            if (jctypes.isSameType(anno.getAnnotationType().asElement().asType(), deprecatedType))
+                return true;
+        }
+        return false;
+    }
+
+    // TODO: fix jx.l.m add this method.
+    public boolean isSynthesized(AnnotationMirror aDesc) {
+        return ((Attribute)aDesc).isSynthesized();
+    }
+
+    // TODO: implement using jx.l.model
+    public boolean isVisible(TypeElement te) {
+        return env.isVisible((ClassSymbol)te);
+    }
+
+    // TODO: fix the caller
+    public Object getConstValue(VariableElement ve) {
+        return ((VarSymbol)ve).getConstValue();
+    }
+
+    //TODO: DocTrees: Trees.getPath(Element e) is slow a factor 4-5 times.
+    public Map<Element, TreePath> getElementToTreePath() {
+        return env.elementToTreePath;
+    }
+
+    // TODO: needs to ported to jx.l.m.
+    public TypeElement searchClass(TypeElement klass, String className) {
+        // search by qualified name first
+        TypeElement te = configuration.root.getElementUtils().getTypeElement(className);
+        if (te != null) {
+            return te;
+        }
+
+        // search inner classes
+        for (TypeElement ite : utils.getClasses(klass)) {
+            TypeElement innerClass = searchClass(ite, className);
+            if (innerClass != null) {
+                return innerClass;
+            }
+        }
+
+        // check in this package
+        te = utils.findClassInPackageElement(utils.containingPackage(klass), className);
+        if (te != null) {
+            return te;
+        }
+
+        ClassSymbol tsym = (ClassSymbol)klass;
+        // make sure that this symbol has been completed
+        // TODO: do we need this anymore ?
+        if (tsym.completer != null) {
+            tsym.complete();
+        }
+
+        // search imports
+        if (tsym.sourcefile != null) {
+
+            //### This information is available only for source classes.
+            Env<AttrContext> compenv = env.getEnv(tsym);
+            if (compenv == null) {
+                return null;
+            }
+            Names names = tsym.name.table.names;
+            Scope s = compenv.toplevel.namedImportScope;
+            for (Symbol sym : s.getSymbolsByName(names.fromString(className))) {
+                if (sym.kind == TYP) {
+                    return (TypeElement)sym;
+                }
+            }
+
+            s = compenv.toplevel.starImportScope;
+            for (Symbol sym : s.getSymbolsByName(names.fromString(className))) {
+                if (sym.kind == TYP) {
+                    return (TypeElement)sym;
+                }
+            }
+        }
+
+        return null; // not found
+    }
+
+    // TODO:  need to re-implement this using j.l.m. correctly!, this has
+    // implications on testInterface, the note here is that javac's supertype
+    // does the right thing returning Parameters in scope.
+    /**
+     * Return the type containing the method that this method overrides.
+     * It may be a <code>TypeElement</code> or a <code>TypeParameterElement</code>.
+     * @param method target
+     * @return a type
+     */
+    public TypeMirror overriddenType(ExecutableElement method) {
+        if (utils.isStatic(method)) {
+            return null;
+        }
+        MethodSymbol sym = (MethodSymbol)method;
+        ClassSymbol origin = (ClassSymbol) sym.owner;
+        for (com.sun.tools.javac.code.Type t = env.getTypes().supertype(origin.type);
+                t.hasTag(com.sun.tools.javac.code.TypeTag.CLASS);
+                t = env.getTypes().supertype(t)) {
+            ClassSymbol c = (ClassSymbol) t.tsym;
+            for (com.sun.tools.javac.code.Symbol sym2 : c.members().getSymbolsByName(sym.name)) {
+                if (sym.overrides(sym2, origin, env.getTypes(), true)) {
+                    return t;
+                }
+            }
+        }
+        return null;
+    }
+
+    // TODO: investigate and reimplement without javac dependencies.
+    public boolean shouldDocument(Element e) {
+        return env.shouldDocument(e);
+    }
+
+    //------------------Start of Serializable Implementation---------------------//
+    private final static Map<TypeElement, NewSerializedForm> serializedForms = new HashMap<>();
+
+    public SortedSet<VariableElement> getSerializableFields(Utils utils, TypeElement klass) {
+        NewSerializedForm sf = serializedForms.get(klass);
+        if (sf == null) {
+            sf = new NewSerializedForm(utils, configuration.root.getElementUtils(), klass);
+            serializedForms.put(klass, sf);
+        }
+        return sf.fields;
+    }
+
+    public SortedSet<ExecutableElement>  getSerializationMethods(Utils utils, TypeElement klass) {
+        NewSerializedForm sf = serializedForms.get(klass);
+        if (sf == null) {
+            sf = new NewSerializedForm(utils, configuration.root.getElementUtils(), klass);
+            serializedForms.put(klass, sf);
+        }
+        return sf.methods;
+    }
+
+    public boolean definesSerializableFields(Utils utils, TypeElement klass) {
+        if (!utils.isSerializable(klass) || utils.isExternalizable(klass)) {
+            return false;
+        } else {
+            NewSerializedForm sf = serializedForms.get(klass);
+            if (sf == null) {
+                sf = new NewSerializedForm(utils, configuration.root.getElementUtils(), klass);
+                serializedForms.put(klass, sf);
+            }
+            return sf.definesSerializableFields;
+        }
+    }
+
+    /* TODO we need a clean port to jx.l.m
+     * The serialized form is the specification of a class' serialization state.
+     * <p>
+     *
+     * It consists of the following information:
+     * <p>
+     *
+     * <pre>
+     * 1. Whether class is Serializable or Externalizable.
+     * 2. Javadoc for serialization methods.
+     *    a. For Serializable, the optional readObject, writeObject,
+     *       readResolve and writeReplace.
+     *       serialData tag describes, in prose, the sequence and type
+     *       of optional data written by writeObject.
+     *    b. For Externalizable, writeExternal and readExternal.
+     *       serialData tag describes, in prose, the sequence and type
+     *       of optional data written by writeExternal.
+     * 3. Javadoc for serialization data layout.
+     *    a. For Serializable, the name,type and description
+     *       of each Serializable fields.
+     *    b. For Externalizable, data layout is described by 2(b).
+     * </pre>
+     *
+     */
+    static class NewSerializedForm {
+
+        final Utils utils;
+        final Elements elements;
+
+        final SortedSet<ExecutableElement> methods;
+
+        /* List of FieldDocImpl - Serializable fields.
+         * Singleton list if class defines Serializable fields explicitly.
+         * Otherwise, list of default serializable fields.
+         * 0 length list for Externalizable.
+         */
+        final SortedSet<VariableElement> fields;
+
+        /* True if class specifies serializable fields explicitly.
+         * using special static member, serialPersistentFields.
+         */
+        boolean definesSerializableFields = false;
+
+        // Specially treated field/method names defined by Serialization.
+        private static final String SERIALIZABLE_FIELDS = "serialPersistentFields";
+        private static final String READOBJECT = "readObject";
+        private static final String WRITEOBJECT = "writeObject";
+        private static final String READRESOLVE = "readResolve";
+        private static final String WRITEREPLACE = "writeReplace";
+        private static final String READOBJECTNODATA = "readObjectNoData";
+
+        NewSerializedForm(Utils utils, Elements elements, TypeElement te) {
+            this.utils = utils;
+            this.elements = elements;
+            methods = new TreeSet<>(utils.makeGeneralPurposeComparator());
+            fields = new TreeSet<>(utils.makeGeneralPurposeComparator());
+            if (utils.isExternalizable(te)) {
+                /* look up required public accessible methods,
+                 *   writeExternal and readExternal.
+                 */
+                String[] readExternalParamArr = {"java.io.ObjectInput"};
+                String[] writeExternalParamArr = {"java.io.ObjectOutput"};
+
+                ExecutableElement md = findMethod(te, "readExternal", Arrays.asList(readExternalParamArr));
+                if (md != null) {
+                    methods.add(md);
+                }
+                md = findMethod((ClassSymbol) te, "writeExternal", Arrays.asList(writeExternalParamArr));
+                if (md != null) {
+                    methods.add(md);
+                }
+            } else if (utils.isSerializable(te)) {
+                VarSymbol dsf = getDefinedSerializableFields((ClassSymbol) te);
+                if (dsf != null) {
+                    /* Define serializable fields with array of ObjectStreamField.
+                     * Each ObjectStreamField should be documented by a
+                     * serialField tag.
+                     */
+                    definesSerializableFields = true;
+                    fields.add((VariableElement) dsf);
+                } else {
+
+                    /* Calculate default Serializable fields as all
+                     * non-transient, non-static fields.
+                     * Fields should be documented by serial tag.
+                     */
+                    computeDefaultSerializableFields((ClassSymbol) te);
+                }
+
+                /* Check for optional customized readObject, writeObject,
+                 * readResolve and writeReplace, which can all contain
+                 * the serialData tag.        */
+                addMethodIfExist((ClassSymbol) te, READOBJECT);
+                addMethodIfExist((ClassSymbol) te, WRITEOBJECT);
+                addMethodIfExist((ClassSymbol) te, READRESOLVE);
+                addMethodIfExist((ClassSymbol) te, WRITEREPLACE);
+                addMethodIfExist((ClassSymbol) te, READOBJECTNODATA);
+            }
+        }
+
+        private VarSymbol getDefinedSerializableFields(ClassSymbol def) {
+            Names names = def.name.table.names;
+
+            /* SERIALIZABLE_FIELDS can be private,
+             */
+            for (Symbol sym : def.members().getSymbolsByName(names.fromString(SERIALIZABLE_FIELDS))) {
+                if (sym.kind == VAR) {
+                    VarSymbol f = (VarSymbol) sym;
+                    if ((f.flags() & Flags.STATIC) != 0
+                            && (f.flags() & Flags.PRIVATE) != 0) {
+                        return f;
+                    }
+                }
+            }
+            return null;
+        }
+
+        /*
+         * Catalog Serializable method if it exists in current ClassSymbol.
+         * Do not look for method in superclasses.
+         *
+         * Serialization requires these methods to be non-static.
+         *
+         * @param method should be an unqualified Serializable method
+         *               name either READOBJECT, WRITEOBJECT, READRESOLVE
+         *               or WRITEREPLACE.
+         * @param visibility the visibility flag for the given method.
+         */
+        private void addMethodIfExist(ClassSymbol def, String methodName) {
+            Names names = def.name.table.names;
+
+            for (Symbol sym : def.members().getSymbolsByName(names.fromString(methodName))) {
+                if (sym.kind == MTH) {
+                    MethodSymbol md = (MethodSymbol) sym;
+                    if ((md.flags() & Flags.STATIC) == 0) {
+                        /*
+                         * WARNING: not robust if unqualifiedMethodName is overloaded
+                         *          method. Signature checking could make more robust.
+                         * READOBJECT takes a single parameter, java.io.ObjectInputStream.
+                         * WRITEOBJECT takes a single parameter, java.io.ObjectOutputStream.
+                         */
+                        methods.add(md);
+                    }
+                }
+            }
+        }
+
+        /*
+         * Compute default Serializable fields from all members of ClassSymbol.
+         *
+         * must walk over all members of ClassSymbol.
+         */
+        private void computeDefaultSerializableFields(ClassSymbol te) {
+            for (Symbol sym : te.members().getSymbols(NON_RECURSIVE)) {
+                if (sym != null && sym.kind == VAR) {
+                    VarSymbol f = (VarSymbol) sym;
+                    if ((f.flags() & Flags.STATIC) == 0
+                            && (f.flags() & Flags.TRANSIENT) == 0) {
+                        //### No modifier filtering applied here.
+                        //### Add to beginning.
+                        //### Preserve order used by old 'javadoc'.
+                        fields.add(f);
+                    }
+                }
+            }
+        }
+
+        /**
+         * Find a method in this class scope. Search order: this class, interfaces, superclasses,
+         * outerclasses. Note that this is not necessarily what the compiler would do!
+         *
+         * @param methodName the unqualified name to search for.
+         * @param paramTypes the array of Strings for method parameter types.
+         * @return the first MethodDocImpl which matches, null if not found.
+         */
+        public ExecutableElement findMethod(TypeElement te, String methodName,
+                List<String> paramTypes) {
+            List<? extends Element> allMembers = this.elements.getAllMembers(te);
+            loop:
+            for (Element e : allMembers) {
+                if (e.getKind() != METHOD) {
+                    continue;
+                }
+                ExecutableElement ee = (ExecutableElement) e;
+                if (!ee.getSimpleName().contentEquals(methodName)) {
+                    continue;
+                }
+                List<? extends VariableElement> parameters = ee.getParameters();
+                if (paramTypes.size() != parameters.size()) {
+                    continue;
+                }
+                for (int i = 0; i < parameters.size(); i++) {
+                    VariableElement ve = parameters.get(i);
+                    if (!ve.asType().toString().equals(paramTypes.get(i))) {
+                        break loop;
+                    }
+                }
+                return ee;
+            }
+            TypeElement encl = utils.getEnclosingTypeElement(te);
+            if (encl == null) {
+                return null;
+            }
+            return findMethod(encl, methodName, paramTypes);
+        }
+    }
+
+    // TODO: this is a fast way to get the JavaFileObject for
+    // a package.html file, however we need to eliminate this.
+    public JavaFileObject getJavaFileObject(PackageElement pe) {
+        return env.pkgToJavaFOMap.get(pe);
+    }
+}