com.github.ukase.toolkit.xlsx.RenderingTable.java Source code

Java tutorial

Introduction

Here is the source code for com.github.ukase.toolkit.xlsx.RenderingTable.java

Source

/*
 * Copyright (c) 2016 Konstantin Lepa <konstantin+ukase@lepabox.net>
 *
 * This file is part of Ukase.
 *
 *  Ukase is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

package com.github.ukase.toolkit.xlsx;

import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xhtmlrenderer.css.constants.CSSName;
import org.xhtmlrenderer.css.style.CalculatedStyle;
import org.xhtmlrenderer.css.style.derived.BorderPropertySet;
import org.xhtmlrenderer.newtable.TableCellBox;
import org.xhtmlrenderer.newtable.TableRowBox;
import org.xhtmlrenderer.render.BlockBox;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import static com.github.ukase.toolkit.xlsx.XlsxUtil.*;

public class RenderingTable implements Runnable {
    private static final int CALCULATED_KOEF = 36;
    private static final String TAG_TD = "td";
    private static final String TAG_TR = "tr";
    private static final String TAG_CAPTION = "caption";
    private static final String TAG_TH = "th";
    private static final String ATTR_COL_SPAN = "colspan";
    private static final String ATTR_ROW_SPAN = "rowspan";
    private final Workbook wb;
    private final Sheet sheet;
    private final BlockBox box;
    private final Element table;
    private final List<CellMerge> mergedCells;
    private final ConcurrentMap<Integer, Integer> cellSizes;

    RenderingTable(Workbook wb, Element table, BlockBox box) {
        this.wb = wb;
        this.box = box;
        this.table = table;
        this.mergedCells = new ArrayList<>();
        this.cellSizes = new ConcurrentHashMap<>();
        this.sheet = prepareSheet();
    }

    @Override
    public void run() {
        new ElementList(table.getElementsByTagName(TAG_TR)).stream().forEach(this::processRows);
        cellSizes.forEach(sheet::setColumnWidth);
    }

    private Sheet prepareSheet() {
        NodeList captionList = table.getElementsByTagName(TAG_CAPTION);
        if (captionList.getLength() > 0) {
            return wb.createSheet(captionList.item(0).getTextContent());
        }
        return wb.createSheet();
    }

    private void processRows(Element tr) {
        Row row = sheet.createRow(sheet.getPhysicalNumberOfRows());

        new ElementList(tr.getChildNodes()).stream().filter(this::isTableCellTag)
                .forEach(td -> processCell(row, td));

        updateRowStyle(tr, row);
    }

    private void updateRowStyle(Element tr, Row row) {
        XSSFCellStyle cellStyle = (XSSFCellStyle) wb.createCellStyle();
        TableRowBox rowBox = (TableRowBox) (box.getElementBoxes(tr)).get(0);
        CalculatedStyle style = rowBox.getStyle();

        cellStyle.setAlignment(prepareAlignment(style.getIdent(CSSName.TEXT_ALIGN)));

        row.setRowStyle(cellStyle);
    }

    private boolean isTableCellTag(Element tag) {
        String tagName = tag.getTagName();
        return TAG_TH.equals(tagName) || TAG_TD.equals(tagName);
    }

    private void processCell(Row row, Element td) {
        TableCellBox cellBox = (TableCellBox) (box.getElementBoxes(td)).get(0);
        CellStyle style = prepareCellStyle(cellBox.getStyle());

        mergedCells.stream().filter(merge -> merge.isApplicable(row)).forEach(merge -> merge.fillRow(row));

        int cellNumber = row.getPhysicalNumberOfCells();
        Cell cell = row.createCell(cellNumber);
        cell.setCellValue(td.getTextContent());
        cell.setCellStyle(style);

        mergeCells(row, td, cellNumber, style);
        calculateColumnWidth(cellNumber, cellBox.getStyle());
    }

    private void mergeCells(Row row, Element td, int cellNumber, CellStyle style) {
        int columns = intValue(td.getAttribute(ATTR_COL_SPAN), 1);
        int rows = intValue(td.getAttribute(ATTR_ROW_SPAN), 1);
        if (columns == 1 && rows == 1) {
            return;
        }

        CellMerge merge = new CellMerge(cellNumber, columns, row.getRowNum(), rows, style);
        merge.apply(sheet);
        merge.fillRow(row);
        mergedCells.add(merge);
    }

    private CellStyle prepareCellStyle(CalculatedStyle style) {
        XSSFCellStyle cellStyle = (XSSFCellStyle) wb.createCellStyle();

        BorderPropertySet border = style.getBorder(null);
        cellStyle.setBorderTop(prepareTopBorder(border));
        cellStyle.setBorderRight(prepareRightBorder(border));
        cellStyle.setBorderBottom(prepareBottomBorder(border));
        cellStyle.setBorderLeft(prepareLeftBorder(border));

        cellStyle.setAlignment(prepareAlignment(style.getIdent(CSSName.TEXT_ALIGN)));

        return cellStyle;
    }

    private void calculateColumnWidth(int cellNumber, CalculatedStyle style) {
        if (style.isAutoWidth()) {
            return;
        }
        int width = Math.round(style.valueByName(CSSName.WIDTH).asFloat() * CALCULATED_KOEF);
        if (width > 0) {
            cellSizes.compute(cellNumber, (num, w) -> greaterInt(w, width));
        }
    }
}