package org.reldb.rel.v0.storage.relvars.external.xls;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Iterator;
import java.util.NoSuchElementException;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.reldb.rel.exceptions.ExceptionSemantic;
import org.reldb.rel.v0.generator.Generator;
import org.reldb.rel.v0.storage.RelDatabase;
import org.reldb.rel.v0.storage.relvars.RelvarCustomMetadata;
import org.reldb.rel.v0.storage.relvars.RelvarExternal;
import org.reldb.rel.v0.storage.relvars.RelvarGlobal;
import org.reldb.rel.v0.storage.relvars.RelvarHeading;
import org.reldb.rel.v0.storage.relvars.external.CSVLineParse;
import org.reldb.rel.v0.storage.relvars.external.ColumnName;
import org.reldb.rel.v0.storage.tables.TableExternal.DuplicateHandling;
import org.reldb.rel.v0.types.Heading;
import org.reldb.rel.v0.types.builtin.TypeCharacter;
import org.reldb.rel.v0.types.builtin.TypeInteger;
public class RelvarXLSMetadata extends RelvarCustomMetadata {
public static final long serialVersionUID = 0;
private String path;
private DuplicateHandling duplicates;
public static class SheetSpec {
String filePath;
int sheetIndex = 0;
boolean hasHeading = true;
}
public static SheetSpec obtainSheetSpec(String path) {
String[] splitPath = CSVLineParse.parseTrimmed(path);
SheetSpec spec = new SheetSpec();
spec.filePath = splitPath[0].trim();
for (int index = 1; index < splitPath.length; index++) {
String part = splitPath[index].trim();
if (part.compareToIgnoreCase("NOHEADING") == 0)
spec.hasHeading = false;
else
try {
spec.sheetIndex = Integer.parseInt(part);
} catch (NumberFormatException nfe) {
throw new ExceptionSemantic("RS0465: Invalid spreadsheet specification '" + part + "'.");
}
}
return spec;
}
private static RelvarHeading buildHeadingFromColumnsInFirstRow(DuplicateHandling duplicates, boolean hasHeadingRow, Iterator<Row> rowIterator) {
Heading heading = new Heading();
if (duplicates == DuplicateHandling.DUP_COUNT)
heading.add("_DUP_COUNT", TypeInteger.getInstance());
else if (duplicates == DuplicateHandling.AUTOKEY)
heading.add("_AUTOKEY", TypeInteger.getInstance());
Row row;
try {
row = rowIterator.next();
} catch (NoSuchElementException nsee) {
return new RelvarHeading(heading);
}
Iterator<Cell> cellIterator = row.cellIterator();
int blankCount = 0;
while (cellIterator.hasNext()) {
Cell cell = cellIterator.next();
String columnName;
if (hasHeadingRow) {
columnName = ColumnName.cleanName(cell.toString());
if (columnName.length() == 0)
columnName = "BLANK" + ++blankCount;
} else
columnName = "COLUMN" + ++blankCount;
heading.add(ColumnName.cleanName(columnName), TypeCharacter.getInstance());
}
return new RelvarHeading(heading);
}
public static RelvarHeading getHeadingFromXLS(String path, DuplicateHandling duplicates) {
SheetSpec spec = obtainSheetSpec(path);
File f = new File(spec.filePath);
if (!f.exists())
throw new ExceptionSemantic("RS0461: File " + path + " doesn't exist.");
try (FileInputStream reader = new FileInputStream(f)) {
if (spec.filePath.toLowerCase().endsWith("xls")) {
try (HSSFWorkbook workbook = new HSSFWorkbook(reader)) {
return buildHeadingFromColumnsInFirstRow(duplicates, spec.hasHeading, workbook.getSheetAt(spec.sheetIndex).iterator());
}
} else if (spec.filePath.toLowerCase().endsWith("xlsx")) {
try (XSSFWorkbook workbook = new XSSFWorkbook(reader)) {
return buildHeadingFromColumnsInFirstRow(duplicates, spec.hasHeading, workbook.getSheetAt(spec.sheetIndex).iterator());
}
} else {
throw new ExceptionSemantic("RS0462: Unrecognised file type. It should be .XLS or .XLSX.");
}
} catch (IOException ioe) {
throw new ExceptionSemantic("RS0463: Unable to read " + path + " due to " + ioe);
}
}
public RelvarXLSMetadata(RelDatabase database, String owner, String path, DuplicateHandling duplicates) {
super(database, getHeadingFromXLS(path, duplicates), owner);
this.path = path;
this.duplicates = duplicates;
}
@Override
public String getSourceDefinition() {
return "EXTERNAL XLS \"" + path.replace('\\', '/') + "\" " + duplicates;
}
@Override
public RelvarGlobal getRelvar(String name, RelDatabase database) {
SheetSpec spec = obtainSheetSpec(path);
File file = new File(spec.filePath);
if (!file.exists())
throw new ExceptionSemantic("EX0027: File at " + path + " not found");
return new RelvarExternal(name, database, new Generator(database, System.out), this, duplicates);
}
@Override
public void dropRelvar(RelDatabase database) {
}
public String getPath() {
return path;
}
@Override
public String tableClassName() {
return "TableXLS";
}
@Override
public String getType() {
return "XLS";
}
}