
org.wicketstuff.poi.excel.TableParser Maven / Gradle / Ivy
/*
* 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 rowsToSpanByColumn = new HashMap();
private final Map columnSpan = new HashMap();
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;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy