/*
* JBoss, Home of Professional Open Source.
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership. Some portions may be licensed
* to Red Hat, Inc. under one or more contributor license agreements.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA.
*/
package org.teiid.translator.excel;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Blob;
import java.sql.Clob;
import java.sql.SQLXML;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import javax.resource.ResourceException;
import org.apache.poi.hssf.usermodel.HSSFSheet;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.FormulaEvaluator;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.teiid.core.types.BlobImpl;
import org.teiid.core.types.BlobType;
import org.teiid.core.types.ClobImpl;
import org.teiid.core.types.ClobType;
import org.teiid.core.types.DataTypeManager;
import org.teiid.core.types.InputStreamFactory;
import org.teiid.core.types.SQLXMLImpl;
import org.teiid.core.types.TransformationException;
import org.teiid.core.types.XMLType;
import org.teiid.language.Select;
import org.teiid.metadata.RuntimeMetadata;
import org.teiid.translator.DataNotAvailableException;
import org.teiid.translator.ExecutionContext;
import org.teiid.translator.FileConnection;
import org.teiid.translator.ResultSetExecution;
import org.teiid.translator.TranslatorException;
public class ExcelExecution implements ResultSetExecution {
@SuppressWarnings("unused")
private ExecutionContext executionContext;
@SuppressWarnings("unused")
private RuntimeMetadata metadata;
private FileConnection connection;
// Execution state
private Iterator<Row> rowIterator;
private Row currentRow;
private File[] xlsFiles;
private AtomicInteger fileCount = new AtomicInteger();
private ExcelQueryVisitor visitor;
private FormulaEvaluator evaluator;
private FileInputStream xlsFileStream;
private Class<?>[] expectedColumnTypes;
public ExcelExecution(Select query, ExecutionContext executionContext,
RuntimeMetadata metadata, FileConnection connection)
throws TranslatorException {
this.executionContext = executionContext;
this.metadata = metadata;
this.connection = connection;
this.expectedColumnTypes = query.getColumnTypes();
this.visitor = new ExcelQueryVisitor();
this.visitor.visitNode(query);
if (!visitor.exceptions.isEmpty()) {
throw visitor.exceptions.get(0);
}
}
@Override
public void execute() throws TranslatorException {
try {
this.xlsFiles = FileConnection.Util.getFiles(this.visitor.getXlsPath(), this.connection, true);
this.rowIterator = readXLSFile(xlsFiles[fileCount.getAndIncrement()]);
} catch (ResourceException e) {
throw new TranslatorException(e);
}
}
private Iterator<Row> readXLSFile(File xlsFile) throws TranslatorException {
try {
this.xlsFileStream = new FileInputStream(xlsFile);
Iterator<Row> rowIter = null;
String extension = ExcelMetadataProcessor.getFileExtension(xlsFile);
if (extension.equalsIgnoreCase("xls")) { //$NON-NLS-1$
HSSFWorkbook workbook = new HSSFWorkbook(this.xlsFileStream);
HSSFSheet sheet = workbook.getSheet(this.visitor.getSheetName());
this.evaluator = workbook.getCreationHelper().createFormulaEvaluator();
rowIter = sheet.iterator();
}
else if (extension.equalsIgnoreCase("xlsx")) { //$NON-NLS-1$
XSSFWorkbook workbook = new XSSFWorkbook(this.xlsFileStream);
XSSFSheet sheet = workbook.getSheet(this.visitor.getSheetName());
this.evaluator = workbook.getCreationHelper().createFormulaEvaluator();
rowIter = sheet.iterator();
}
else {
throw new TranslatorException(ExcelPlugin.Event.TEIID23000, ExcelPlugin.Util.gs(ExcelPlugin.Event.TEIID23000));
}
// skip up to the first data row
if (this.visitor.getFirstDataRowNumber() > 0 && rowIter != null) {
while(rowIter.hasNext()) {
this.currentRow = rowIter.next();
if (this.currentRow.getRowNum() >= this.visitor.getFirstDataRowNumber()) {
break;
}
}
}
return rowIter;
} catch (IOException e) {
throw new TranslatorException(e);
}
}
@Override
public List<?> next() throws TranslatorException, DataNotAvailableException {
while (hasNext()) {
Row row = nextRow();
// when the first cell number is -1, then it is empty row, skip it
if (row.getFirstCellNum() == -1) {
continue;
}
if (!this.visitor.allows(row.getRowNum())) {
continue;
}
return projectRow(row);
}
return null;
}
private boolean hasNext() throws TranslatorException {
if (this.currentRow != null) {
return true;
}
boolean hasNext = false;
if (this.rowIterator != null) {
hasNext = this.rowIterator.hasNext();
}
if (!hasNext) {
this.rowIterator = null;
File nextXlsFile = getNextXLSFile();
if (nextXlsFile != null) {
this.rowIterator = readXLSFile(nextXlsFile);
hasNext = this.rowIterator.hasNext();
}
}
return hasNext;
}
private File getNextXLSFile() {
if (this.xlsFiles.length > this.fileCount.get()) {
try {
this.xlsFileStream.close();
} catch (IOException e) {
// ignore
}
return this.xlsFiles[this.fileCount.getAndIncrement()];
}
return null;
}
private Row nextRow() {
if (this.currentRow != null) {
Row row = this.currentRow;
this.currentRow = null;
return row;
}
Row row = null;
if (this.rowIterator != null && this.rowIterator.hasNext()) {
row = this.rowIterator.next();
}
return row;
}
/**
* @param row
* @param neededColumns
*/
List<Object> projectRow(Row row) throws TranslatorException {
ArrayList output = new ArrayList();
int id = row.getRowNum()+1;
int i = -1;
for (int index:this.visitor.getProjectedColumns()) {
i++;
// check if the row is ROW_ID
if (index == -1) {
output.add(id);
continue;
}
Cell cell = row.getCell(index-1, Row.RETURN_BLANK_AS_NULL);
if (cell == null) {
output.add(null);
continue;
}
switch (this.evaluator.evaluateInCell(cell).getCellType()) {
case Cell.CELL_TYPE_NUMERIC:
output.add(convertFromExcelType(cell.getNumericCellValue(), cell, this.expectedColumnTypes[i]));
break;
case Cell.CELL_TYPE_STRING:
output.add(convertFromExcelType(cell.getStringCellValue(), this.expectedColumnTypes[i]));
break;
case Cell.CELL_TYPE_BOOLEAN:
if (this.expectedColumnTypes[i].isAssignableFrom(Boolean.class)) {
output.add(Boolean.valueOf(cell.getBooleanCellValue()));
}
else {
throw new TranslatorException(ExcelPlugin.Event.TEIID23001, ExcelPlugin.Util.gs(ExcelPlugin.Event.TEIID23001, this.expectedColumnTypes[i].getName()));
}
break;
default:
output.add(null);
break;
}
}
return output;
}
static Object convertFromExcelType(final Double value, Cell cell, final Class<?> expectedType) throws TranslatorException {
if (value == null) {
return null;
}
if (expectedType.isAssignableFrom(Double.class)) {
return value;
}
else if (expectedType.isAssignableFrom(Timestamp.class)) {
Date date = cell.getDateCellValue();
return new Timestamp(date.getTime());
}
else if (expectedType.isAssignableFrom(java.sql.Date.class)) {
Date date = cell.getDateCellValue();
return new java.sql.Date(date.getTime());
}
else if (expectedType.isAssignableFrom(java.sql.Time.class)) {
Date date = cell.getDateCellValue();
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
StringBuilder sb = new StringBuilder();
sb.append(calendar.get(Calendar.HOUR_OF_DAY))
.append(":") //$NON-NLS-1$
.append(calendar.get(Calendar.MINUTE))
.append(":") //$NON-NLS-1$
.append(calendar.get(Calendar.SECOND));
return java.sql.Time.valueOf(sb.toString());
}
if (DataTypeManager.isTransformable(double.class, expectedType)) {
try {
return DataTypeManager.transformValue(value, expectedType);
} catch (TransformationException e) {
throw new TranslatorException(e);
}
}
throw new TranslatorException(ExcelPlugin.Event.TEIID23002, ExcelPlugin.Util.gs(ExcelPlugin.Event.TEIID23002, expectedType.getName()));
}
static Object convertFromExcelType(final Boolean value, final Class<?> expectedType) throws TranslatorException {
if (value == null) {
return null;
}
if (expectedType.isAssignableFrom(Boolean.class)) {
return value;
}
throw new TranslatorException(ExcelPlugin.Event.TEIID23001, ExcelPlugin.Util.gs(ExcelPlugin.Event.TEIID23001, expectedType.getName()));
}
static Object convertFromExcelType(final String value, final Class<?> expectedType) throws TranslatorException {
if (value == null) {
return null;
}
if (expectedType.isAssignableFrom(String.class)) {
return value;
}
if (expectedType.isAssignableFrom(Blob.class)) {
return new BlobType(new BlobImpl(new InputStreamFactory() {
@Override
public InputStream getInputStream() throws IOException {
return new ByteArrayInputStream(value.getBytes());
}
}));
} else if (expectedType.isAssignableFrom(Clob.class)) {
return new ClobType(new ClobImpl(value));
} else if (expectedType.isAssignableFrom(SQLXML.class)) {
return new XMLType(new SQLXMLImpl(value.getBytes()));
} else if (DataTypeManager.isTransformable(String.class, expectedType)) {
try {
return DataTypeManager.transformValue(value, expectedType);
} catch (TransformationException e) {
throw new TranslatorException(e);
}
} else {
throw new TranslatorException(ExcelPlugin.Event.TEIID23003, ExcelPlugin.Util.gs(ExcelPlugin.Event.TEIID23003, expectedType.getName()));
}
}
@Override
public void close() {
if (this.xlsFileStream != null) {
try {
this.xlsFileStream.close();
} catch (IOException e) {
}
}
}
@Override
public void cancel() throws TranslatorException {
}
}