package dbfit.fixture;
import fit.*;
import java.sql.*;
import java.util.*;
import dbfit.util.*;
public abstract class RowSetFixture extends ColumnFixture {
private MatchableDataTable dt;
private DataRow currentRow;
// if element not 0, fixture column -> result set column index
private String[] keyColumns;
protected abstract MatchableDataTable getDataTable() throws SQLException;
protected abstract boolean isOrdered();
private class CurrentDataRowTypeAdapter extends TypeAdapter {
public String key;
@SuppressWarnings("unchecked")
public CurrentDataRowTypeAdapter(String key, Class type) throws NoSuchMethodException {
this.fixture = RowSetFixture.this;
target = null;
method = CurrentDataRowTypeAdapter.class.getMethod("get", new Class[] {});
this.type = type;
this.key = key;
}
@Override
public void set(Object value) throws Exception {
throw new UnsupportedOperationException("changing values in row sets is not supported");
}
@Override
public Object get() {
return currentRow.get(key);
}
@Override
public Object invoke() throws IllegalAccessException {
return get();
}
@Override
public Object parse(String s) throws Exception {
return new ParseHelper(this.fixture, this.type).parse(s);
}
}
private int findColumn(String name) throws Exception {
//todo: implement non-key
String normalisedName = NameNormaliser.normaliseName(name);
for (int i = 0; i < dt.getColumns().size(); i++) {
String colName = dt.getColumns().get(i).getName();
if (normalisedName.equals(NameNormaliser.normaliseName(colName))) {
return i;
}
}
throw new Exception("Unknown column " + normalisedName);
}
@Override
protected void bindColumnHeadersToMethodsAndFields(Parse heads) {
try {
columnBindings = new Binding[heads.size()];
keyColumns = new String[heads.size()];
for (int i = 0; heads != null; i++, heads = heads.more) {
String name = heads.text();
columnBindings[i] = new SymbolAccessQueryBinding();
int idx = findColumn(name);
String columnName = dt.getColumns().get(idx).getName();
if (!name.endsWith("?")) {
keyColumns[i] = columnName;
}
columnBindings[i].adapter = new CurrentDataRowTypeAdapter(
columnName,
getJavaClassForColumn(dt.getColumns().get(idx))
);
}
} catch (Exception sqle) {
exception(heads, sqle);
}
}
@Override
public void doRows(Parse rows) {
try {
dt = getDataTable();
super.doRows(rows);
addSurplusRows(rows.last());
} catch (SQLException sqle) {
sqle.printStackTrace();
exception(rows, sqle);
}
}
@Override
public void doRow(Parse row) {
try {
if (isOrdered()) {
currentRow = dt.findFirstUnprocessedRow();
} else {
currentRow = findMatchingRow(row);
}
super.doRow(row);
dt.markProcessed(currentRow);
} catch (NoMatchingRowFoundException e) {
row.parts.addToBody(Fixture.gray(" missing"));
wrong(row);
}
}
public DataRow findMatchingRow(Parse row) throws NoMatchingRowFoundException {
Parse columns = row.parts;
Map<String, Object> keyMap = new HashMap<String, Object>();
for (int i = 0; i < keyColumns.length; i++, columns = columns.more) {
if (keyColumns[i] != null) {
try {
Object value = columnBindings[i].adapter.parse(columns.text());
keyMap.put(keyColumns[i], value);
} catch (Exception e) {
exception(columns, e);
}
}
}
return dt.findMatching(keyMap);
}
private void addSurplusRows(Parse rows) {
Parse lastRow = rows;
for (DataRow dr: dt.getUnprocessedRows()) {
Parse newRow = new Parse("tr", null, null, null);
lastRow.more = newRow;
lastRow = newRow;
try {
currentRow = dr; // for getting
Parse firstCell = new Parse("td",
String.valueOf(columnBindings[0].adapter.invoke()), null, null);
newRow.parts = firstCell;
firstCell.addToBody(Fixture.gray(" surplus"));
wrong(firstCell);
for (int i = 1; i < columnBindings.length; i++) {
Parse nextCell = new Parse("td",
String.valueOf(columnBindings[i].adapter.invoke()), null, null);
firstCell.more = nextCell;
firstCell = nextCell;
}
} catch (Exception e) {
exception(newRow, e);
}
}
}
@SuppressWarnings("unchecked")
protected Class getJavaClassForColumn(DataColumn col) throws ClassNotFoundException, SQLException {
return Class.forName(col.getJavaClassName());
}
}