/* * 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 { } }