All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.primefaces.component.export.TableExporter Maven / Gradle / Ivy

There is a newer version: 14.0.0
Show newest version
/*
 * The MIT License
 *
 * Copyright (c) 2009-2023 PrimeTek Informatics
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package org.primefaces.component.export;

import java.io.IOException;
import java.io.OutputStream;
import java.util.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.ObjIntConsumer;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import javax.faces.FacesException;
import javax.faces.component.UIComponent;
import javax.faces.component.visit.VisitCallback;
import javax.faces.component.visit.VisitContext;
import javax.faces.component.visit.VisitResult;
import javax.faces.context.FacesContext;

import org.primefaces.component.api.DynamicColumn;
import org.primefaces.component.api.UIColumn;
import org.primefaces.component.api.UITable;
import org.primefaces.component.columngroup.ColumnGroup;
import org.primefaces.model.ColumnMeta;
import org.primefaces.util.IOUtils;
import org.primefaces.util.LangUtils;

public abstract class TableExporter implements Exporter {

    protected static final Set ALL_FACETS = EnumSet.allOf(FacetType.class);

    private static final Logger LOGGER = Logger.getLogger(TableExporter.class.getName());

    protected D document;

    protected ExportConfiguration exportConfiguration;

    // Because more than 1 table can be exported we cache each one for performance
    private final Map> exportableColumns = new HashMap<>();

    private final O defaultOptions;

    private final Set supportedFacetTypes;

    private final boolean cellJoinComponents;

    protected enum ColumnType {
        HEADER("header"),
        FOOTER("footer");

        private final String facet;

        ColumnType(String facet) {
            this.facet = facet;
        }

        public String facet() {
            return facet;
        }

        @Override
        public String toString() {
            return facet;
        }
    }

    protected enum FacetType {
        TABLE,
        COLUMN_GROUP,
        COLUMN
    }

    protected TableExporter(O defaultOptions) {
        this(defaultOptions, ALL_FACETS, true);
    }

    protected TableExporter(O defaultOptions, Set supportedFacetTypes, boolean cellJoinComponents) {
        this.defaultOptions = defaultOptions;
        this.supportedFacetTypes = supportedFacetTypes;
        this.cellJoinComponents = cellJoinComponents;
    }

    @Override
    public void export(FacesContext context, List tables, ExportConfiguration exportConfiguration) throws IOException {
        this.exportConfiguration = exportConfiguration;

        try {
            preExport(context);

            ExportVisitCallback exportCallback = new ExportVisitCallback(tables);
            exportCallback.export(context);

            postExport(context);
        }
        finally {
            if (document instanceof AutoCloseable) {
                IOUtils.closeQuietly((AutoCloseable) document, e -> LOGGER.log(Level.SEVERE, e.getMessage(), e));
            }
        }
    }

    protected void exportTable(FacesContext context, T table, int index) throws IOException {
        if (exportConfiguration.getOnTableRender() != null) {
            exportConfiguration.getOnTableRender().invoke(context.getELContext(), getOnTableRenderArgs());
        }

        if (exportConfiguration.isExportHeader()) {
            addTableFacets(context, table, ColumnType.HEADER);
            boolean headerGroup = addColumnGroupFacets(context, table, ColumnType.HEADER);
            if (!headerGroup) {
                addColumnFacets(context, table, ColumnType.HEADER);
            }
        }

        if (exportConfiguration.isPageOnly()) {
            exportPageOnly(context, table);
        }
        else if (exportConfiguration.isSelectionOnly()) {
            exportSelectionOnly(context, table);
        }
        else {
            exportAll(context, table);
        }

        if (exportConfiguration.isExportFooter()) {
            if (table.hasFooterColumn()) {
                addColumnFacets(context, table, ColumnType.FOOTER);
            }

            addTableFacets(context, table, ColumnType.FOOTER);
        }
    }

    protected void addTableFacets(FacesContext context, T table, ColumnType columnType) {
        if (!supportedFacetTypes.contains(FacetType.TABLE)) {
            return;
        }

        String facetText = ExporterUtils.getComponentFacetValue(context, table, columnType.facet());
        if (LangUtils.isNotBlank(facetText)) {
            proxifyWithRowExport(context,
                    table,
                    0,
                    -1,
                    () -> exportTabletFacetValue(context, table, facetText));
        }
    }

    protected void addColumnFacets(FacesContext context, T table, ColumnType columnType) throws IOException {
        if (!supportedFacetTypes.contains(FacetType.COLUMN)) {
            return;
        }

        addRow(context, table, (col, i) -> {
            String textValue = ExporterUtils.getColumnFacetValue(context, col, columnType);
            exportColumnFacetValue(context, table, textValue, i);
        });
    }

    protected boolean addColumnGroupFacets(FacesContext context, T table, ColumnType columnType) {
        if (!supportedFacetTypes.contains(FacetType.COLUMN_GROUP)) {
            return false;
        }

        ColumnGroup cg = table.getColumnGroup(columnType.facet());
        if (cg == null || cg.getChildCount() == 0) {
            return false;
        }

        int total = getExportableColumns(table).size();
        table.forEachColumnGroupRow(context, cg, true, row -> {
            final AtomicInteger colIndex = new AtomicInteger(0);

            table.forEachColumn(context, row, true, true, false, column -> {
                if (column.isExportable()) {
                    String text = ExporterUtils.getColumnFacetValue(context, column, columnType);

                    proxifyWithRowExport(context,
                            table,
                            colIndex.get(),
                            total,
                            () -> exportColumnGroupFacetValue(context, table, column, colIndex, text));

                    colIndex.incrementAndGet();
                }
                return true;
            });
            return true;
        });
        return true;
    }

    protected void addCells(FacesContext context, T table) {
        addRow(context, table, (col, i) ->
                exportCellValue(context, table, col, ExporterUtils.getColumnValue(context, table, col, cellJoinComponents), i)
        );
    }

    protected void exportTabletFacetValue(FacesContext context, T table, String textValue) {
        if (supportedFacetTypes.contains(FacetType.TABLE)) {
            throw new UnsupportedOperationException(getClass().getName() + "#exportTabletFacetValue() must be implemented");
        }
    }

    protected void exportColumnFacetValue(FacesContext context, T table, String text, int index) {
        if (supportedFacetTypes.contains(FacetType.COLUMN)) {
            throw new UnsupportedOperationException(getClass().getName() + "#exportColumnFacetValue() must be implemented");
        }
    }

    protected void exportColumnGroupFacetValue(FacesContext context, T table, UIColumn column, AtomicInteger colIndex, String text) {
        if (supportedFacetTypes.contains(FacetType.COLUMN_GROUP)) {
            throw new UnsupportedOperationException(getClass().getName() + "#exportColumnGroupFacetValue() must be implemented");
        }
    }

    protected abstract void exportCellValue(FacesContext context, T table, UIColumn col, String text, int index);

    protected abstract void exportPageOnly(FacesContext context, T table);

    protected abstract void exportAll(FacesContext context, T table);

    protected abstract void exportSelectionOnly(FacesContext context, T table);

    protected void preExport(FacesContext context) throws IOException {
        document = createDocument(context);
        if (exportConfiguration.getPreProcessor() != null) {
            exportConfiguration.getPreProcessor().invoke(context.getELContext(), new Object[]{document});
        }
    }

    protected void postExport(FacesContext context) throws IOException {
        if (exportConfiguration.getPostProcessor() != null) {
            exportConfiguration.getPostProcessor().invoke(context.getELContext(), new Object[]{document});
        }
    }

    protected void preRowExport(FacesContext context, T table) {
        // NOOP
    }

    protected void postRowExport(FacesContext context, T table) {
        if (exportConfiguration.getOnRowExport() != null) {
            exportConfiguration.getOnRowExport().invoke(context.getELContext(), new Object[]{document});
        }
    }

    protected abstract D createDocument(FacesContext context) throws IOException;

    private void addRow(FacesContext context, T table, ObjIntConsumer callback) {
        List columns = getExportableColumns(table);
        for (int i = 0; i < columns.size(); i++) {
            UIColumn col = columns.get(i);
            if (col instanceof DynamicColumn) {
                ((DynamicColumn) col).applyStatelessModel();
            }

            final int finalI = i;
            proxifyWithRowExport(context,
                    table,
                    i,
                    columns.size(),
                    () -> callback.accept(col, finalI));
        }
    }

    private void proxifyWithRowExport(FacesContext context, T table, int colIndex, int colTotal, Runnable callback) {
        if (colIndex == 0) {
            preRowExport(context, table);
        }

        callback.run();

        if (colIndex == colTotal - 1) {
            postRowExport(context, table);
        }
    }

    private class ExportVisitCallback implements VisitCallback {

        private final List tables;
        private int index;

        public ExportVisitCallback(List tables) {
            this.tables = tables;
            this.index = 0;
        }

        @Override
        public VisitResult visit(VisitContext context, UIComponent component) {
            try {
                exportTable(context.getFacesContext(), (T) component, index);
                index++;
            }
            catch (IOException e) {
                throw new FacesException(e);
            }

            return VisitResult.ACCEPT;
        }

        public void export(FacesContext context) {
            List tableIds = tables.stream()
                    .map(dt -> dt.getClientId(context))
                    .collect(Collectors.toList());

            VisitContext visitContext = VisitContext.createVisitContext(context, tableIds, null);
            context.getViewRoot().visitTree(visitContext, this);
        }
    }

    /**
     * Gets and caches the list of UIColumns that are exportable="true", visible="true", and rendered="true".
     * Orders them by displayPriority so they match the UI display of the columns.
     *
     * @param table the Table with columns to export
     * @return the List that are exportable
     */
    protected List getExportableColumns(T table) {
        if (exportableColumns.containsKey(table)) {
            return exportableColumns.get(table);
        }

        int allColumnsSize = table.getColumns().size();
        List exportcolumns = new ArrayList<>(allColumnsSize);
        boolean visibleColumnsOnly = exportConfiguration.isVisibleOnly();
        final AtomicBoolean hasNonDefaultSortPriorities = new AtomicBoolean(false);
        final List visibleColumnMetadata = new ArrayList<>(allColumnsSize);
        Map allColumnMeta = table.getColumnMeta();

        table.forEachColumn(true, true, true, column -> {
            if (column.isExportable()) {
                String columnKey = column.getColumnKey();
                ColumnMeta currentMeta = allColumnMeta.get(columnKey);
                if (!visibleColumnsOnly || (visibleColumnsOnly && (currentMeta == null || currentMeta.getVisible()))) {
                    int displayPriority = column.getDisplayPriority();
                    ColumnMeta metaCopy = new ColumnMeta(columnKey);
                    metaCopy.setDisplayPriority(displayPriority);
                    visibleColumnMetadata.add(metaCopy);
                    if (displayPriority != 0) {
                        hasNonDefaultSortPriorities.set(true);
                    }
                }
            }
            return true;
        });

        if (hasNonDefaultSortPriorities.get()) {
            // sort by display priority
            Comparator sortIntegersNaturallyWithNullsLast = Comparator.nullsLast(Comparator.naturalOrder());
            visibleColumnMetadata.sort(Comparator.comparing(ColumnMeta::getDisplayPriority, sortIntegersNaturallyWithNullsLast));
        }

        for (ColumnMeta meta : visibleColumnMetadata) {
            String metaColumnKey = meta.getColumnKey();
            table.invokeOnColumn(metaColumnKey, -1, exportcolumns::add);
        }

        exportableColumns.put(table, exportcolumns);

        return exportcolumns;
    }

    protected O options() {
        ExporterOptions opts = exportConfiguration.getOptions();
        if (opts != null) {
            if (defaultOptions.getClass().isAssignableFrom(opts.getClass())) {
                return (O) opts;
            }
            else {
                throw new IllegalArgumentException("Options must be an instance of " + defaultOptions.getClass().getName());
            }
        }

        return defaultOptions;
    }

    protected OutputStream os() {
        return exportConfiguration.getOutputStream();
    }

    protected Object[] getOnTableRenderArgs() {
        return new Object[]{document};
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy