<commit_before>/*
 * Copyright 2020-2020 <NAME>, https://github.com/pponec
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.ujorm.tools.web.table;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Stream;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.ujorm.tools.Assert;
import org.ujorm.tools.Check;
import org.ujorm.tools.web.Element;
import org.ujorm.tools.web.Html;
import org.ujorm.tools.web.HtmlElement;
import org.ujorm.tools.web.ajax.JavaScriptWriter;
import org.ujorm.tools.web.ajax.ReqestDispatcher;
import org.ujorm.tools.web.ao.Column;
import org.ujorm.tools.web.ao.HttpParameter;
import org.ujorm.tools.web.ao.Injector;
import org.ujorm.tools.web.ao.WebUtils;
import org.ujorm.tools.web.json.JsonBuilder;
import org.ujorm.tools.xml.config.HtmlConfig;

/**
 * A HTML page builder for table based an AJAX.
 *
 * <br>Please note that this is an experimental implementation.
 *
 * <h3>Usage<h3>
 *
 * <pre class="pre">
 *  TableBuilder.of("Hotel Report", service.findHotels(ROW_LIMIT, NAME.of(input), CITY.of(input)))
 *          .add(Hotel::getName, "Hotel", NAME)
 *          .add(Hotel::getCity, "City", CITY)
 *          .add(Hotel::getStreet, "Street")
 *          .build(httpServletRequest, HtpServletResponse);
 * </pre>
 *
 * @author <NAME>
 */
public class TableBuilder<D> {

    /** Logger */
    private static final Logger LOGGER = Logger.getLogger(TableBuilder.class.getName());

    /** Columns */
    protected final List<ColumnModel<D,?>> columns = new ArrayList<>();
    /** Table builder config */
    protected final TableBuilderConfig config;
    /** AJAX request param */
    protected HttpParameter ajaxRequestParam = JavaScriptWriter.DEFAULT_AJAX_REQUEST_PARAM;
    /** Extension is empty by default */
    @Nonnull
    protected Injector htmlHeader = e -> {};
    /** Print a config title by default */
    @Nonnull
    protected Injector header = e -> e.addHeading(TableBuilder.this.config.getConfig().getTitle());
    /** Print an empty text by default */
    @Nonnull
    protected Injector footer = e -> e.addText("");
    /** Form injector */
    @Nonnull
    protected Injector formAdditions = footer;
    /** Javascript writer */
    @Nonnull
    protected Supplier<Injector> javascritWriter = () -> new JavaScriptWriter()
            .setSortable(TableBuilder.this.isSortable())
            .setAjax(TableBuilder.this.ajaxEnabled)
            .setSubtitleSelector("." + TableBuilder.this.config.getSubtitleCss());
    /** is An AJAX enabled? */
    protected boolean ajaxEnabled = true;
    /** Call an autosubmit on first load */
    protected boolean autoSubmmitOnLoad = false;
    /** Sorted column index */
    @Nullable
    private int sortedColumn = -1;

    public TableBuilder(@Nonnull CharSequence title) {
        this((HtmlConfig) HtmlConfig.ofDefault().setTitle(title).setNiceFormat());
    }

    public TableBuilder(@Nonnull HtmlConfig config) {
        this(TableBuilderConfig.of(config));
    }

    public TableBuilder(@Nonnull TableBuilderConfig config) {
        this.config = config;
    }

    @Nonnull
    public <V> TableBuilder<D> add(Function<D,V> column) {
        return addInternal(column, "Column-" + (columns.size() + 1), null);
    }

    @Nonnull
    public <V> TableBuilder<D> add(Function<D,V> column, CharSequence title) {
        return addInternal(column, title, null);
    }

    @Nonnull
    public <V> TableBuilder<D> add(Function<D,V> column, Injector title) {
        return addInternal(column, title, null);
    }

    @Nonnull
    public <V> TableBuilder<D> add(Function<D,V> column, CharSequence title, @Nullable HttpParameter param) {
        return addInternal(column, title, param);
    }

