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 java.util.Set;
  29 import java.util.SortedMap;
  30 import java.util.TreeMap;
  31 import java.util.TreeSet;
  32 
  33 import javax.lang.model.element.PackageElement;
  34 import javax.lang.model.element.TypeElement;
  35 
  36 import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder;
  37 import jdk.javadoc.internal.doclets.formats.html.markup.Entity;
  38 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle;
  39 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTag;
  40 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree;
  41 import jdk.javadoc.internal.doclets.formats.html.markup.Navigation;
  42 import jdk.javadoc.internal.doclets.formats.html.markup.Navigation.PageMode;
  43 import jdk.javadoc.internal.doclets.formats.html.markup.StringContent;
  44 import jdk.javadoc.internal.doclets.formats.html.markup.Table;
  45 import jdk.javadoc.internal.doclets.formats.html.markup.TableHeader;
  46 import jdk.javadoc.internal.doclets.toolkit.Content;
  47 import jdk.javadoc.internal.doclets.toolkit.util.ClassUseMapper;
  48 import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException;
  49 import jdk.javadoc.internal.doclets.toolkit.util.DocPath;
  50 import jdk.javadoc.internal.doclets.toolkit.util.DocPaths;
  51 
  52 /**
  53  * Generate package usage information.
  54  *
  55  *  <p><b>This is NOT part of any supported API.
  56  *  If you write code that depends on this, you do so at your own risk.
  57  *  This code and its internal interfaces are subject to change or
  58  *  deletion without notice.</b>
  59  */
  60 public class PackageUseWriter extends SubWriterHolderWriter {
  61 
  62     final PackageElement packageElement;
  63     final SortedMap<String, Set<TypeElement>> usingPackageToUsedClasses = new TreeMap<>();
  64     final String packageUseTableSummary;
  65     private final Navigation navBar;
  66 
  67     /**
  68      * Constructor.
  69      *
  70      * @param configuration the configuration
  71      * @param mapper a mapper to provide details of where elements are used
  72      * @param filename the file to be generated
  73      * @param pkgElement the package element to be documented
  74      */
  75     public PackageUseWriter(HtmlConfiguration configuration,
  76                             ClassUseMapper mapper, DocPath filename,
  77                             PackageElement pkgElement) {
  78         super(configuration, configuration.docPaths.forPackage(pkgElement).resolve(filename));
  79         this.packageElement = pkgElement;
  80 
  81         // by examining all classes in this package, find what packages
  82         // use these classes - produce a map between using package and
  83         // used classes.
  84         for (TypeElement usedClass : utils.getEnclosedTypeElements(pkgElement)) {
  85             Set<TypeElement> usingClasses = mapper.classToClass.get(usedClass);
  86             if (usingClasses != null) {
  87                 for (TypeElement usingClass : usingClasses) {
  88                     PackageElement usingPackage = utils.containingPackage(usingClass);
  89                     Set<TypeElement> usedClasses = usingPackageToUsedClasses
  90                             .get(utils.getPackageName(usingPackage));
  91                     if (usedClasses == null) {
  92                         usedClasses = new TreeSet<>(utils.makeGeneralPurposeComparator());
  93                         usingPackageToUsedClasses.put(utils.getPackageName(usingPackage),
  94                                                       usedClasses);
  95                     }
  96                     usedClasses.add(usedClass);
  97                 }
  98             }
  99         }
 100 
 101         packageUseTableSummary = resources.getText("doclet.Use_Table_Summary",
 102                 resources.getText("doclet.packages"));
 103         this.navBar = new Navigation(packageElement, configuration, PageMode.USE, path);
 104     }
 105 
 106     /**
 107      * Generate a class page.
 108      *
 109      * @param configuration the current configuration of the doclet.
 110      * @param mapper        the mapping of the class usage.
 111      * @param pkgElement    the package being documented.
 112      * @throws DocFileIOException if there is a problem generating the package use page
 113      */
 114     public static void generate(HtmlConfiguration configuration,
 115                                 ClassUseMapper mapper, PackageElement pkgElement)
 116             throws DocFileIOException {
 117         DocPath filename = DocPaths.PACKAGE_USE;
 118         PackageUseWriter pkgusegen = new PackageUseWriter(configuration, mapper, filename, pkgElement);
 119         pkgusegen.generatePackageUseFile();
 120     }
 121 
 122     /**
 123      * Generate the package use list.
 124      * @throws DocFileIOException if there is a problem generating the package use page
 125      */
 126     protected void generatePackageUseFile() throws DocFileIOException {
 127         HtmlTree body = getPackageUseHeader();
 128         HtmlTree div = new HtmlTree(HtmlTag.DIV);
 129         div.setStyle(HtmlStyle.contentContainer);
 130         if (usingPackageToUsedClasses.isEmpty()) {
 131             div.add(contents.getContent("doclet.ClassUse_No.usage.of.0", utils.getPackageName(packageElement)));
 132         } else {
 133             addPackageUse(div);
 134         }
 135         bodyContents.addMainContent(div);
 136         HtmlTree footer = HtmlTree.FOOTER();
 137         navBar.setUserFooter(getUserHeaderFooter(false));
 138         footer.add(navBar.getContent(false));
 139         addBottom(footer);
 140         bodyContents.setFooter(footer);
 141         body.add(bodyContents.toContent());
 142         printHtmlDocument(null,
 143                 getDescription("use", packageElement),
 144                 body);
 145     }
 146 
 147     /**
 148      * Add the package use information.
 149      *
 150      * @param contentTree the content tree to which the package use information will be added
 151      */
 152     protected void addPackageUse(Content contentTree) {
 153         Content content = new ContentBuilder();
 154         if (configuration.packages.size() > 1) {
 155             addPackageList(content);
 156         }
 157         addClassList(content);
 158         contentTree.add(content);
 159     }
 160 
 161     /**
 162      * Add the list of packages that use the given package.
 163      *
 164      * @param contentTree the content tree to which the package list will be added
 165      */
 166     protected void addPackageList(Content contentTree) {
 167         Content caption = contents.getContent(
 168                 "doclet.ClassUse_Packages.that.use.0",
 169                 getPackageLink(packageElement, utils.getPackageName(packageElement)));
 170         Table table = new Table(HtmlStyle.useSummary)
 171                 .setCaption(caption)
 172                 .setHeader(getPackageTableHeader())
 173                 .setColumnStyles(HtmlStyle.colFirst, HtmlStyle.colLast);
 174         for (String pkgname: usingPackageToUsedClasses.keySet()) {
 175             PackageElement pkg = utils.elementUtils.getPackageElement(pkgname);
 176             Content packageLink = links.createLink(getPackageAnchorName(pkg),
 177                     new StringContent(utils.getPackageName(pkg)));
 178             Content summary = new ContentBuilder();
 179             if (pkg != null && !pkg.isUnnamed()) {
 180                 addSummaryComment(pkg, summary);
 181             } else {
 182                 summary.add(Entity.NO_BREAK_SPACE);
 183             }
 184             table.addRow(packageLink, summary);
 185         }
 186         contentTree.add(table.toContent());
 187     }
 188 
 189     /**
 190      * Add the list of classes that use the given package.
 191      *
 192      * @param contentTree the content tree to which the class list will be added
 193      */
 194     protected void addClassList(Content contentTree) {
 195         TableHeader classTableHeader = new TableHeader(
 196                 contents.classLabel, contents.descriptionLabel);
 197         HtmlTree ul = new HtmlTree(HtmlTag.UL);
 198         ul.setStyle(HtmlStyle.blockList);
 199         for (String packageName : usingPackageToUsedClasses.keySet()) {
 200             PackageElement usingPackage = utils.elementUtils.getPackageElement(packageName);
 201             HtmlTree section = HtmlTree.SECTION(HtmlStyle.detail)
 202                     .setId(getPackageAnchorName(usingPackage));
 203             String tableSummary = resources.getText("doclet.Use_Table_Summary",
 204                                                         resources.getText("doclet.classes"));
 205             Content caption = contents.getContent(
 206                     "doclet.ClassUse_Classes.in.0.used.by.1",
 207                     getPackageLink(packageElement, utils.getPackageName(packageElement)),
 208                     getPackageLink(usingPackage, utils.getPackageName(usingPackage)));
 209             Table table = new Table(HtmlStyle.useSummary)
 210                     .setCaption(caption)
 211                     .setHeader(classTableHeader)
 212                     .setColumnStyles(HtmlStyle.colFirst, HtmlStyle.colLast);
 213             for (TypeElement te : usingPackageToUsedClasses.get(packageName)) {
 214                 DocPath dp = pathString(te,
 215                         DocPaths.CLASS_USE.resolve(docPaths.forName(te)));
 216                 Content stringContent = new StringContent(utils.getSimpleName(te));
 217                 Content typeContent = links.createLink(dp.fragment(getPackageAnchorName(usingPackage)),
 218                         stringContent);
 219                 Content summary = new ContentBuilder();
 220                 addIndexComment(te, summary);
 221 
 222                 table.addRow(typeContent, summary);
 223             }
 224             section.add(table.toContent());
 225             ul.add(HtmlTree.LI(HtmlStyle.blockList, section));
 226         }
 227         Content li = HtmlTree.SECTION(HtmlStyle.packageUses, ul);
 228         contentTree.add(li);
 229     }
 230 
 231     /**
 232      * Get the header for the package use listing.
 233      *
 234      * @return a content tree representing the package use header
 235      */
 236     private HtmlTree getPackageUseHeader() {
 237         String packageText = resources.getText("doclet.Package");
 238         String name = packageElement.isUnnamed() ? "" : utils.getPackageName(packageElement);
 239         String title = resources.getText("doclet.Window_ClassUse_Header", packageText, name);
 240         HtmlTree bodyTree = getBody(getWindowTitle(title));
 241         Content headerContent = new ContentBuilder();
 242         addTop(headerContent);
 243         Content linkContent = getModuleLink(utils.elementUtils.getModuleOf(packageElement),
 244                 contents.moduleLabel);
 245         navBar.setNavLinkModule(linkContent);
 246         navBar.setUserHeader(getUserHeaderFooter(true));
 247         headerContent.add(navBar.getContent(true));
 248         ContentBuilder headingContent = new ContentBuilder();
 249         headingContent.add(contents.getContent("doclet.ClassUse_Title", packageText));
 250         headingContent.add(new HtmlTree(HtmlTag.BR));
 251         headingContent.add(name);
 252         Content heading = HtmlTree.HEADING(Headings.PAGE_TITLE_HEADING, true,
 253                 HtmlStyle.title, headingContent);
 254         Content div = HtmlTree.DIV(HtmlStyle.header, heading);
 255         bodyContents.setHeader(headerContent)
 256                 .addMainContent(div);
 257         return bodyTree;
 258     }
 259 }