package org.reldb.rel.v0.storage.relvars.external.xls; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; 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.DataFormatter; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.xssf.usermodel.XSSFSheet; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.reldb.rel.exceptions.ExceptionSemantic; import org.reldb.rel.v0.generator.Generator; import org.reldb.rel.v0.storage.relvars.RelvarExternal; import org.reldb.rel.v0.storage.relvars.RelvarExternalMetadata; import org.reldb.rel.v0.storage.relvars.RelvarHeading; import org.reldb.rel.v0.storage.relvars.external.xls.RelvarXLSMetadata.SheetSpec; import org.reldb.rel.v0.storage.tables.TableCustom; import org.reldb.rel.v0.types.Heading; import org.reldb.rel.v0.values.RelTupleFilter; import org.reldb.rel.v0.values.RelTupleMap; import org.reldb.rel.v0.values.TupleFilter; import org.reldb.rel.v0.values.TupleIterator; import org.reldb.rel.v0.values.TupleIteratorAutokey; import org.reldb.rel.v0.values.TupleIteratorCount; import org.reldb.rel.v0.values.TupleIteratorUnique; import org.reldb.rel.v0.values.Value; import org.reldb.rel.v0.values.ValueCharacter; import org.reldb.rel.v0.values.ValueRelation; import org.reldb.rel.v0.values.ValueTuple; import org.reldb.rel.v0.vm.Context; public class TableXLS extends TableCustom { private File file; private DuplicateHandling duplicates; private Generator generator; private Heading fileHeading; private int sheetIndex = 0; private boolean hasHeading = true; public TableXLS(String Name, RelvarExternalMetadata metadata, Generator generator, DuplicateHandling duplicates) { this.generator = generator; this.duplicates = duplicates; RelvarXLSMetadata meta = (RelvarXLSMetadata) metadata; SheetSpec spec = RelvarXLSMetadata.obtainSheetSpec(meta.getPath()); file = new File(spec.filePath); sheetIndex = spec.sheetIndex; hasHeading = spec.hasHeading; RelvarHeading heading = meta.getHeadingDefinition(generator.getDatabase()); Heading storedHeading = heading.getHeading(); fileHeading = RelvarXLSMetadata.getHeadingFromXLS(meta.getPath(), duplicates).getHeading(); if (storedHeading.toString().compareTo(fileHeading.toString()) != 0) throw new ExceptionSemantic("RS0464: Stored XLS metadata is " + storedHeading + " but file metadata is " + fileHeading + ". Has the file structure changed?"); } private ValueTuple toTuple(Iterator<Cell> cellIterator) { Value[] values = new Value[fileHeading.getDegree() - ((duplicates == DuplicateHandling.DUP_COUNT || duplicates == DuplicateHandling.AUTOKEY) ? 1 : 0)]; int index = 0; DataFormatter formatter = new DataFormatter(); while (cellIterator.hasNext() && index < values.length) { Cell cell = cellIterator.next(); values[index] = ValueCharacter.select(generator, formatter.formatCellValue(cell)); index++; } for (; index < values.length; index++) values[index] = ValueCharacter.select(generator, ""); return new ValueTuple(generator, values); } @Override public TupleIterator iterator() { try { if (file.getName().toLowerCase().endsWith("xls")) { switch (duplicates) { case DUP_REMOVE: return new TupleIteratorUnique(iteratorRawXLS()); case DUP_COUNT: return new TupleIteratorUnique(new TupleIteratorCount(iteratorRawXLS(), generator)); case AUTOKEY: return new TupleIteratorAutokey(iteratorRawXLS(), generator); default: throw new ExceptionSemantic("RS0467: Duplicate handling method " + duplicates.toString() + " is not supported by XLS."); } } else if (file.getName().toLowerCase().endsWith("xlsx")) { switch (duplicates) { case DUP_REMOVE: return new TupleIteratorUnique(iteratorRawXLSX()); case DUP_COUNT: return new TupleIteratorUnique(new TupleIteratorCount(iteratorRawXLSX(), generator)); case AUTOKEY: return new TupleIteratorAutokey(iteratorRawXLSX(), generator); default: throw new ExceptionSemantic("RS0468: Duplicate handling method " + duplicates.toString() + " is not supported by XLS."); } } else throw new ExceptionSemantic("EX0028: Type should be XLS or XLSX."); } catch (IOException e) { throw new ExceptionSemantic("EX0029: Failed to read the file." + e); } } @Override public TupleIterator iterator(Generator generator) { return iterator(); } @Override public long getCardinality() { long count = 0; TupleIterator iterator = iterator(); try { while (iterator.hasNext()) { count++; iterator.next(); } } finally { iterator.close(); } return count; } private static void notImplemented(String what) { throw new ExceptionSemantic("EX0030: XLS relvars do not yet support " + what); } @Override public boolean contains(Generator generator, ValueTuple tuple) { TupleIterator iterator = iterator(); try { while (iterator.hasNext()) if (tuple.equals(iterator.next())) return true; } finally { iterator.close(); } return false; } @Override public ValueTuple getTupleForKey(Generator generator, ValueTuple tuple) { return null; } @Override public void setValue(RelvarExternal relvarXLS, ValueRelation relation) { notImplemented("assignment"); } @Override public long insert(Generator generator, ValueRelation relation) { long count = 0; TupleIterator iterator = relation.iterator(); while (iterator.hasNext()) count += insert(generator, iterator.next()); return count; } @Override public long insert(Generator generator, ValueTuple tuple) { notImplemented("INSERT"); return 0; } @Override public long insertNoDuplicates(Generator generator, ValueRelation relation) { long count = 0; TupleIterator iterator = relation.iterator(); while (iterator.hasNext()) { ValueTuple tuple = iterator.next(); if (!contains(generator, tuple)) count += insert(generator, tuple); } return count; } @Override public void purge() { notImplemented("DELETE"); } @Override public void delete(Generator generator, ValueTuple tuple) { notImplemented("DELETE"); } @Override public long delete(Generator generator, RelTupleFilter relTupleFilter) { long count = 0; TupleIterator iterator = this.iterator(); ValueTuple tuple; List<ValueTuple> tuplesToDelete = new ArrayList<ValueTuple>(); while (iterator.hasNext()) { tuple = iterator.next(); if (relTupleFilter.filter(tuple)) tuplesToDelete.add(tuple); } for (ValueTuple tuples : tuplesToDelete) { delete(generator, tuples); count++; } return count; } @Override public long delete(Generator generator, TupleFilter filter) { long count = 0; TupleIterator iterator = this.iterator(); ValueTuple tuple; List<ValueTuple> tuplesToDelete = new ArrayList<ValueTuple>(); while (iterator.hasNext()) { tuple = iterator.next(); if (filter.filter(tuple)) tuplesToDelete.add(tuple); } for (ValueTuple tuples : tuplesToDelete) { delete(generator, tuples); count++; } return count; } @Override public long delete(Context context, ValueRelation tuplesToDelete, boolean errorIfNotIncluded) { long count = 0; TupleIterator iterator = tuplesToDelete.iterator(); while (iterator.hasNext()) { delete(generator, iterator.next()); count++; } return count; } @Override public long update(Generator generator, RelTupleMap relTupleMap) { notImplemented("UPDATE"); return 0; } @Override public long update(Generator generator, RelTupleFilter relTupleFilter, RelTupleMap relTupleMap) { notImplemented("UPDATE"); return 0; } private Row obtainFirstRow(Iterator<Row> rowIterator) { try { return (hasHeading) ? rowIterator.next() : null; // skip heading row? } catch (NoSuchElementException nsee) { return null; } } private abstract class SpreadsheetTupleIterator extends TupleIterator { private Iterator<Row> rowIterator; private Row row; public SpreadsheetTupleIterator(Iterator<Row> rowIterator) { this.rowIterator = rowIterator; row = obtainFirstRow(rowIterator); } @Override public boolean hasNext() { return rowIterator.hasNext(); } @Override public ValueTuple next() { row = rowIterator.next(); Iterator<Cell> cellIterator = row.cellIterator(); return toTuple(cellIterator); } } private TupleIterator iteratorRawXLS() throws IOException { FileInputStream reader = new FileInputStream(file); HSSFWorkbook workbook = new HSSFWorkbook(reader); HSSFSheet sheet = workbook.getSheetAt(sheetIndex); return new SpreadsheetTupleIterator(sheet.iterator()) { @Override public void close() { try { workbook.close(); } catch (IOException e1) { } try { reader.close(); } catch (IOException e) { } } }; } private TupleIterator iteratorRawXLSX() throws IOException { FileInputStream reader = new FileInputStream(file); XSSFWorkbook workbook = new XSSFWorkbook(reader); XSSFSheet sheet = workbook.getSheetAt(sheetIndex); return new SpreadsheetTupleIterator(sheet.iterator()) { @Override public void close() { try { workbook.close(); } catch (IOException e1) { } try { reader.close(); } catch (IOException e) { } } }; } }