    @Nonnull
    public <V> TableBuilder<D> add(Function<D,V> column, Injector title, @Nullable HttpParameter param) {
        return addInternal(column, title, param);
    }

    @Deprecated
    @Nonnull
    public TableBuilder<D> addToElement(Column<D> column, CharSequence title) {
        return addColumn(column, title);
    }

    @Deprecated
    @Nonnull
    public TableBuilder<D> addToElement(Column<D> column, Injector title) {
        return addColumn(column, title);
    }

    @Nonnull
    public TableBuilder<D> addColumn(@Nonnull final Column<D> column, @Nonnull final CharSequence title) {
        return addInternal(column, title, null);
    }

    @Nonnull
    public TableBuilder<D> addColumn(@Nonnull final Column<D> column, @Nonnull final Injector title) {
        return addInternal(column, title, null);
    }

    /** Add new column for a row counting */
    @Nonnull
    public TableBuilder<D> addOrder(@Nonnull final CharSequence title) {
        final String textRight = "text-right";
        return addColumn(new Column<D>() {
            final AtomicLong order = new AtomicLong();
            @Override
            public void write(final Element e, final D row) {
                e.setClass(Html.A_CLASS, textRight).addText(order.incrementAndGet(), '.');
            }
            @Override
            public Object apply(D t) {
                return order.incrementAndGet();
            }
        }, e -> e.setClass(Html.A_CLASS, textRight).addText(title));
    }

