WorkArounds.java revision 3595:81692f730015
1217773Slstewart/* 2217773Slstewart * Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. 3217806Slstewart * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4217773Slstewart * 5217773Slstewart * 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 jdk.javadoc.internal.doclets.toolkit; 27 28import java.util.ArrayList; 29import java.util.Arrays; 30import java.util.Collection; 31import java.util.Collections; 32import java.util.HashMap; 33import java.util.List; 34import java.util.Map; 35import java.util.SortedSet; 36import java.util.TreeSet; 37 38import javax.lang.model.element.AnnotationMirror; 39import javax.lang.model.element.Element; 40import javax.lang.model.element.ExecutableElement; 41import javax.lang.model.element.ModuleElement; 42import javax.lang.model.element.PackageElement; 43import javax.lang.model.element.TypeElement; 44import javax.lang.model.element.VariableElement; 45import javax.lang.model.type.TypeMirror; 46import javax.lang.model.util.Elements; 47import javax.tools.JavaFileObject; 48 49import com.sun.source.tree.CompilationUnitTree; 50import com.sun.source.util.JavacTask; 51import com.sun.source.util.TreePath; 52import com.sun.tools.doclint.DocLint; 53import com.sun.tools.javac.api.BasicJavacTask; 54import com.sun.tools.javac.code.Attribute; 55import com.sun.tools.javac.code.Flags; 56import com.sun.tools.javac.code.Scope; 57import com.sun.tools.javac.code.Symbol; 58import com.sun.tools.javac.code.Symbol.ClassSymbol; 59import com.sun.tools.javac.code.Symbol.MethodSymbol; 60import com.sun.tools.javac.code.Symbol.VarSymbol; 61import com.sun.tools.javac.comp.AttrContext; 62import com.sun.tools.javac.comp.Env; 63import com.sun.tools.javac.model.JavacElements; 64import com.sun.tools.javac.model.JavacTypes; 65import com.sun.tools.javac.util.Names; 66 67import jdk.javadoc.internal.doclets.toolkit.util.Utils; 68import jdk.javadoc.internal.tool.ToolEnvironment; 69import jdk.javadoc.internal.tool.DocEnvImpl; 70 71import static com.sun.tools.javac.code.Kinds.Kind.*; 72import static com.sun.tools.javac.code.Scope.LookupKind.NON_RECURSIVE; 73 74import static javax.lang.model.element.ElementKind.*; 75 76/** 77 * A quarantine class to isolate all the workarounds and bridges to 78 * a locality. This class should eventually disappear once all the 79 * standard APIs support the needed interfaces. 80 * 81 * 82 * <p><b>This is NOT part of any supported API. 83 * If you write code that depends on this, you do so at your own risk. 84 * This code and its internal interfaces are subject to change or 85 * deletion without notice.</b> 86 */ 87public class WorkArounds { 88 89 public final Configuration configuration; 90 public final ToolEnvironment toolEnv; 91 public final Utils utils; 92 93 private DocLint doclint; 94 95 public WorkArounds(Configuration configuration) { 96 this.configuration = configuration; 97 this.utils = this.configuration.utils; 98 this.toolEnv = ((DocEnvImpl)this.configuration.docEnv).toolEnv; 99 } 100 101 Map<CompilationUnitTree, Boolean> shouldCheck = new HashMap<>(); 102 // TODO: fix this up correctly 103 public void runDocLint(TreePath path) { 104 CompilationUnitTree unit = path.getCompilationUnit(); 105 if (doclint != null && shouldCheck.computeIfAbsent(unit, doclint::shouldCheck)) { 106 doclint.scan(path); 107 } 108 } 109 110 // TODO: fix this up correctly 111 public void initDocLint(Collection<String> opts, Collection<String> customTagNames, String htmlVersion) { 112 ArrayList<String> doclintOpts = new ArrayList<>(); 113 boolean msgOptionSeen = false; 114 115 for (String opt : opts) { 116 if (opt.startsWith(DocLint.XMSGS_OPTION)) { 117 if (opt.equals(DocLint.XMSGS_CUSTOM_PREFIX + "none")) 118 return; 119 msgOptionSeen = true; 120 } 121 doclintOpts.add(opt); 122 } 123 124 if (!msgOptionSeen) { 125 doclintOpts.add(DocLint.XMSGS_OPTION); 126 } 127 128 String sep = ""; 129 StringBuilder customTags = new StringBuilder(); 130 for (String customTag : customTagNames) { 131 customTags.append(sep); 132 customTags.append(customTag); 133 sep = DocLint.SEPARATOR; 134 } 135 doclintOpts.add(DocLint.XCUSTOM_TAGS_PREFIX + customTags.toString()); 136 doclintOpts.add(DocLint.XHTML_VERSION_PREFIX + htmlVersion); 137 138 JavacTask t = BasicJavacTask.instance(toolEnv.context); 139 doclint = new DocLint(); 140 // standard doclet normally generates H1, H2 141 doclintOpts.add(DocLint.XIMPLICIT_HEADERS + "2"); 142 doclint.init(t, doclintOpts.toArray(new String[doclintOpts.size()]), false); 143 } 144 145 // TODO: fix this up correctly 146 public boolean haveDocLint() { 147 return (doclint == null); 148 } 149 150 // TODO: jx.l.m directSuperTypes don't work for things like Enum, 151 // so we use javac directly, investigate why jx.l.m is not cutting it. 152 public List<TypeMirror> interfaceTypesOf(TypeMirror type) { 153 com.sun.tools.javac.util.List<com.sun.tools.javac.code.Type> interfaces = 154 ((DocEnvImpl)configuration.docEnv).toolEnv.getTypes().interfaces((com.sun.tools.javac.code.Type)type); 155 if (interfaces.isEmpty()) { 156 return Collections.emptyList(); 157 } 158 List<TypeMirror> list = new ArrayList<>(interfaces.size()); 159 for (com.sun.tools.javac.code.Type t : interfaces) { 160 list.add((TypeMirror)t); 161 } 162 return list; 163 } 164 165 /* 166 * TODO: This method exists because of a bug in javac which does not 167 * handle "@deprecated tag in package-info.java", when this issue 168 * is fixed this method and its uses must be jettisoned. 169 */ 170 public boolean isDeprecated0(Element e) { 171 if (!utils.getDeprecatedTrees(e).isEmpty()) { 172 return true; 173 } 174 JavacTypes jctypes = ((DocEnvImpl)configuration.docEnv).toolEnv.typeutils; 175 TypeMirror deprecatedType = utils.getDeprecatedType(); 176 for (AnnotationMirror anno : e.getAnnotationMirrors()) { 177 if (jctypes.isSameType(anno.getAnnotationType().asElement().asType(), deprecatedType)) 178 return true; 179 } 180 return false; 181 } 182 183 // TODO: fix jx.l.m add this method. 184 public boolean isSynthesized(AnnotationMirror aDesc) { 185 return ((Attribute)aDesc).isSynthesized(); 186 } 187 188 // TODO: implement using jx.l.model 189 public boolean isVisible(TypeElement te) { 190 return ((DocEnvImpl)(configuration.docEnv)).etable.isVisible(te); 191 } 192 193 // TODO: fix the caller 194 public Object getConstValue(VariableElement ve) { 195 return ((VarSymbol)ve).getConstValue(); 196 } 197 198 //TODO: DocTrees: Trees.getPath(Element e) is slow a factor 4-5 times. 199 public Map<Element, TreePath> getElementToTreePath() { 200 return toolEnv.elementToTreePath; 201 } 202 203 // TODO: needs to ported to jx.l.m. 204 public TypeElement searchClass(TypeElement klass, String className) { 205 // search by qualified name first 206 TypeElement te = configuration.docEnv.getElementUtils().getTypeElement(className); 207 if (te != null) { 208 return te; 209 } 210 211 // search inner classes 212 for (TypeElement ite : utils.getClasses(klass)) { 213 TypeElement innerClass = searchClass(ite, className); 214 if (innerClass != null) { 215 return innerClass; 216 } 217 } 218 219 // check in this package 220 te = utils.findClassInPackageElement(utils.containingPackage(klass), className); 221 if (te != null) { 222 return te; 223 } 224 225 ClassSymbol tsym = (ClassSymbol)klass; 226 // make sure that this symbol has been completed 227 // TODO: do we need this anymore ? 228 if (tsym.completer != null) { 229 tsym.complete(); 230 } 231 232 // search imports 233 if (tsym.sourcefile != null) { 234 235 //### This information is available only for source classes. 236 Env<AttrContext> compenv = toolEnv.getEnv(tsym); 237 if (compenv == null) { 238 return null; 239 } 240 Names names = tsym.name.table.names; 241 Scope s = compenv.toplevel.namedImportScope; 242 for (Symbol sym : s.getSymbolsByName(names.fromString(className))) { 243 if (sym.kind == TYP) { 244 return (TypeElement)sym; 245 } 246 } 247 248 s = compenv.toplevel.starImportScope; 249 for (Symbol sym : s.getSymbolsByName(names.fromString(className))) { 250 if (sym.kind == TYP) { 251 return (TypeElement)sym; 252 } 253 } 254 } 255 256 return null; // not found 257 } 258 259 // TODO: need to re-implement this using j.l.m. correctly!, this has 260 // implications on testInterface, the note here is that javac's supertype 261 // does the right thing returning Parameters in scope. 262 /** 263 * Return the type containing the method that this method overrides. 264 * It may be a <code>TypeElement</code> or a <code>TypeParameterElement</code>. 265 * @param method target 266 * @return a type 267 */ 268 public TypeMirror overriddenType(ExecutableElement method) { 269 if (utils.isStatic(method)) { 270 return null; 271 } 272 MethodSymbol sym = (MethodSymbol)method; 273 ClassSymbol origin = (ClassSymbol) sym.owner; 274 for (com.sun.tools.javac.code.Type t = toolEnv.getTypes().supertype(origin.type); 275 t.hasTag(com.sun.tools.javac.code.TypeTag.CLASS); 276 t = toolEnv.getTypes().supertype(t)) { 277 ClassSymbol c = (ClassSymbol) t.tsym; 278 for (com.sun.tools.javac.code.Symbol sym2 : c.members().getSymbolsByName(sym.name)) { 279 if (sym.overrides(sym2, origin, toolEnv.getTypes(), true)) { 280 return t; 281 } 282 } 283 } 284 return null; 285 } 286 287 // TODO: investigate and reimplement without javac dependencies. 288 public boolean shouldDocument(Element e) { 289 return ((DocEnvImpl)(configuration.docEnv)).etable.shouldDocument(e); 290 } 291 292 //------------------Start of Serializable Implementation---------------------// 293 private final static Map<TypeElement, NewSerializedForm> serializedForms = new HashMap<>(); 294 295 public SortedSet<VariableElement> getSerializableFields(Utils utils, TypeElement klass) { 296 NewSerializedForm sf = serializedForms.get(klass); 297 if (sf == null) { 298 sf = new NewSerializedForm(utils, configuration.docEnv.getElementUtils(), klass); 299 serializedForms.put(klass, sf); 300 } 301 return sf.fields; 302 } 303 304 public SortedSet<ExecutableElement> getSerializationMethods(Utils utils, TypeElement klass) { 305 NewSerializedForm sf = serializedForms.get(klass); 306 if (sf == null) { 307 sf = new NewSerializedForm(utils, configuration.docEnv.getElementUtils(), klass); 308 serializedForms.put(klass, sf); 309 } 310 return sf.methods; 311 } 312 313 public boolean definesSerializableFields(Utils utils, TypeElement klass) { 314 if (!utils.isSerializable(klass) || utils.isExternalizable(klass)) { 315 return false; 316 } else { 317 NewSerializedForm sf = serializedForms.get(klass); 318 if (sf == null) { 319 sf = new NewSerializedForm(utils, configuration.docEnv.getElementUtils(), klass); 320 serializedForms.put(klass, sf); 321 } 322 return sf.definesSerializableFields; 323 } 324 } 325 326 /* TODO we need a clean port to jx.l.m 327 * The serialized form is the specification of a class' serialization state. 328 * <p> 329 * 330 * It consists of the following information: 331 * <p> 332 * 333 * <pre> 334 * 1. Whether class is Serializable or Externalizable. 335 * 2. Javadoc for serialization methods. 336 * a. For Serializable, the optional readObject, writeObject, 337 * readResolve and writeReplace. 338 * serialData tag describes, in prose, the sequence and type 339 * of optional data written by writeObject. 340 * b. For Externalizable, writeExternal and readExternal. 341 * serialData tag describes, in prose, the sequence and type 342 * of optional data written by writeExternal. 343 * 3. Javadoc for serialization data layout. 344 * a. For Serializable, the name,type and description 345 * of each Serializable fields. 346 * b. For Externalizable, data layout is described by 2(b). 347 * </pre> 348 * 349 */ 350 static class NewSerializedForm { 351 352 final Utils utils; 353 final Elements elements; 354 355 final SortedSet<ExecutableElement> methods; 356 357 /* List of FieldDocImpl - Serializable fields. 358 * Singleton list if class defines Serializable fields explicitly. 359 * Otherwise, list of default serializable fields. 360 * 0 length list for Externalizable. 361 */ 362 final SortedSet<VariableElement> fields; 363 364 /* True if class specifies serializable fields explicitly. 365 * using special static member, serialPersistentFields. 366 */ 367 boolean definesSerializableFields = false; 368 369 // Specially treated field/method names defined by Serialization. 370 private static final String SERIALIZABLE_FIELDS = "serialPersistentFields"; 371 private static final String READOBJECT = "readObject"; 372 private static final String WRITEOBJECT = "writeObject"; 373 private static final String READRESOLVE = "readResolve"; 374 private static final String WRITEREPLACE = "writeReplace"; 375 private static final String READOBJECTNODATA = "readObjectNoData"; 376 377 NewSerializedForm(Utils utils, Elements elements, TypeElement te) { 378 this.utils = utils; 379 this.elements = elements; 380 methods = new TreeSet<>(utils.makeGeneralPurposeComparator()); 381 fields = new TreeSet<>(utils.makeGeneralPurposeComparator()); 382 if (utils.isExternalizable(te)) { 383 /* look up required public accessible methods, 384 * writeExternal and readExternal. 385 */ 386 String[] readExternalParamArr = {"java.io.ObjectInput"}; 387 String[] writeExternalParamArr = {"java.io.ObjectOutput"}; 388 389 ExecutableElement md = findMethod(te, "readExternal", Arrays.asList(readExternalParamArr)); 390 if (md != null) { 391 methods.add(md); 392 } 393 md = findMethod((ClassSymbol) te, "writeExternal", Arrays.asList(writeExternalParamArr)); 394 if (md != null) { 395 methods.add(md); 396 } 397 } else if (utils.isSerializable(te)) { 398 VarSymbol dsf = getDefinedSerializableFields((ClassSymbol) te); 399 if (dsf != null) { 400 /* Define serializable fields with array of ObjectStreamField. 401 * Each ObjectStreamField should be documented by a 402 * serialField tag. 403 */ 404 definesSerializableFields = true; 405 fields.add((VariableElement) dsf); 406 } else { 407 408 /* Calculate default Serializable fields as all 409 * non-transient, non-static fields. 410 * Fields should be documented by serial tag. 411 */ 412 computeDefaultSerializableFields((ClassSymbol) te); 413 } 414 415 /* Check for optional customized readObject, writeObject, 416 * readResolve and writeReplace, which can all contain 417 * the serialData tag. */ 418 addMethodIfExist((ClassSymbol) te, READOBJECT); 419 addMethodIfExist((ClassSymbol) te, WRITEOBJECT); 420 addMethodIfExist((ClassSymbol) te, READRESOLVE); 421 addMethodIfExist((ClassSymbol) te, WRITEREPLACE); 422 addMethodIfExist((ClassSymbol) te, READOBJECTNODATA); 423 } 424 } 425 426 private VarSymbol getDefinedSerializableFields(ClassSymbol def) { 427 Names names = def.name.table.names; 428 429 /* SERIALIZABLE_FIELDS can be private, 430 */ 431 for (Symbol sym : def.members().getSymbolsByName(names.fromString(SERIALIZABLE_FIELDS))) { 432 if (sym.kind == VAR) { 433 VarSymbol f = (VarSymbol) sym; 434 if ((f.flags() & Flags.STATIC) != 0 435 && (f.flags() & Flags.PRIVATE) != 0) { 436 return f; 437 } 438 } 439 } 440 return null; 441 } 442 443 /* 444 * Catalog Serializable method if it exists in current ClassSymbol. 445 * Do not look for method in superclasses. 446 * 447 * Serialization requires these methods to be non-static. 448 * 449 * @param method should be an unqualified Serializable method 450 * name either READOBJECT, WRITEOBJECT, READRESOLVE 451 * or WRITEREPLACE. 452 * @param visibility the visibility flag for the given method. 453 */ 454 private void addMethodIfExist(ClassSymbol def, String methodName) { 455 Names names = def.name.table.names; 456 457 for (Symbol sym : def.members().getSymbolsByName(names.fromString(methodName))) { 458 if (sym.kind == MTH) { 459 MethodSymbol md = (MethodSymbol) sym; 460 if ((md.flags() & Flags.STATIC) == 0) { 461 /* 462 * WARNING: not robust if unqualifiedMethodName is overloaded 463 * method. Signature checking could make more robust. 464 * READOBJECT takes a single parameter, java.io.ObjectInputStream. 465 * WRITEOBJECT takes a single parameter, java.io.ObjectOutputStream. 466 */ 467 methods.add(md); 468 } 469 } 470 } 471 } 472 473 /* 474 * Compute default Serializable fields from all members of ClassSymbol. 475 * 476 * must walk over all members of ClassSymbol. 477 */ 478 private void computeDefaultSerializableFields(ClassSymbol te) { 479 for (Symbol sym : te.members().getSymbols(NON_RECURSIVE)) { 480 if (sym != null && sym.kind == VAR) { 481 VarSymbol f = (VarSymbol) sym; 482 if ((f.flags() & Flags.STATIC) == 0 483 && (f.flags() & Flags.TRANSIENT) == 0) { 484 //### No modifier filtering applied here. 485 //### Add to beginning. 486 //### Preserve order used by old 'javadoc'. 487 fields.add(f); 488 } 489 } 490 } 491 } 492 493 /** 494 * Find a method in this class scope. Search order: this class, interfaces, superclasses, 495 * outerclasses. Note that this is not necessarily what the compiler would do! 496 * 497 * @param methodName the unqualified name to search for. 498 * @param paramTypes the array of Strings for method parameter types. 499 * @return the first MethodDocImpl which matches, null if not found. 500 */ 501 public ExecutableElement findMethod(TypeElement te, String methodName, 502 List<String> paramTypes) { 503 List<? extends Element> allMembers = this.elements.getAllMembers(te); 504 loop: 505 for (Element e : allMembers) { 506 if (e.getKind() != METHOD) { 507 continue; 508 } 509 ExecutableElement ee = (ExecutableElement) e; 510 if (!ee.getSimpleName().contentEquals(methodName)) { 511 continue; 512 } 513 List<? extends VariableElement> parameters = ee.getParameters(); 514 if (paramTypes.size() != parameters.size()) { 515 continue; 516 } 517 for (int i = 0; i < parameters.size(); i++) { 518 VariableElement ve = parameters.get(i); 519 if (!ve.asType().toString().equals(paramTypes.get(i))) { 520 break loop; 521 } 522 } 523 return ee; 524 } 525 TypeElement encl = utils.getEnclosingTypeElement(te); 526 if (encl == null) { 527 return null; 528 } 529 return findMethod(encl, methodName, paramTypes); 530 } 531 } 532 533 // TODO: this is a fast way to get the JavaFileObject for 534 // a package.html file, however we need to eliminate this. 535 public JavaFileObject getJavaFileObject(PackageElement pe) { 536 return toolEnv.pkgToJavaFOMap.get(pe); 537 } 538 539 // TODO: we need to eliminate this, as it is hacky. 540 /** 541 * Returns a representation of the package truncated to two levels. 542 * For instance if the given package represents foo.bar.baz will return 543 * a representation of foo.bar 544 * @param pkg the PackageElement 545 * @return an abbreviated PackageElement 546 */ 547 public PackageElement getAbbreviatedPackageElement(PackageElement pkg) { 548 String parsedPackageName = utils.parsePackageName(pkg); 549 ModuleElement encl = (ModuleElement) pkg.getEnclosingElement(); 550 PackageElement abbrevPkg = encl == null 551 ? utils.elementUtils.getPackageElement(parsedPackageName) 552 : ((JavacElements) utils.elementUtils).getPackageElement(encl, parsedPackageName); 553 return abbrevPkg; 554 } 555} 556