1 /*
2  * Copyright (c) 2019, 2020, 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 package jdk.javadoc.internal.doclets.formats.html;
26 
27 import com.sun.source.doctree.DocTree;
28 import jdk.javadoc.internal.doclets.formats.html.markup.BodyContents;
29 import jdk.javadoc.internal.doclets.formats.html.markup.ContentBuilder;
30 import jdk.javadoc.internal.doclets.formats.html.markup.FixedStringContent;
31 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlStyle;
32 import jdk.javadoc.internal.doclets.formats.html.markup.HtmlTree;
33 import jdk.javadoc.internal.doclets.formats.html.Navigation.PageMode;
34 import jdk.javadoc.internal.doclets.formats.html.markup.StringContent;
35 import jdk.javadoc.internal.doclets.formats.html.markup.Table;
36 import jdk.javadoc.internal.doclets.formats.html.markup.TableHeader;
37 import jdk.javadoc.internal.doclets.toolkit.Content;
38 import jdk.javadoc.internal.doclets.toolkit.DocletElement;
39 import jdk.javadoc.internal.doclets.toolkit.OverviewElement;
40 import jdk.javadoc.internal.doclets.toolkit.util.DocFileIOException;
41 import jdk.javadoc.internal.doclets.toolkit.util.DocPath;
42 import jdk.javadoc.internal.doclets.toolkit.util.DocPaths;
43 import jdk.javadoc.internal.doclets.toolkit.util.IndexItem;
44 
45 import javax.lang.model.element.Element;
46 import java.nio.file.Path;
47 import java.util.List;
48 import java.util.Map;
49 import java.util.Map.Entry;
50 import java.util.TreeMap;
51 import java.util.WeakHashMap;
52 
53 import static java.util.stream.Collectors.groupingBy;
54 import static java.util.stream.Collectors.toList;
55 
56 /**
57  * Generates the file with the summary of all the system properties.
58  *
59  *  <p><b>This is NOT part of any supported API.
60  *  If you write code that depends on this, you do so at your own risk.
61  *  This code and its internal interfaces are subject to change or
62  *  deletion without notice.</b>
63  */
64 public class SystemPropertiesWriter extends HtmlDocletWriter {
65 
66     /**
67      * Cached contents of {@code <title>...</title>} tags of the HTML pages.
68      */
69     final Map<Element, String> titles = new WeakHashMap<>();
70 
71     /**
72      * Constructs SystemPropertiesWriter object.
73      *
74      * @param configuration The current configuration
75      * @param filename Path to the file which is getting generated.
76      */
SystemPropertiesWriter(HtmlConfiguration configuration, DocPath filename)77     public SystemPropertiesWriter(HtmlConfiguration configuration, DocPath filename) {
78         super(configuration, filename);
79     }
80 
generate(HtmlConfiguration configuration)81     public static void generate(HtmlConfiguration configuration) throws DocFileIOException {
82         generate(configuration, DocPaths.SYSTEM_PROPERTIES);
83     }
84 
generate(HtmlConfiguration configuration, DocPath fileName)85     private static void generate(HtmlConfiguration configuration, DocPath fileName) throws DocFileIOException {
86         boolean hasSystemProperties = configuration.mainIndex != null
87                 && !configuration.mainIndex.getItems(DocTree.Kind.SYSTEM_PROPERTY).isEmpty();
88         if (!hasSystemProperties) {
89             // Cannot defer this check any further, because of the super() call
90             // that prints out notices on creating files, etc.
91             //
92             // There is probably a better place for this kind of checks (see how
93             // this is achieved in other "optional" pages, like Constant Values
94             // and Serialized Form).
95             return;
96         }
97         SystemPropertiesWriter systemPropertiesGen = new SystemPropertiesWriter(configuration, fileName);
98         systemPropertiesGen.buildSystemPropertiesPage();
99         configuration.conditionalPages.add(HtmlConfiguration.ConditionalPage.SYSTEM_PROPERTIES);
100     }
101 
102     /**
103      * Prints all the system properties to the file.
104      */
buildSystemPropertiesPage()105     protected void buildSystemPropertiesPage() throws DocFileIOException {
106         String title = resources.getText("doclet.systemProperties");
107         HtmlTree body = getBody(getWindowTitle(title));
108         Content mainContent = new ContentBuilder();
109         addSystemProperties(mainContent);
110         body.add(new BodyContents()
111                 .setHeader(getHeader(PageMode.SYSTEM_PROPERTIES))
112                 .addMainContent(HtmlTree.DIV(HtmlStyle.header,
113                         HtmlTree.HEADING(Headings.PAGE_TITLE_HEADING,
114                                 contents.getContent("doclet.systemProperties"))))
115                 .addMainContent(mainContent)
116                 .setFooter(getFooter()));
117         printHtmlDocument(null, "system properties", body);
118 
119         if (configuration.mainIndex != null) {
120             configuration.mainIndex.add(IndexItem.of(IndexItem.Category.TAGS, title, path));
121         }
122     }
123 
124     /**
125      * Adds all the system properties to the content tree.
126      *
127      * @param content HtmlTree content to which the links will be added
128      */
addSystemProperties(Content content)129     protected void addSystemProperties(Content content) {
130         Map<String, List<IndexItem>> searchIndexMap = groupSystemProperties();
131         Content separator = new StringContent(", ");
132         Table table = new Table(HtmlStyle.summaryTable)
133                 .setCaption(contents.systemPropertiesSummaryLabel)
134                 .setHeader(new TableHeader(contents.propertyLabel, contents.referencedIn))
135                 .setColumnStyles(HtmlStyle.colFirst, HtmlStyle.colLast);
136         for (Entry<String, List<IndexItem>> entry : searchIndexMap.entrySet()) {
137             Content propertyName = new StringContent(entry.getKey());
138             List<IndexItem> searchIndexItems = entry.getValue();
139             Content separatedReferenceLinks = new ContentBuilder();
140             separatedReferenceLinks.add(createLink(searchIndexItems.get(0)));
141             for (int i = 1; i < searchIndexItems.size(); i++) {
142                 separatedReferenceLinks.add(separator);
143                 separatedReferenceLinks.add(createLink(searchIndexItems.get(i)));
144             }
145             table.addRow(propertyName, HtmlTree.DIV(HtmlStyle.block, separatedReferenceLinks));
146         }
147         content.add(table);
148     }
149 
groupSystemProperties()150     private Map<String, List<IndexItem>> groupSystemProperties() {
151         return configuration.mainIndex.getItems(DocTree.Kind.SYSTEM_PROPERTY).stream()
152                 .collect(groupingBy(IndexItem::getLabel, TreeMap::new, toList()));
153     }
154 
createLink(IndexItem i)155     private Content createLink(IndexItem i) {
156         assert i.getDocTree().getKind() == DocTree.Kind.SYSTEM_PROPERTY : i;
157         Element element = i.getElement();
158         if (element instanceof OverviewElement) {
159             return links.createLink(pathToRoot.resolve(i.getUrl()),
160                     resources.getText("doclet.Overview"));
161         } else if (element instanceof DocletElement) {
162             DocletElement e = (DocletElement) element;
163             // Implementations of DocletElement do not override equals and
164             // hashCode; putting instances of DocletElement in a map is not
165             // incorrect, but might well be inefficient
166             String t = titles.computeIfAbsent(element, utils::getHTMLTitle);
167             if (t.isBlank()) {
168                 // The user should probably be notified (a warning?) that this
169                 // file does not have a title
170                 Path p = Path.of(e.getFileObject().toUri());
171                 t = p.getFileName().toString();
172             }
173             ContentBuilder b = new ContentBuilder();
174             b.add(HtmlTree.CODE(new FixedStringContent(i.getHolder() + ": ")));
175             // non-program elements should be displayed using a normal font
176             b.add(t);
177             return links.createLink(pathToRoot.resolve(i.getUrl()), b);
178         } else {
179             // program elements should be displayed using a code font
180             Content link = links.createLink(pathToRoot.resolve(i.getUrl()), i.getHolder());
181             return HtmlTree.CODE(link);
182         }
183     }
184 }
185