/*
* Copyright 2004-2015 the Seasar Foundation and the Others.
*
* 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.seasar.extension.dataset.impl;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import org.apache.poi.hssf.record.RowRecord;
import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.usermodel.HSSFDataFormat;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.seasar.extension.dataset.ColumnType;
import org.seasar.extension.dataset.DataReader;
import org.seasar.extension.dataset.DataRow;
import org.seasar.extension.dataset.DataSet;
import org.seasar.extension.dataset.DataSetConstants;
import org.seasar.extension.dataset.DataTable;
import org.seasar.extension.dataset.types.ColumnTypes;
import org.seasar.framework.exception.IORuntimeException;
import org.seasar.framework.util.Base64Util;
import org.seasar.framework.util.FileInputStreamUtil;
import org.seasar.framework.util.ResourceUtil;
import org.seasar.framework.util.StringUtil;
import org.seasar.framework.util.TimestampConversionUtil;
/**
* Excel用の {@link DataReader}です。
*
* @author higa
* @author manhole
* @author azusa
*/
public class XlsReader implements DataReader, DataSetConstants {
/**
* データセットです。
*/
protected DataSet dataSet;
/**
* ワークブックです。
*/
protected HSSFWorkbook workbook;
/**
* データフォーマットです。
*/
protected HSSFDataFormat dataFormat;
/**
* 文字列をトリミングするかどうか
*/
protected boolean trimString = true;
/**
* {@link XlsReader}を作成します。
*
* @param path
* パス
*/
public XlsReader(String path) {
this(path, true);
}
/**
* {@link XlsReader}を作成します。
*
* @param path
* パス
* @param trimString
* 文字列をトリムするかどうか
*/
public XlsReader(String path, boolean trimString) {
this(ResourceUtil.getResourceAsStream(path), trimString);
}
/**
* {@link XlsReader}を作成します。
*
* @param dirName
* ディレクトリ名
* @param fileName
* ファイル名
*/
public XlsReader(String dirName, String fileName) {
this(dirName, fileName, true);
}
/**
* {@link XlsReader}を作成します。
*
* @param dirName
* ディレクトリ名
* @param fileName
* ファイル名
* @param trimString
* 文字列をトリムするかどうか
*/
public XlsReader(String dirName, String fileName, boolean trimString) {
this(ResourceUtil.getResourceAsFile(dirName), fileName, trimString);
}
/**
* {@link XlsReader}を作成します。
*
* @param dir
* ディレクトリ
* @param fileName
* ファイル名
*/
public XlsReader(File dir, String fileName) {
this(dir, fileName, true);
}
/**
* {@link XlsReader}を作成します。
*
* @param dir
* ディレクトリ
* @param fileName
* ファイル名
* @param trimString
* 文字列をトリムするかどうか
*/
public XlsReader(File dir, String fileName, boolean trimString) {
this(new File(dir, fileName), trimString);
}
/**
* {@link XlsReader}を作成します。
*
* @param file
* ファイル
*/
public XlsReader(File file) {
this(file, true);
}
/**
* {@link XlsReader}を作成します。
*
* @param file
* ファイル
* @param trimString
* 文字列をトリムするかどうか
*/
public XlsReader(File file, boolean trimString) {
this(FileInputStreamUtil.create(file), trimString);
}
/**
* {@link XlsReader}を作成します。
*
* @param in
* 入力ストリーム
*/
public XlsReader(InputStream in) {
this(in, true);
}
/**
* {@link XlsReader}を作成します。
*
* @param in
* 入力ストリーム
* @param trimString
* 文字列をトリムするかどうか
*/
public XlsReader(InputStream in, boolean trimString) {
this.trimString = trimString;
try {
workbook = new HSSFWorkbook(in);
} catch (IOException ex) {
throw new IORuntimeException(ex);
}
dataFormat = workbook.createDataFormat();
dataSet = new DataSetImpl();
for (int i = 0; i < workbook.getNumberOfSheets(); ++i) {
createTable(workbook.getSheetName(i), workbook.getSheetAt(i));
}
}
public DataSet read() {
return dataSet;
}
/**
* テーブルを作成します。
*
* @param sheetName
* シート名
* @param sheet
* シート
* @return テーブル
*/
protected DataTable createTable(String sheetName, HSSFSheet sheet) {
DataTable table = dataSet.addTable(sheetName);
int rowCount = sheet.getLastRowNum();
if (rowCount > 0) {
setupColumns(table, sheet);
setupRows(table, sheet);
} else if (rowCount == 0) {
setupColumns(table, sheet);
}
return table;
}
/**
* カラムの情報をセットアップします。
*
* @param table
* テーブル
* @param sheet
* シート
*/
protected void setupColumns(DataTable table, HSSFSheet sheet) {
HSSFRow nameRow = sheet.getRow(0);
HSSFRow valueRow = sheet.getRow(1);
for (int i = 0; i <= Short.MAX_VALUE; ++i) {
HSSFCell nameCell = nameRow.getCell((short) i);
if (nameCell == null) {
break;
}
String columnName = nameCell.getRichStringCellValue().getString();
if (columnName.length() == 0) {
break;
}
HSSFCell valueCell = null;
if (valueRow != null) {
for (int j = 1; j <= sheet.getLastRowNum(); j++) {
valueCell = sheet.getRow(j).getCell((short) i);
if (valueCell != null
&& !StringUtil.isEmpty(valueCell.toString())) {
break;
}
}
}
if (valueCell != null) {
table.addColumn(columnName, getColumnType(valueCell));
} else {
table.addColumn(columnName);
}
}
}
/**
* シートの行をセットアップします。
*
* @param table
* テーブル
* @param sheet
* シート
*/
protected void setupRows(DataTable table, HSSFSheet sheet) {
for (int i = 1; i <= RowRecord.MAX_ROW_NUMBER; ++i) {
HSSFRow row = sheet.getRow(i);
if (row == null) {
break;
}
setupRow(table, row);
}
}
/**
* 行をセットアップします。
*
* @param table
* テーブル
* @param row
* 行
*/
protected void setupRow(DataTable table, HSSFRow row) {
DataRow dataRow = table.addRow();
for (int i = 0; i < table.getColumnSize(); ++i) {
HSSFCell cell = row.getCell((short) i);
Object value = getValue(cell);
dataRow.setValue(i, value);
}
}
/**
* セルがBase64でフォーマットされているかどうかを返します。
*
* @param cell
* セル
* @return セルがBase64でフォーマットされているかどうか
*/
public boolean isCellBase64Formatted(HSSFCell cell) {
HSSFCellStyle cs = cell.getCellStyle();
short dfNum = cs.getDataFormat();
return BASE64_FORMAT.equals(dataFormat.getFormat(dfNum));
}
/**
* セルが日付のフォーマットかどうかを返します。
*
* @param cell
* セル
* @return セルが日付のフォーマットかどうか
*/
public boolean isCellDateFormatted(HSSFCell cell) {
HSSFCellStyle cs = cell.getCellStyle();
short dfNum = cs.getDataFormat();
String format = dataFormat.getFormat(dfNum);
if (StringUtil.isEmpty(format)) {
return false;
}
if (format.indexOf('/') > 0 || format.indexOf('y') > 0
|| format.indexOf('m') > 0 || format.indexOf('d') > 0) {
return true;
}
return false;
}
/**
* セルの値を返します。
*
* @param cell
* セル
* @return セルの値
*/
public Object getValue(HSSFCell cell) {
if (cell == null) {
return null;
}
switch (cell.getCellType()) {
case HSSFCell.CELL_TYPE_NUMERIC:
if (isCellDateFormatted(cell)) {
return TimestampConversionUtil.toTimestamp(cell
.getDateCellValue());
}
final double numericCellValue = cell.getNumericCellValue();
if (isInt(numericCellValue)) {
return new BigDecimal((int) numericCellValue);
}
return new BigDecimal(Double.toString(numericCellValue));
case HSSFCell.CELL_TYPE_STRING:
String s = cell.getRichStringCellValue().getString();
if (s != null) {
s = StringUtil.rtrim(s);
if (!trimString && s.length() > 1 && s.startsWith("\"")
&& s.endsWith("\"")) {
s = s.substring(1, s.length() - 1);
}
}
if ("".equals(s)) {
s = null;
}
if (isCellBase64Formatted(cell)) {
return Base64Util.decode(s);
}
return s;
case HSSFCell.CELL_TYPE_BOOLEAN:
boolean b = cell.getBooleanCellValue();
return Boolean.valueOf(b);
default:
return null;
}
}
/**
* カラムの型を返します。
*
* @param cell
* セル
* @return カラムの型
*/
protected ColumnType getColumnType(HSSFCell cell) {
switch (cell.getCellType()) {
case HSSFCell.CELL_TYPE_NUMERIC:
if (isCellDateFormatted(cell)) {
return ColumnTypes.TIMESTAMP;
}
return ColumnTypes.BIGDECIMAL;
case HSSFCell.CELL_TYPE_BOOLEAN:
return ColumnTypes.BOOLEAN;
case HSSFCell.CELL_TYPE_STRING:
if (isCellBase64Formatted(cell)) {
return ColumnTypes.BINARY;
} else if (trimString) {
return ColumnTypes.STRING;
} else {
return ColumnTypes.NOT_TRIM_STRING;
}
default:
return ColumnTypes.STRING;
}
}
/**
* 整数かどうかを返します。
*
* @param numericCellValue
* numericな値
* @return 整数かどうか
*/
protected boolean isInt(final double numericCellValue) {
return ((int) numericCellValue) == numericCellValue;
}
}