org.wicketstuff.poi.excel.TableParser.java Source code

Java tutorial

Introduction

Here is the source code for org.wicketstuff.poi.excel.TableParser.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You 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.wicketstuff.poi.excel;

import java.io.IOException;
import java.text.ParseException;
import java.util.HashMap;
import java.util.Map;

import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.wicket.Application;
import org.apache.wicket.Component;
import org.apache.wicket.Page;
import org.apache.wicket.application.IComponentOnBeforeRenderListener;
import org.apache.wicket.behavior.Behavior;
import org.apache.wicket.markup.ComponentTag;
import org.apache.wicket.markup.parser.XmlPullParser;
import org.apache.wicket.markup.parser.XmlTag;
import org.apache.wicket.protocol.http.BufferedWebResponse;
import org.apache.wicket.request.Response;
import org.apache.wicket.request.cycle.RequestCycle;
import org.apache.wicket.util.resource.ResourceStreamNotFoundException;

/**
 * Parse a Wicket component and generates a {@link Sheet} based on its markup and component models.
 *
 * @author Pedro Santos
 */
public class TableParser {
    private Row row;
    private Cell cell;
    private final Map<Integer, Integer> rowsToSpanByColumn = new HashMap<Integer, Integer>();
    private final Map<Integer, Integer> columnSpan = new HashMap<Integer, Integer>();
    private int colsToSpan;
    private final Sheet targetSheet;
    private CellExporter cellExporter;
    private Response originalResponse;

    public TableParser(Sheet sheet, CellExporter cellExporter) {
        this.cellExporter = cellExporter;
        targetSheet = sheet;
    }

    /**
     * Parse the grid component to a {@link Sheet} object
     *
     * @param tableComponent
     * @throws IOException
     * @throws ResourceStreamNotFoundException
     * @throws ParseException
     */
    public void parse(Component tableComponent)
            throws IOException, ResourceStreamNotFoundException, ParseException {
        try {
            BufferedWebResponse mockResponse = doRequest(tableComponent);
            doParse(mockResponse.getText(), tableComponent);
        } finally {
            afterParse(tableComponent);
        }
    }

    private void doParse(CharSequence gridComponentMarkup, Component tableComponent)
            throws IOException, ResourceStreamNotFoundException, ParseException {
        XmlPullParser parser = new XmlPullParser();
        parser.parse(gridComponentMarkup);
        XmlTag tag;
        int tableDeep = 0;
        while ((tag = parser.nextTag()) != null) {
            if ("table".equals(tag.getName().toLowerCase())) {
                if (tag.isOpen()) {
                    tableDeep++;
                } else {
                    tableDeep--;
                }
            }
            if (tableDeep > 1) {
                // we don't want to read inner tables
                continue;
            }
            if (tag.isOpen()) {
                String tagName = tag.getName().toLowerCase();

                if ("tr".equals(tagName)) {
                    if (tableDeep == 0) {
                        // means that root table is outside the component markup
                        tableDeep = 1;
                    }
                    int index = row == null ? 0 : row.getRowNum() + 1;
                    row = targetSheet.createRow(index);
                    cell = null;
                } else if ("td".equals(tagName) || "th".equals(tagName)) {
                    int index = cell == null ? 0 : cell.getColumnIndex() + 1 + colsToSpan;
                    if (skipColumn(index)) {
                        index += columnSpan.get(index);
                    }
                    colsToSpan = 0;
                    CharSequence rowspan = tag.getAttribute("rowspan");
                    CharSequence colspan = tag.getAttribute("colspan");
                    cell = row.createCell(index);
                    if (rowspan != null || colspan != null) {
                        int rowsToSpan = rowspan == null ? 0 : Integer.valueOf(rowspan.toString()) - 1;
                        colsToSpan = colspan == null ? 0 : Integer.valueOf(colspan.toString()) - 1;

                        if (rowsToSpan > 0) {
                            rowsToSpanByColumn.put(index, rowsToSpan);
                            columnSpan.put(index, colsToSpan + 1);
                        }

                        int lastRowNum = row.getRowNum() + rowsToSpan;
                        int lastColIndex = index + colsToSpan;
                        targetSheet.addMergedRegion(new CellRangeAddress(//
                                row.getRowNum(), // first row (0-based)
                                lastRowNum, // last row (0-based)
                                index, // first column (0-based)
                                lastColIndex // last column (0-based)
                        ));
                    }
                    cellExporter.exportCell(tag, parser, cell, tableComponent);
                }
            }
        }
    }

    private boolean skipColumn(int column) {
        Integer rowspan = rowsToSpanByColumn.remove(column);
        if (rowspan != null && rowspan > 0) {
            rowsToSpanByColumn.put(column, rowspan - 1);
            return true;
        } else {
            return false;
        }
    }

    /**
     * Mock a request to table component and return its response.
     *
     * @param tableComponent
     * @return
     */
    private BufferedWebResponse doRequest(Component tableComponent) {
        originalResponse = RequestCycle.get().getResponse();
        BufferedWebResponse mockResponse = new BufferedWebResponse(null);
        RequestCycle.get().setResponse(mockResponse);
        Application.get().getComponentPreOnBeforeRenderListeners().add(PathSetupListener.INSTANCE);
        Page page = tableComponent.getPage();
        page.startComponentRender(tableComponent);
        tableComponent.prepareForRender();
        tableComponent.render();
        return mockResponse;
    }

    private void afterParse(Component tableComponent) {
        tableComponent.getPage().endComponentRender(tableComponent);
        Application.get().getComponentPreOnBeforeRenderListeners().remove(PathSetupListener.INSTANCE);
        RequestCycle.get().setResponse(originalResponse);
        originalResponse = null;
    }

    /**
     * We try to maintain a relation between the HTML tag and its Wicket component.
     */
    private static class PathSetupListener implements IComponentOnBeforeRenderListener {
        public static final PathSetupListener INSTANCE = new PathSetupListener();

        public void onBeforeRender(Component component) {
            component.add(OutputPathBehavior.INSTANCE);
        }
    }

    /**
     * Set the page relative path in tag.
     */
    public static class OutputPathBehavior extends Behavior {
        /** */
        private static final long serialVersionUID = 1L;
        public static final String PATH_ATTRIBUTE = "component_path";
        public static final OutputPathBehavior INSTANCE = new OutputPathBehavior();
        private boolean removing;

        @Override
        public void onComponentTag(Component component, ComponentTag tag) {
            tag.put(PATH_ATTRIBUTE, component.getPageRelativePath());
        }

        @Override
        public void detach(Component component) {
            // preventing endless recursion.
            if (removing) {
                return;
            }
            removing = true;
            component.remove(this);
            removing = false;
        }
    }

    public Sheet getSheet() {
        return targetSheet;
    }
}