1 /*
   2  * Copyright (c) 1998, 2019, 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 
  26 package jdk.javadoc.internal.doclets.formats.html;
  27 
  28 import jdk.javadoc.internal.doclets.formats.html.markup.Table;
  29 
  30 import java.util.ArrayList;
  31 import java.util.Collections;
  32 import java.util.HashMap;
  33 import java.util.List;
  34 import java.util.Map;
  35 import java.util.Set;
  36 import java.util.SortedSet;
  37 import java.util.TreeSet;
  38 
  39 import javax.lang.model.element.Element;
  40 import javax.lang.model.element.PackageElement;
  41 import javax.lang.model.element.TypeElement;
  42 import javax.tools.Diagnostic;
  43 
  44 import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder;
  45 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle;
  46 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag;
  47 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree;
  48 import jdk.javadoc.internal.doclets.formats.html.markup.Navigation;
  49 import jdk.javadoc.internal.doclets.formats.html.markup.Navigation.PageMode;
  50 import jdk.javadoc.internal.doclets.formats.html.markup.StringContent;
  51 import jdk.javadoc.internal.doclets.toolkit.Content;
  52 import jdk.javadoc.internal.doclets.toolkit.util.ClassTree;
  53 import jdk.javadoc.internal.doclets.toolkit.util.ClassUseMapper;
  54 import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException;
  55 import jdk.javadoc.internal.doclets.toolkit.util.DocPath;
  56 import jdk.javadoc.internal.doclets.toolkit.util.DocPaths;
  57 
  58 
  59 /**
  60  * Generate class usage information.
  61  *
  62  *  <p><b>This is NOT part of any supported API.
  63  *  If you write code that depends on this, you do so at your own risk.
  64  *  This code and its internal interfaces are subject to change or
  65  *  deletion without notice.</b>
  66  */
  67 public class ClassUseWriter extends SubWriterHolderWriter {
  68 
  69     final TypeElement typeElement;
  70     Set<PackageElement> pkgToPackageAnnotations = null;
  71     final Map<PackageElement, List<Element>> pkgToClassTypeParameter;
  72     final Map<PackageElement, List<Element>> pkgToClassAnnotations;
  73     final Map<PackageElement, List<Element>> pkgToMethodTypeParameter;
  74     final Map<PackageElement, List<Element>> pkgToMethodArgTypeParameter;
  75     final Map<PackageElement, List<Element>> pkgToMethodReturnTypeParameter;
  76     final Map<PackageElement, List<Element>> pkgToMethodAnnotations;
  77     final Map<PackageElement, List<Element>> pkgToMethodParameterAnnotations;
  78     final Map<PackageElement, List<Element>> pkgToFieldTypeParameter;
  79     final Map<PackageElement, List<Element>> pkgToFieldAnnotations;
  80     final Map<PackageElement, List<Element>> pkgToSubclass;
  81     final Map<PackageElement, List<Element>> pkgToSubinterface;
  82     final Map<PackageElement, List<Element>> pkgToImplementingClass;
  83     final Map<PackageElement, List<Element>> pkgToField;
  84     final Map<PackageElement, List<Element>> pkgToMethodReturn;
  85     final Map<PackageElement, List<Element>> pkgToMethodArgs;
  86     final Map<PackageElement, List<Element>> pkgToMethodThrows;
  87     final Map<PackageElement, List<Element>> pkgToConstructorAnnotations;
  88     final Map<PackageElement, List<Element>> pkgToConstructorParameterAnnotations;
  89     final Map<PackageElement, List<Element>> pkgToConstructorArgs;
  90     final Map<PackageElement, List<Element>> pkgToConstructorArgTypeParameter;
  91     final Map<PackageElement, List<Element>> pkgToConstructorThrows;
  92     final SortedSet<PackageElement> pkgSet;
  93     final MethodWriterImpl methodSubWriter;
  94     final ConstructorWriterImpl constrSubWriter;
  95     final FieldWriterImpl fieldSubWriter;
  96     final NestedClassWriterImpl classSubWriter;
  97     private final Navigation navBar;
  98 
  99     /**
 100      * Constructor.
 101      *
 102      * @param filename the file to be generated.
 103      */
 104     public ClassUseWriter(HtmlConfiguration configuration,
 105                           ClassUseMapper mapper, DocPath filename,
 106                           TypeElement typeElement) {
 107         super(configuration, filename);
 108         this.typeElement = typeElement;
 109         if (mapper.classToPackageAnnotations.containsKey(typeElement)) {
 110             pkgToPackageAnnotations = new TreeSet<>(utils.makeClassUseComparator());
 111             pkgToPackageAnnotations.addAll(mapper.classToPackageAnnotations.get(typeElement));
 112         }
 113         configuration.currentTypeElement = typeElement;
 114         this.pkgSet = new TreeSet<>(utils.makePackageComparator());
 115         this.pkgToClassTypeParameter = pkgDivide(mapper.classToClassTypeParam);
 116         this.pkgToClassAnnotations = pkgDivide(mapper.classToClassAnnotations);
 117         this.pkgToMethodTypeParameter = pkgDivide(mapper.classToMethodTypeParam);
 118         this.pkgToMethodArgTypeParameter = pkgDivide(mapper.classToMethodArgTypeParam);
 119         this.pkgToFieldTypeParameter = pkgDivide(mapper.classToFieldTypeParam);
 120         this.pkgToFieldAnnotations = pkgDivide(mapper.annotationToField);
 121         this.pkgToMethodReturnTypeParameter = pkgDivide(mapper.classToMethodReturnTypeParam);
 122         this.pkgToMethodAnnotations = pkgDivide(mapper.classToMethodAnnotations);
 123         this.pkgToMethodParameterAnnotations = pkgDivide(mapper.classToMethodParamAnnotation);
 124         this.pkgToSubclass = pkgDivide(mapper.classToSubclass);
 125         this.pkgToSubinterface = pkgDivide(mapper.classToSubinterface);
 126         this.pkgToImplementingClass = pkgDivide(mapper.classToImplementingClass);
 127         this.pkgToField = pkgDivide(mapper.classToField);
 128         this.pkgToMethodReturn = pkgDivide(mapper.classToMethodReturn);
 129         this.pkgToMethodArgs = pkgDivide(mapper.classToMethodArgs);
 130         this.pkgToMethodThrows = pkgDivide(mapper.classToMethodThrows);
 131         this.pkgToConstructorAnnotations = pkgDivide(mapper.classToConstructorAnnotations);
 132         this.pkgToConstructorParameterAnnotations = pkgDivide(mapper.classToConstructorParamAnnotation);
 133         this.pkgToConstructorArgs = pkgDivide(mapper.classToConstructorArgs);
 134         this.pkgToConstructorArgTypeParameter = pkgDivide(mapper.classToConstructorArgTypeParam);
 135         this.pkgToConstructorThrows = pkgDivide(mapper.classToConstructorThrows);
 136         //tmp test
 137         if (pkgSet.size() > 0 &&
 138             mapper.classToPackage.containsKey(this.typeElement) &&
 139             !pkgSet.equals(mapper.classToPackage.get(this.typeElement))) {
 140             configuration.reporter.print(Diagnostic.Kind.WARNING,
 141                     "Internal error: package sets don't match: "
 142                     + pkgSet + " with: " + mapper.classToPackage.get(this.typeElement));
 143         }
 144 
 145         methodSubWriter = new MethodWriterImpl(this);
 146         constrSubWriter = new ConstructorWriterImpl(this);
 147         fieldSubWriter = new FieldWriterImpl(this);
 148         classSubWriter = new NestedClassWriterImpl(this);
 149         this.navBar = new Navigation(typeElement, configuration, PageMode.USE, path);
 150     }
 151 
 152     /**
 153      * Write out class use pages.
 154      *
 155      * @param configuration the configuration for this doclet
 156      * @param classtree the class tree hierarchy
 157      * @throws DocFileIOException if there is an error while generating the documentation
 158      */
 159     public static void generate(HtmlConfiguration configuration, ClassTree classtree) throws DocFileIOException  {
 160         ClassUseMapper mapper = new ClassUseMapper(configuration, classtree);
 161         for (TypeElement aClass : configuration.getIncludedTypeElements()) {
 162             // If -nodeprecated option is set and the containing package is marked
 163             // as deprecated, do not generate the class-use page. We will still generate
 164             // the class-use page if the class is marked as deprecated but the containing
 165             // package is not since it could still be linked from that package-use page.
 166             if (!(configuration.nodeprecated &&
 167                   configuration.utils.isDeprecated(configuration.utils.containingPackage(aClass))))
 168                 ClassUseWriter.generate(configuration, mapper, aClass);
 169         }
 170         for (PackageElement pkg : configuration.packages) {
 171             // If -nodeprecated option is set and the package is marked
 172             // as deprecated, do not generate the package-use page.
 173             if (!(configuration.nodeprecated && configuration.utils.isDeprecated(pkg)))
 174                 PackageUseWriter.generate(configuration, mapper, pkg);
 175         }
 176     }
 177 
 178     private Map<PackageElement, List<Element>> pkgDivide(Map<TypeElement, ? extends List<? extends Element>> classMap) {
 179         Map<PackageElement, List<Element>> map = new HashMap<>();
 180         List<? extends Element> elements = (List<? extends Element>) classMap.get(typeElement);
 181         if (elements != null) {
 182             Collections.sort(elements, utils.makeClassUseComparator());
 183             for (Element e : elements) {
 184                 PackageElement pkg = utils.containingPackage(e);
 185                 pkgSet.add(pkg);
 186                 List<Element> inPkg = map.get(pkg);
 187                 if (inPkg == null) {
 188                     inPkg = new ArrayList<>();
 189                     map.put(pkg, inPkg);
 190                 }
 191                 inPkg.add(e);
 192             }
 193         }
 194         return map;
 195     }
 196 
 197     /**
 198      * Generate a class page.
 199      *
 200      * @throws DocFileIOException if there is a problem while generating the documentation
 201      */
 202     public static void generate(HtmlConfiguration configuration, ClassUseMapper mapper,
 203                                 TypeElement typeElement) throws DocFileIOException {
 204         ClassUseWriter clsgen;
 205         DocPath path = configuration.docPaths.forPackage(typeElement)
 206                               .resolve(DocPaths.CLASS_USE)
 207                               .resolve(configuration.docPaths.forName( typeElement));
 208         clsgen = new ClassUseWriter(configuration, mapper, path, typeElement);
 209         clsgen.generateClassUseFile();
 210     }
 211 
 212     /**
 213      * Generate the class use elements.
 214      *
 215      * @throws DocFileIOException if there is a problem while generating the documentation
 216      */
 217     protected void generateClassUseFile() throws DocFileIOException {
 218         HtmlTree body = getClassUseHeader();
 219         HtmlTree div = new HtmlTree(HtmlTag.DIV);
 220         div.setStyle(HtmlStyle.classUseContainer);
 221         if (pkgSet.size() > 0) {
 222             addClassUse(div);
 223         } else {
 224             div.add(contents.getContent("doclet.ClassUse_No.usage.of.0",
 225                     utils.getFullyQualifiedName(typeElement)));
 226         }
 227         bodyContents.addMainContent(div);
 228         HtmlTree footer = HtmlTree.FOOTER();
 229         navBar.setUserFooter(getUserHeaderFooter(false));
 230         footer.add(navBar.getContent(false));
 231         addBottom(footer);
 232         bodyContents.setFooter(footer);
 233         body.add(bodyContents.toContent());
 234         String description = getDescription("use", typeElement);
 235         printHtmlDocument(null, description, body);
 236     }
 237 
 238     /**
 239      * Add the class use documentation.
 240      *
 241      * @param contentTree the content tree to which the class use information will be added
 242      */
 243     protected void addClassUse(Content contentTree) {
 244         Content content = new ContentBuilder();
 245         if (configuration.packages.size() > 1) {
 246             addPackageList(content);
 247             addPackageAnnotationList(content);
 248         }
 249         addClassList(content);
 250         contentTree.add(content);
 251     }
 252 
 253     /**
 254      * Add the packages elements that use the given class.
 255      *
 256      * @param contentTree the content tree to which the packages elements will be added
 257      */
 258     protected void addPackageList(Content contentTree) {
 259         Content caption = getTableCaption(contents.getContent(
 260                 "doclet.ClassUse_Packages.that.use.0",
 261                 getLink(new LinkInfoImpl(configuration,
 262                         LinkInfoImpl.Kind.CLASS_USE_HEADER, typeElement))));
 263         Table table = new Table(HtmlStyle.useSummary)
 264                 .setCaption(caption)
 265                 .setHeader(getPackageTableHeader())
 266                 .setColumnStyles(HtmlStyle.colFirst, HtmlStyle.colLast);
 267         for (PackageElement pkg : pkgSet) {
 268             addPackageUse(pkg, table);
 269         }
 270         contentTree.add(table.toContent());
 271     }
 272 
 273     /**
 274      * Add the package annotation elements.
 275      *
 276      * @param contentTree the content tree to which the package annotation elements will be added
 277      */
 278     protected void addPackageAnnotationList(Content contentTree) {
 279         if (!utils.isAnnotationType(typeElement) ||
 280                 pkgToPackageAnnotations == null ||
 281                 pkgToPackageAnnotations.isEmpty()) {
 282             return;
 283         }
 284         Content caption = getTableCaption(contents.getContent(
 285                 "doclet.ClassUse_PackageAnnotation",
 286                 getLink(new LinkInfoImpl(configuration,
 287                         LinkInfoImpl.Kind.CLASS_USE_HEADER, typeElement))));
 288 
 289         Table table = new Table(HtmlStyle.useSummary)
 290                 .setCaption(caption)
 291                 .setHeader(getPackageTableHeader())
 292                 .setColumnStyles(HtmlStyle.colFirst, HtmlStyle.colLast);
 293         for (PackageElement pkg : pkgToPackageAnnotations) {
 294             Content summary = new ContentBuilder();
 295             addSummaryComment(pkg, summary);
 296             table.addRow(getPackageLink(pkg), summary);
 297         }
 298         contentTree.add(table.toContent());
 299     }
 300 
 301     /**
 302      * Add the class elements that use the given class.
 303      *
 304      * @param contentTree the content tree to which the class elements will be added
 305      */
 306     protected void addClassList(Content contentTree) {
 307         HtmlTree ul = new HtmlTree(HtmlTag.UL);
 308         ul.setStyle(HtmlStyle.blockList);
 309         for (PackageElement pkg : pkgSet) {
 310             Content markerAnchor = links.createAnchor(getPackageAnchorName(pkg));
 311             HtmlTree htmlTree = HtmlTree.SECTION(HtmlStyle.detail, markerAnchor);
 312             Content link = contents.getContent("doclet.ClassUse_Uses.of.0.in.1",
 313                     getLink(new LinkInfoImpl(configuration, LinkInfoImpl.Kind.CLASS_USE_HEADER,
 314                             typeElement)),
 315                     getPackageLink(pkg, utils.getPackageName(pkg)));
 316             Content heading = HtmlTree.HEADING(Headings.TypeUse.SUMMARY_HEADING, link);
 317             htmlTree.add(heading);
 318             addClassUse(pkg, htmlTree);
 319             ul.add(HtmlTree.LI(HtmlStyle.blockList, htmlTree));
 320         }
 321         Content li = HtmlTree.SECTION(HtmlStyle.classUses, ul);
 322         contentTree.add(li);
 323     }
 324 
 325     /**
 326      * Add the package use information.
 327      *
 328      * @param pkg the package that uses the given class
 329      * @param table the table to which the package use information will be added
 330      */
 331     protected void addPackageUse(PackageElement pkg, Table table) {
 332         Content pkgLink =
 333                 links.createLink(getPackageAnchorName(pkg), new StringContent(utils.getPackageName(pkg)));
 334         Content summary = new ContentBuilder();
 335         addSummaryComment(pkg, summary);
 336         table.addRow(pkgLink, summary);
 337     }
 338 
 339     /**
 340      * Add the class use information.
 341      *
 342      * @param pkg the package that uses the given class
 343      * @param contentTree the content tree to which the class use information will be added
 344      */
 345     protected void addClassUse(PackageElement pkg, Content contentTree) {
 346         Content classLink = getLink(new LinkInfoImpl(configuration,
 347             LinkInfoImpl.Kind.CLASS_USE_HEADER, typeElement));
 348         Content pkgLink = getPackageLink(pkg, utils.getPackageName(pkg));
 349         classSubWriter.addUseInfo(pkgToClassAnnotations.get(pkg),
 350                 contents.getContent("doclet.ClassUse_Annotation", classLink,
 351                 pkgLink), contentTree);
 352         classSubWriter.addUseInfo(pkgToClassTypeParameter.get(pkg),
 353                 contents.getContent("doclet.ClassUse_TypeParameter", classLink,
 354                 pkgLink), contentTree);
 355         classSubWriter.addUseInfo(pkgToSubclass.get(pkg),
 356                 contents.getContent("doclet.ClassUse_Subclass", classLink,
 357                 pkgLink), contentTree);
 358         classSubWriter.addUseInfo(pkgToSubinterface.get(pkg),
 359                 contents.getContent("doclet.ClassUse_Subinterface", classLink,
 360                 pkgLink), contentTree);
 361         classSubWriter.addUseInfo(pkgToImplementingClass.get(pkg),
 362                 contents.getContent("doclet.ClassUse_ImplementingClass", classLink,
 363                 pkgLink), contentTree);
 364         fieldSubWriter.addUseInfo(pkgToField.get(pkg),
 365                 contents.getContent("doclet.ClassUse_Field", classLink,
 366                 pkgLink), contentTree);
 367         fieldSubWriter.addUseInfo(pkgToFieldAnnotations.get(pkg),
 368                 contents.getContent("doclet.ClassUse_FieldAnnotations", classLink,
 369                 pkgLink), contentTree);
 370         fieldSubWriter.addUseInfo(pkgToFieldTypeParameter.get(pkg),
 371                 contents.getContent("doclet.ClassUse_FieldTypeParameter", classLink,
 372                 pkgLink), contentTree);
 373         methodSubWriter.addUseInfo(pkgToMethodAnnotations.get(pkg),
 374                 contents.getContent("doclet.ClassUse_MethodAnnotations", classLink,
 375                 pkgLink), contentTree);
 376         methodSubWriter.addUseInfo(pkgToMethodParameterAnnotations.get(pkg),
 377                 contents.getContent("doclet.ClassUse_MethodParameterAnnotations", classLink,
 378                 pkgLink), contentTree);
 379         methodSubWriter.addUseInfo(pkgToMethodTypeParameter.get(pkg),
 380                 contents.getContent("doclet.ClassUse_MethodTypeParameter", classLink,
 381                 pkgLink), contentTree);
 382         methodSubWriter.addUseInfo(pkgToMethodReturn.get(pkg),
 383                 contents.getContent("doclet.ClassUse_MethodReturn", classLink,
 384                 pkgLink), contentTree);
 385         methodSubWriter.addUseInfo(pkgToMethodReturnTypeParameter.get(pkg),
 386                 contents.getContent("doclet.ClassUse_MethodReturnTypeParameter", classLink,
 387                 pkgLink), contentTree);
 388         methodSubWriter.addUseInfo(pkgToMethodArgs.get(pkg),
 389                 contents.getContent("doclet.ClassUse_MethodArgs", classLink,
 390                 pkgLink), contentTree);
 391         methodSubWriter.addUseInfo(pkgToMethodArgTypeParameter.get(pkg),
 392                 contents.getContent("doclet.ClassUse_MethodArgsTypeParameters", classLink,
 393                 pkgLink), contentTree);
 394         methodSubWriter.addUseInfo(pkgToMethodThrows.get(pkg),
 395                 contents.getContent("doclet.ClassUse_MethodThrows", classLink,
 396                 pkgLink), contentTree);
 397         constrSubWriter.addUseInfo(pkgToConstructorAnnotations.get(pkg),
 398                 contents.getContent("doclet.ClassUse_ConstructorAnnotations", classLink,
 399                 pkgLink), contentTree);
 400         constrSubWriter.addUseInfo(pkgToConstructorParameterAnnotations.get(pkg),
 401                 contents.getContent("doclet.ClassUse_ConstructorParameterAnnotations", classLink,
 402                 pkgLink), contentTree);
 403         constrSubWriter.addUseInfo(pkgToConstructorArgs.get(pkg),
 404                 contents.getContent("doclet.ClassUse_ConstructorArgs", classLink,
 405                 pkgLink), contentTree);
 406         constrSubWriter.addUseInfo(pkgToConstructorArgTypeParameter.get(pkg),
 407                 contents.getContent("doclet.ClassUse_ConstructorArgsTypeParameters", classLink,
 408                 pkgLink), contentTree);
 409         constrSubWriter.addUseInfo(pkgToConstructorThrows.get(pkg),
 410                 contents.getContent("doclet.ClassUse_ConstructorThrows", classLink,
 411                 pkgLink), contentTree);
 412     }
 413 
 414     /**
 415      * Get the header for the class use Listing.
 416      *
 417      * @return a content tree representing the class use header
 418      */
 419     protected HtmlTree getClassUseHeader() {
 420         String cltype = resources.getText(utils.isInterface(typeElement)
 421                 ? "doclet.Interface"
 422                 : "doclet.Class");
 423         String clname = utils.getFullyQualifiedName(typeElement);
 424         String title = resources.getText("doclet.Window_ClassUse_Header",
 425                 cltype, clname);
 426         HtmlTree bodyTree = getBody(getWindowTitle(title));
 427         Content headerContent = new ContentBuilder();
 428         addTop(headerContent);
 429         Content mdleLinkContent = getModuleLink(utils.elementUtils.getModuleOf(typeElement),
 430                 contents.moduleLabel);
 431         navBar.setNavLinkModule(mdleLinkContent);
 432         Content classLinkContent = getLink(new LinkInfoImpl(
 433                 configuration, LinkInfoImpl.Kind.CLASS_USE_HEADER, typeElement)
 434                 .label(resources.getText("doclet.Class")));
 435         navBar.setNavLinkClass(classLinkContent);
 436         navBar.setUserHeader(getUserHeaderFooter(true));
 437         headerContent.add(navBar.getContent(true));
 438         ContentBuilder headingContent = new ContentBuilder();
 439         headingContent.add(contents.getContent("doclet.ClassUse_Title", cltype));
 440         headingContent.add(new HtmlTree(HtmlTag.BR));
 441         headingContent.add(clname);
 442         Content heading = HtmlTree.HEADING(Headings.PAGE_TITLE_HEADING,
 443                 true, HtmlStyle.title, headingContent);
 444         Content div = HtmlTree.DIV(HtmlStyle.header, heading);
 445         bodyContents.setHeader(headerContent).addMainContent(div);
 446         return bodyTree;
 447     }
 448 }