package jeql.command.io;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import jeql.api.error.JeqlException;
import jeql.api.row.BasicRow;
import jeql.api.row.Row;
import jeql.api.row.RowIterator;
import jeql.api.row.RowList;
import jeql.api.row.RowSchema;
/**
* A {@link RowList} for Simple Table Format files
* @author Martin Davis
*
*/
public class STFRowList
implements RowList
{
private static String parseColName(String line)
{
int sepPos = line.indexOf('>');
if (sepPos == -1)
throw new IllegalArgumentException("Invalid column name format in line: "
+ line);
String colName = line.substring(1, sepPos).trim();
return colName;
}
private static String parseColValue(String line)
{
int sepPos = line.indexOf('>');
if (sepPos == -1)
return "";
String val = line.substring(sepPos + 1).trim();
return val;
}
private String filename;
private RowSchema schema;
public STFRowList(String filename)
throws IOException
{
this.filename = filename;
schema = readSchema(filename);
}
public RowSchema getSchema()
{
return schema;
}
public RowIterator iterator()
{
return new STFRowIterator(schema, filename);
}
private RowSchema readSchema(String filename)
throws IOException
{
SchemaExtracter se = new SchemaExtracter(filename);
return se.getSchema();
}
//=====================================================
private static class SchemaExtracter
{
private String filename = null;
private LineNumberReader lineReader = null;
private List colNames = new ArrayList();
public SchemaExtracter(String filename) {
this.filename = filename;
}
public RowSchema getSchema()
throws IOException
{
try {
lineReader = new LineNumberReader(new FileReader(filename));
readCols();
}
finally {
if (lineReader != null)
lineReader.close();
}
return buildSchema(colNames);
}
private RowSchema buildSchema(List colNames)
{
String[] names = new String[colNames.size()];
Class[] types = new Class[colNames.size()];
int index = 0;
for (Iterator i = colNames.iterator(); i.hasNext(); ) {
String name = (String) i.next();
names[index] = name;
types[index] = String.class;
index++;
}
RowSchema schema = new RowSchema(names, types);
return schema;
}
private void readCols()
throws IOException
{
RowLinesReader linesReader = new RowLinesReader(lineReader);
List lines = linesReader.readRow();
for (int i = 0; i < lines.size(); i++) {
// extract a column name
colNames.add(parseColName((String) lines.get(i)));
//TODO: check for duplicate col names and throw error if found (prevents very long rows)
}
}
}
/**
* Reads a block of lines from a STF file which comprise a row.
*
* @author Martin Davis
*
*/
private static class RowLinesReader
{
private LineNumberReader rdr;
public RowLinesReader(LineNumberReader rdr)
{
this.rdr = rdr;
}
public List readRow()
throws IOException
{
List lines = new ArrayList();
readRow(lines);
return lines;
}
/**
* Reads lines from file until maximum is exceeded,
* or blank line or EOF is found.
*
* @param maxLines
* @return an array containing the lines read
* @return null if no lines were read (i.e. EOF)
*/
public void readRow(List lines)
throws IOException
{
int numLinesRead = 0;
while (true) {
String line = readLine();
// EOF
if (line == null) break;
// blank line = end of row
if (line.trim().length() == 0) break;
lines.add(line);
}
}
private String readLine()
throws IOException
{
String lineBuffer = rdr.readLine();
// if at end, can close input
return lineBuffer;
}
}
// =============================================
private static class STFRowIterator
implements RowIterator
{
private String filename;
private RowSchema schema;
private LineNumberReader lineReader = null;
private RowLinesReader rowReader = null;
private List lines = new ArrayList();
private String lineBuffer = null;
private boolean isClosed = false;
public STFRowIterator(RowSchema schema, String filename)
{
this.schema = schema;
this.filename = filename;
init();
}
public RowSchema getSchema()
{
return schema;
}
private void init()
{
if (isClosed) return;
if (lineReader != null) return;
try {
lineReader = new LineNumberReader(new FileReader(filename));
rowReader = new RowLinesReader(lineReader);
}
catch (FileNotFoundException ex) {
// convert to unchecked
throw new JeqlException(ex);
}
}
public Row next()
{
// init in case not already started
init();
lines.clear();
try {
rowReader.readRow(lines);
}
catch (IOException ex) {
// convert to unchecked
throw new JeqlException(ex);
}
// check if at EOF
if (lines.size() == 0) {
close();
return null;
}
return createRow(lines);
}
private void close()
{
if (lineReader != null) {
try {
lineReader.close();
}
catch (IOException ex) {
// eat this exception - nothing we can do about it anyway
}
}
lineReader = null;
isClosed = true;
}
private Row createRow(List lines)
{
BasicRow row = new BasicRow(schema.size());
for (int i = 0; i < lines.size(); i++) {
String line = (String) lines.get(i);
String col = parseColName(line);
String val = parseColValue(line);
int colIndex = schema.getColIndex(col);
row.setValue(colIndex, val);
}
return row;
}
}
}