package dbfit.util;
import fit.Fixture;
import fit.TypeAdapter;
import static dbfit.util.SymbolUtil.isSymbolGetter;
/* this class addresses several issues with parsing, and is used as a base for any other dbfit type adapters:
* 1: TypeAdapter does not check the parse delegates directly when parsing, it relies on appropriate delegate
* being selected when adapter is created; this creates problems for dbfit adapters which need to delegate parsing
* to appropriate types, but access values from sql parameters etc
* 2: this class encapsulates symbol access using <<
* 3: some db drivers will return different numeric types for the same column depending on use (in view, autogenerated..);
* this adapter tries to fix inconsistent types first by casting, then by parsing if needed
*
*/
public class ParseHelper {
private Class<?> type;
private Fixture fixture;
public ParseHelper(Fixture fixture, Class<?> type) {
this.type = type;
this.fixture = fixture;
}
private Object tryToConvert(Object value) throws Exception {
try {
return type.cast(value);
}
catch (ClassCastException cex) {
return parse(value.toString());
}
}
private Object parseSymbol(String s) throws Exception {
Object value = dbfit.util.SymbolUtil.getSymbol(s);
if (null == value || value.getClass().equals(type)) {
return value;
}
// else try to convert
try {
return tryToConvert(value);
} catch (Exception e) {
throw new UnsupportedOperationException(
"Incompatible types between symbol and cell value: expected " +
type + "; but symbol is " + value.getClass(), e);
}
}
public Object parse(String s) throws Exception {
if (isSymbolGetter(s)) {
return parseSymbol(s);
}
String trim = s.trim();
if (trim.toLowerCase().equals("null")) {
return null;
}
if (type.equals(String.class) && Options.isFixedLengthStringParsing() &&
trim.startsWith("'") && trim.endsWith("'")) {
return trim.substring(1, trim.length() - 1);
}
TypeAdapter ta = TypeAdapter.adapterFor(type);
ta.init(fixture, type);
return ta.parse(s);
}
}