001/*
002 * Copyright (c) 2019, 2022, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.  Oracle designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Oracle in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
022 * or visit www.oracle.com if you need additional information or have any
023 * questions.
024 */
025
026package org.jdrupes.mdoclet.internal.doclets.formats.html;
027
028import com.sun.source.doctree.DocTree;
029
030import javax.lang.model.element.Element;
031
032import org.jdrupes.mdoclet.internal.doclets.formats.html.Navigation.PageMode;
033import org.jdrupes.mdoclet.internal.doclets.formats.html.markup.BodyContents;
034import org.jdrupes.mdoclet.internal.doclets.formats.html.markup.ContentBuilder;
035import org.jdrupes.mdoclet.internal.doclets.formats.html.markup.HtmlStyle;
036import org.jdrupes.mdoclet.internal.doclets.formats.html.markup.HtmlTree;
037import org.jdrupes.mdoclet.internal.doclets.formats.html.markup.Text;
038import org.jdrupes.mdoclet.internal.doclets.toolkit.Content;
039import org.jdrupes.mdoclet.internal.doclets.toolkit.DocletElement;
040import org.jdrupes.mdoclet.internal.doclets.toolkit.OverviewElement;
041import org.jdrupes.mdoclet.internal.doclets.toolkit.util.DocFileIOException;
042import org.jdrupes.mdoclet.internal.doclets.toolkit.util.DocPath;
043import org.jdrupes.mdoclet.internal.doclets.toolkit.util.DocPaths;
044import org.jdrupes.mdoclet.internal.doclets.toolkit.util.IndexItem;
045
046import java.nio.file.Path;
047import java.util.*;
048import java.util.Map.Entry;
049
050import static java.util.stream.Collectors.groupingBy;
051import java.util.stream.Collectors;
052import java.util.ArrayList;
053
054/**
055 * Generates the file with the summary of all the system properties.
056 */
057public class SystemPropertiesWriter extends HtmlDocletWriter {
058
059    /**
060     * Cached contents of {@code <title>...</title>} tags of the HTML pages.
061     */
062    final Map<Element, String> titles = new WeakHashMap<>();
063
064    /**
065     * Constructs SystemPropertiesWriter object.
066     *
067     * @param configuration The current configuration
068     * @param filename Path to the file which is getting generated.
069     */
070    public SystemPropertiesWriter(HtmlConfiguration configuration,
071            DocPath filename) {
072        super(configuration, filename);
073    }
074
075    public static void generate(HtmlConfiguration configuration)
076            throws DocFileIOException {
077        generate(configuration, DocPaths.SYSTEM_PROPERTIES);
078    }
079
080    private static void generate(HtmlConfiguration configuration,
081            DocPath fileName) throws DocFileIOException {
082        boolean hasSystemProperties = configuration.mainIndex != null
083            && !configuration.mainIndex.getItems(DocTree.Kind.SYSTEM_PROPERTY)
084                .isEmpty();
085        if (!hasSystemProperties) {
086            // Cannot defer this check any further, because of the super() call
087            // that prints out notices on creating files, etc.
088            //
089            // There is probably a better place for this kind of checks (see how
090            // this is achieved in other "optional" pages, like Constant Values
091            // and Serialized Form).
092            return;
093        }
094        SystemPropertiesWriter systemPropertiesGen
095            = new SystemPropertiesWriter(configuration, fileName);
096        systemPropertiesGen.buildSystemPropertiesPage();
097        configuration.conditionalPages
098            .add(HtmlConfiguration.ConditionalPage.SYSTEM_PROPERTIES);
099    }
100
101    /**
102     * Prints all the system properties to the file.
103     */
104    protected void buildSystemPropertiesPage() throws DocFileIOException {
105        String title = resources.getText("doclet.systemProperties");
106        HtmlTree body = getBody(getWindowTitle(title));
107        Content mainContent = new ContentBuilder();
108        addSystemProperties(mainContent);
109        body.add(new BodyContents()
110            .setHeader(getHeader(PageMode.SYSTEM_PROPERTIES))
111            .addMainContent(HtmlTree.DIV(HtmlStyle.header,
112                HtmlTree.HEADING(Headings.PAGE_TITLE_HEADING,
113                    contents.getContent("doclet.systemProperties"))))
114            .addMainContent(mainContent)
115            .setFooter(getFooter()));
116        printHtmlDocument(null, "system properties", body);
117
118        if (configuration.mainIndex != null) {
119            configuration.mainIndex
120                .add(IndexItem.of(IndexItem.Category.TAGS, title, path));
121        }
122    }
123
124    /**
125     * Adds all the system properties to the content.
126     *
127     * @param target the content to which the links will be added
128     */
129    protected void addSystemProperties(Content target) {
130        Map<String, List<IndexItem>> searchIndexMap = groupSystemProperties();
131        Content separator = Text.of(", ");
132        var table = new Table<Void>(HtmlStyle.summaryTable)
133            .setCaption(contents.systemPropertiesSummaryLabel)
134            .setHeader(
135                new TableHeader(contents.propertyLabel, contents.referencedIn))
136            .setColumnStyles(HtmlStyle.colFirst, HtmlStyle.colLast);
137        for (Entry<String, List<IndexItem>> entry : searchIndexMap.entrySet()) {
138            Content propertyName = Text.of(entry.getKey());
139            List<IndexItem> searchIndexItems = entry.getValue();
140            Content separatedReferenceLinks = new ContentBuilder();
141            separatedReferenceLinks.add(createLink(searchIndexItems.get(0)));
142            for (int i = 1; i < searchIndexItems.size(); i++) {
143                separatedReferenceLinks.add(separator);
144                separatedReferenceLinks
145                    .add(createLink(searchIndexItems.get(i)));
146            }
147            table.addRow(propertyName,
148                HtmlTree.DIV(HtmlStyle.block, separatedReferenceLinks));
149        }
150        target.add(table);
151    }
152
153    private Map<String, List<IndexItem>> groupSystemProperties() {
154        return configuration.mainIndex.getItems(DocTree.Kind.SYSTEM_PROPERTY)
155            .stream()
156            .collect(groupingBy(IndexItem::getLabel, TreeMap::new,
157                Collectors.toCollection(ArrayList::new)));
158    }
159
160    private Content createLink(IndexItem i) {
161        assert i.getDocTree().getKind() == DocTree.Kind.SYSTEM_PROPERTY : i;
162        Element element = i.getElement();
163        if (element instanceof OverviewElement) {
164            return links.createLink(pathToRoot.resolve(i.getUrl()),
165                resources.getText("doclet.Overview"));
166        } else if (element instanceof DocletElement e) {
167            // Implementations of DocletElement do not override equals and
168            // hashCode; putting instances of DocletElement in a map is not
169            // incorrect, but might well be inefficient
170            String t = titles.computeIfAbsent(element, utils::getHTMLTitle);
171            if (t.isBlank()) {
172                // The user should probably be notified (a warning?) that this
173                // file does not have a title
174                Path p = Path.of(e.getFileObject().toUri());
175                t = p.getFileName().toString();
176            }
177            ContentBuilder b = new ContentBuilder();
178            b.add(HtmlTree.CODE(Text.of(i.getHolder() + ": ")));
179            // non-program elements should be displayed using a normal font
180            b.add(t);
181            return links.createLink(pathToRoot.resolve(i.getUrl()), b);
182        } else {
183            // program elements should be displayed using a code font
184            Content link = links.createLink(pathToRoot.resolve(i.getUrl()),
185                i.getHolder());
186            return HtmlTree.CODE(link);
187        }
188    }
189}