    @Nonnull
    protected <V> TableBuilder<D> addInternal(
            @Nonnull final Function<D,V> column,
            @Nonnull final CharSequence title,
            @Nullable final HttpParameter param) {
        columns.add(new ColumnModel(columns.size(), column, title, param));
        return this;<commit_msg>WEB: TableBuilder extended implementaton for a special use-cases.<commit_after>/*
 * Copyright 2020-2020 <NAME>, https://github.com/pponec
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.ujorm.tools.web.table;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Stream;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.ujorm.tools.Assert;
import org.ujorm.tools.Check;
import org.ujorm.tools.web.Element;
import org.ujorm.tools.web.Html;
import org.ujorm.tools.web.HtmlElement;
import org.ujorm.tools.web.ajax.JavaScriptWriter;
import org.ujorm.tools.web.ajax.ReqestDispatcher;
import org.ujorm.tools.web.ao.Column;
import org.ujorm.tools.web.ao.HttpParameter;
import org.ujorm.tools.web.ao.Injector;
import org.ujorm.tools.web.ao.WebUtils;
import org.ujorm.tools.web.json.JsonBuilder;
import org.ujorm.tools.xml.config.HtmlConfig;

/**
 * A HTML page builder for table based an AJAX.
 *
 * <br>Please note that this is an experimental implementation.
 *
 * <h3>Usage<h3>
 *
 * <pre class="pre">
 *  TableBuilder.of("Hotel Report", service.findHotels(ROW_LIMIT, NAME.of(input), CITY.of(input)))
 *          .add(Hotel::getName, "Hotel", NAME)
 *          .add(Hotel::getCity, "City", CITY)
 *          .add(Hotel::getStreet, "Street")
 *          .build(httpServletRequest, HtpServletResponse);
 * </pre>
 *
 * @author <NAME>
 */
public class TableBuilder<D> {

    /** Logger */
    private static final Logger LOGGER = Logger.getLogger(TableBuilder.class.getName());

    /** Columns */
    protected final List<ColumnModel<D,?>> columns = new ArrayList<>();
    /** Table builder config */
    protected final TableBuilderConfig config;
    /** AJAX request param */
    protected HttpParameter ajaxRequestParam = JavaScriptWriter.DEFAULT_AJAX_REQUEST_PARAM;
    /** Extension is empty by default */
    @Nonnull
    protected Injector htmlHeader = e -> {};
    /** Print a config title by default */
    @Nonnull
    protected Injector header = e -> e.addHeading(TableBuilder.this.config.getConfig().getTitle());
    /** Print an empty text by default */
    @Nonnull
    protected Injector footer = e -> e.addText("");
    /** Form injector */
    @Nonnull
    protected Injector formAdditions = footer;
    /** Javascript writer */
    @Nonnull
    protected Supplier<Injector> javascritWriter = () -> new JavaScriptWriter()
            .setSortable(TableBuilder.this.isSortable())
            .setAjax(TableBuilder.this.ajaxEnabled)
            .setSubtitleSelector("." + TableBuilder.this.config.getSubtitleCss());
    /** is An AJAX enabled? */
    protected boolean ajaxEnabled = true;
    /** Call an autosubmit on first load */
    protected boolean autoSubmmitOnLoad = false;
    /** Sorted column index */
    @Nullable
    private int sortedColumn = -1;

    public TableBuilder(@Nonnull CharSequence title) {
        this((HtmlConfig) HtmlConfig.ofDefault().setTitle(title).setNiceFormat());
    }

    public TableBuilder(@Nonnull HtmlConfig config) {
        this(TableBuilderConfig.of(config));
    }

    public TableBuilder(@Nonnull TableBuilderConfig config) {
        this.config = config;
    }

    @Nonnull
    public <V> TableBuilder<D> add(Function<D,V> column) {
        return addInternal(column, "Column-" + (columns.size() + 1), null);
    }

    @Nonnull
    public <V> TableBuilder<D> add(Function<D,V> column, CharSequence title) {
        return addInternal(column, title, null);
    }

    @Nonnull
    public <V> TableBuilder<D> add(Function<D,V> column, Injector title) {
        return addInternal(column, title, null);
    }

    @Nonnull
    public <V> TableBuilder<D> add(Function<D,V> column, CharSequence title, @Nullable HttpParameter param) {
        return addInternal(column, title, param);
    }

    @Nonnull
    public <V> TableBuilder<D> add(Function<D,V> column, Injector title, @Nullable HttpParameter param) {
        return addInternal(column, title, param);
    }

    @Deprecated
    @Nonnull
    public TableBuilder<D> addToElement(Column<D> column, CharSequence title) {
        return addColumn(column, title);
    }

    @Deprecated
    @Nonnull
    public TableBuilder<D> addToElement(Column<D> column, Injector title) {
        return addColumn(column, title);
    }

    @Nonnull
    public TableBuilder<D> addColumn(@Nonnull final Column<D> column, @Nonnull final CharSequence title) {
        return addInternal(column, title, null);
    }

    @Nonnull
    public TableBuilder<D> addColumn(@Nonnull final Column<D> column, @Nonnull final Injector title) {
        return addInternal(column, title, null);
    }

    /** Add new column for a row counting */
    @Nonnull
    public TableBuilder<D> addOrder(@Nonnull final CharSequence title) {
        final String textRight = "text-right";
        return addColumn(new Column<D>() {
            final AtomicLong order = new AtomicLong();
            @Override
            public void write(final Element e, final D row) {
                e.setClass(Html.A_CLASS, textRight).addText(apply(row), '.');
            }
            @Override
            public Object apply(D t) {
                return order.incrementAndGet();
            }
        }, e -> e.setClass(Html.A_CLASS, textRight).addText(title));
    }

    @Nonnull
    protected <V> TableBuilder<D> addInternal(
            @Nonnull final Function<D,V> column,
            @Nonnull final CharSequence title,
            @Nullable final HttpParameter param) {
        columns.add(new ColumnModel(columns.size(), column, title, param));
        return this;
{'fraction_non_alphanumeric': 0.11522823168392789, 'fraction_numerical': 0.002454929037207518, 'mean_word_length': 3.2783065310141124, 'pattern_counts': {'":': 0, '<': 83, '<?xml version=': 0, '>': 93, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3112286', 'n_tokens_mistral': 3833, 'n_tokens_neox': 3739, 'n_words': 1000}