001/*
002 * Copyright (c) 2017, 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 java.io.IOException;
029import java.io.Writer;
030import java.util.Arrays;
031import java.util.List;
032
033import org.jdrupes.mdoclet.internal.doclets.formats.html.markup.ContentBuilder;
034import org.jdrupes.mdoclet.internal.doclets.formats.html.markup.HtmlAttr;
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.toolkit.Content;
038
039/**
040 * A row of header cells for an HTML table.
041 *
042 * The header contains a list of {@code <th>} cells, providing the column headers.
043 * The attribute {@code scope="col"} is automatically added to each header cell.
044 * In addition, a series of style class names can be specified, to be applied one per cell.
045 */
046public class TableHeader extends Content {
047
048    /**
049     * The content to be put in each of the {@code <th>} cells in the header row.
050     */
051    private final List<Content> cellContents;
052    /**
053     * The style class names for each of the {@code <th>} cells in the header row.
054     * If not set, default style names will be used.
055     */
056    private List<HtmlStyle> styles;
057
058    private boolean[] sortable;
059
060    /**
061     * Creates a header row, with localized content for each cell.
062     * Resources keys will be converted to content using {@link Contents#getContent(String)}.
063     * @param contents a factory to get the content for each header cell.
064     * @param colHeaderKeys the resource keys for the content in each cell.
065     */
066    public TableHeader(Contents contents, String... colHeaderKeys) {
067        this.cellContents = Arrays.stream(colHeaderKeys)
068            .map(contents::getContent)
069            .toList();
070    }
071
072    /**
073     * Creates a header row, with specified content for each cell.
074     * @param headerCellContents a content object for each header cell
075     */
076    public TableHeader(Content... headerCellContents) {
077        this.cellContents = Arrays.asList(headerCellContents);
078    }
079
080    /**
081     * Creates a header row, with specified content for each cell.
082     * @param headerCellContents a content object for each header cell
083     */
084    public TableHeader(List<Content> headerCellContents) {
085        this.cellContents = headerCellContents;
086    }
087
088    /**
089     * Set the style class names for each header cell.
090     * The number of names must match the number of cells given to the constructor.
091     * @param styles the style class names
092     * @return this object
093     */
094    public TableHeader styles(HtmlStyle... styles) {
095        if (styles.length != cellContents.size()) {
096            throw new IllegalStateException();
097        }
098        this.styles = Arrays.asList(styles);
099        return this;
100    }
101
102    /**
103     * Makes the table sortable by the content of columns for which the
104     * argument boolean array contains {@code true}.
105     * @param sortable boolean array specifying sortable columns
106     * @return this object
107     */
108    public TableHeader sortable(boolean... sortable) {
109        if (sortable.length != cellContents.size()) {
110            throw new IllegalStateException();
111        }
112        this.sortable = sortable;
113        return this;
114    }
115
116    /**
117     * Set the style class names for each header cell.
118     * The number of names must match the number of cells given to the constructor.
119     * @param styles the style class names
120     * @return this object
121     */
122    public TableHeader styles(List<HtmlStyle> styles) {
123        if (styles.size() != cellContents.size()) {
124            throw new IllegalStateException();
125        }
126        this.styles = styles;
127        return this;
128    }
129
130    /**
131     * {@inheritDoc}
132     *
133     * @implSpec This implementation always returns {@code false}.
134     *
135     * @return {@code false}
136     */
137    @Override
138    public boolean isEmpty() {
139        return false;
140    }
141
142    @Override
143    public boolean write(Writer out, String newline, boolean atNewline)
144            throws IOException {
145        return toContent().write(out, newline, atNewline);
146    }
147
148    /**
149     * Converts this header to a {@link Content} object, for use in an {@link HtmlTree}.
150     * @return a Content object
151     */
152    private Content toContent() {
153        Content header = new ContentBuilder();
154        int i = 0;
155        for (Content cellContent : cellContents) {
156            HtmlStyle style = (styles != null) ? styles.get(i)
157                : (i == 0) ? HtmlStyle.colFirst
158                    : (i == (cellContents.size() - 1)) ? HtmlStyle.colLast
159                        : (i == 1) ? HtmlStyle.colSecond : null;
160            var cell = HtmlTree.DIV(HtmlStyle.tableHeader, cellContent);
161            if (style != null) {
162                cell.addStyle(style);
163            }
164            if (sortable != null && sortable[i]) {
165                cell.put(HtmlAttr.ONCLICK,
166                    "sortTable(this, " + i + ", " + sortable.length + ")");
167                // Current tables happen to be sorted by first column by
168                // default, this may not hold true for future uses.
169                if (i == 0) {
170                    cell.addStyle("sort-asc");
171                }
172            }
173            header.add(cell);
174            i++;
175        }
176        return header;
177    }
178
179}