All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jdk.javadoc.internal.doclets.toolkit.WorkArounds Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2015, 2017, 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.ModuleElement;
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.FileObject;
import javax.tools.JavaFileManager.Location;

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.ModuleSymbol;
import com.sun.tools.javac.code.Symbol.PackageSymbol;
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.JavacElements;
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.ToolEnvironment;
import jdk.javadoc.internal.tool.DocEnvImpl;

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.
 *
 *
 *  

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. */ public class WorkArounds { public final Configuration configuration; public final ToolEnvironment toolEnv; public final Utils utils; private DocLint doclint; public WorkArounds(Configuration configuration) { this.configuration = configuration; this.utils = this.configuration.utils; this.toolEnv = ((DocEnvImpl)this.configuration.docEnv).toolEnv; } Map 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 opts, Collection customTagNames, String htmlVersion) { ArrayList 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(toolEnv.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 interfaceTypesOf(TypeMirror type) { com.sun.tools.javac.util.List interfaces = ((DocEnvImpl)configuration.docEnv).toolEnv.getTypes().interfaces((com.sun.tools.javac.code.Type)type); if (interfaces.isEmpty()) { return Collections.emptyList(); } List 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 = ((DocEnvImpl)configuration.docEnv).toolEnv.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: 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 getElementToTreePath() { return toolEnv.elementToTreePath; } // TODO: we need ElementUtils.getPackage to cope with input strings // to return the proper unnamedPackage for all supported releases. PackageElement getUnnamedPackage() { return (toolEnv.source.allowModules()) ? toolEnv.syms.unnamedModule.unnamedPackage : toolEnv.syms.noModule.unnamedPackage; } // TODO: implement in either jx.l.m API (preferred) or DocletEnvironment. FileObject getJavaFileObject(PackageElement packageElement) { return ((PackageSymbol)packageElement).sourcefile; } // TODO: needs to ported to jx.l.m. public TypeElement searchClass(TypeElement klass, String className) { // search by qualified name first TypeElement te = configuration.docEnv.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 compenv = toolEnv.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 TypeElement or a TypeParameterElement. * @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 = toolEnv.getTypes().supertype(origin.type); t.hasTag(com.sun.tools.javac.code.TypeTag.CLASS); t = toolEnv.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, toolEnv.getTypes(), true)) { return t; } } } return null; } // TODO: the method jx.l.m.Elements::overrides does not check // the return type, see JDK-8174840 until that is resolved, // use a copy of the same method, with a return type check. // Note: the rider.overrides call in this method *must* be consistent // with the call in overrideType(....), the method above. public boolean overrides(ExecutableElement e1, ExecutableElement e2, TypeElement cls) { MethodSymbol rider = (MethodSymbol)e1; MethodSymbol ridee = (MethodSymbol)e2; ClassSymbol origin = (ClassSymbol)cls; return rider.name == ridee.name && // not reflexive as per JLS rider != ridee && // we don't care if ridee is static, though that wouldn't // compile !rider.isStatic() && // Symbol.overrides assumes the following ridee.isMemberOf(origin, toolEnv.getTypes()) && // check access, signatures and check return types rider.overrides(ridee, origin, toolEnv.getTypes(), true); } // TODO: jx.l.m ? public Location getLocationForModule(ModuleElement mdle) { ModuleSymbol msym = (ModuleSymbol)mdle; return msym.sourceLocation != null ? msym.sourceLocation : msym.classLocation; } //------------------Start of Serializable Implementation---------------------// private final static Map serializedForms = new HashMap<>(); public SortedSet getSerializableFields(Utils utils, TypeElement klass) { NewSerializedForm sf = serializedForms.get(klass); if (sf == null) { sf = new NewSerializedForm(utils, configuration.docEnv.getElementUtils(), klass); serializedForms.put(klass, sf); } return sf.fields; } public SortedSet getSerializationMethods(Utils utils, TypeElement klass) { NewSerializedForm sf = serializedForms.get(klass); if (sf == null) { sf = new NewSerializedForm(utils, configuration.docEnv.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.docEnv.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. *

* * It consists of the following information: *

* *

     * 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).
     * 
* */ static class NewSerializedForm { final Utils utils; final Elements elements; final SortedSet 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 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 paramTypes) { List 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 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: we need to eliminate this, as it is hacky. /** * Returns a representation of the package truncated to two levels. * For instance if the given package represents foo.bar.baz will return * a representation of foo.bar * @param pkg the PackageElement * @return an abbreviated PackageElement */ public PackageElement getAbbreviatedPackageElement(PackageElement pkg) { String parsedPackageName = utils.parsePackageName(pkg); ModuleElement encl = (ModuleElement) pkg.getEnclosingElement(); PackageElement abbrevPkg = encl == null ? utils.elementUtils.getPackageElement(parsedPackageName) : ((JavacElements) utils.elementUtils).getPackageElement(encl, parsedPackageName); return abbrevPkg; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy