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