Made EvalError serializable because errors can be cell values and need to be saved.

Turned is* functions into controls, since they have to be able to test errors, and only controls can do that, not functions.
Polished display of errors in cells and in expression preview dialog.

git-svn-id: http://google-refine.googlecode.com/svn/trunk@155 7d457c2a-affb-35e4-300a-418c747d4874
This commit is contained in:
David Huynh 2010-03-01 00:21:13 +00:00
parent 0c6590fe2c
commit 983be19e14
23 changed files with 175 additions and 179 deletions

View File

@ -73,14 +73,16 @@ public class PreviewExpressionCommand extends Command {
}
}
if (result != null) {
if (result instanceof EvalError) {
result = "[Error: " + ((EvalError) result).message + "]";
} else if (result instanceof HasFields) {
if (ExpressionUtils.isError(result)) {
writer.object();
writer.key("message"); writer.value(((EvalError) result).message);
writer.endObject();
} else {
if (result != null && result instanceof HasFields) {
result = "[object " + result.getClass().getSimpleName() + "]";
}
}
writer.value(result);
}
writer.value(result);
}
writer.endArray();
} catch (ParserException e) {

View File

@ -8,6 +8,11 @@ import java.util.Map.Entry;
import com.metaweb.gridworks.expr.controls.ForEach;
import com.metaweb.gridworks.expr.controls.ForNonBlank;
import com.metaweb.gridworks.expr.controls.If;
import com.metaweb.gridworks.expr.controls.IsBlank;
import com.metaweb.gridworks.expr.controls.IsNonBlank;
import com.metaweb.gridworks.expr.controls.IsNotNull;
import com.metaweb.gridworks.expr.controls.IsNull;
import com.metaweb.gridworks.expr.controls.IsNumeric;
import com.metaweb.gridworks.expr.controls.With;
import com.metaweb.gridworks.expr.functions.Get;
import com.metaweb.gridworks.expr.functions.Length;
@ -56,11 +61,6 @@ import com.metaweb.gridworks.expr.functions.strings.Trim;
import com.metaweb.gridworks.expr.functions.strings.Unescape;
import com.metaweb.gridworks.expr.functions.strings.Unicode;
import com.metaweb.gridworks.expr.functions.strings.UnicodeType;
import com.metaweb.gridworks.expr.functions.tests.IsBlank;
import com.metaweb.gridworks.expr.functions.tests.IsNotBlank;
import com.metaweb.gridworks.expr.functions.tests.IsNotNull;
import com.metaweb.gridworks.expr.functions.tests.IsNull;
import com.metaweb.gridworks.expr.functions.tests.IsNumeric;
public class ControlFunctionRegistry {
@ -83,7 +83,7 @@ public class ControlFunctionRegistry {
static public Control getControl(String name) {
return s_nameToControl.get(name);
}
static public String getControlName(Function f) {
static public String getControlName(Control f) {
return s_controlToName.get(f);
}
static public Set<Entry<String, Control>> getControlMapping() {
@ -156,15 +156,16 @@ public class ControlFunctionRegistry {
registerFunction("and", new And());
registerFunction("or", new Or());
registerFunction("not", new Not());
registerFunction("isNull", new IsNull());
registerFunction("isNotNull", new IsNotNull());
registerFunction("isBlank", new IsBlank());
registerFunction("isNotBlank", new IsNotBlank());
registerFunction("isNumeric", new IsNumeric());
registerControl("if", new If());
registerControl("with", new With());
registerControl("forEach", new ForEach());
registerControl("forNonBlank", new ForNonBlank());
registerControl("isNull", new IsNull());
registerControl("isNotNull", new IsNotNull());
registerControl("isBlank", new IsBlank());
registerControl("isNonBlank", new IsNonBlank());
registerControl("isNumeric", new IsNumeric());
}
}

View File

@ -1,5 +1,6 @@
package com.metaweb.gridworks.expr;
import java.io.Serializable;
import java.util.Properties;
import org.json.JSONException;
@ -7,7 +8,9 @@ import org.json.JSONWriter;
import com.metaweb.gridworks.Jsonizable;
public class EvalError implements Jsonizable {
public class EvalError implements Serializable, Jsonizable {
private static final long serialVersionUID = -102681220092874080L;
final public String message;
public EvalError(String message) {

View File

@ -8,6 +8,7 @@ import org.json.JSONException;
import org.json.JSONWriter;
import com.metaweb.gridworks.expr.Control;
import com.metaweb.gridworks.expr.ControlFunctionRegistry;
import com.metaweb.gridworks.expr.EvalError;
import com.metaweb.gridworks.expr.Evaluable;
import com.metaweb.gridworks.expr.ExpressionUtils;
@ -16,9 +17,9 @@ import com.metaweb.gridworks.expr.VariableExpr;
public class ForEach implements Control {
public String checkArguments(Evaluable[] args) {
if (args.length != 3) {
return "forEach expects 3 arguments";
return ControlFunctionRegistry.getControlName(this) + " expects 3 arguments";
} else if (!(args[1] instanceof VariableExpr)) {
return "forEach expects second argument to be a variable name";
return ControlFunctionRegistry.getControlName(this) + " expects second argument to be a variable name";
}
return null;
}

View File

@ -6,6 +6,7 @@ import org.json.JSONException;
import org.json.JSONWriter;
import com.metaweb.gridworks.expr.Control;
import com.metaweb.gridworks.expr.ControlFunctionRegistry;
import com.metaweb.gridworks.expr.Evaluable;
import com.metaweb.gridworks.expr.ExpressionUtils;
import com.metaweb.gridworks.expr.VariableExpr;
@ -13,9 +14,9 @@ import com.metaweb.gridworks.expr.VariableExpr;
public class ForNonBlank implements Control {
public String checkArguments(Evaluable[] args) {
if (args.length != 4) {
return "forNonBlank expects 4 arguments";
return ControlFunctionRegistry.getControlName(this) + " expects 4 arguments";
} else if (!(args[1] instanceof VariableExpr)) {
return "forNonBlank expects second argument to be a variable name";
return ControlFunctionRegistry.getControlName(this) + " expects second argument to be a variable name";
}
return null;
}

View File

@ -6,13 +6,14 @@ import org.json.JSONException;
import org.json.JSONWriter;
import com.metaweb.gridworks.expr.Control;
import com.metaweb.gridworks.expr.ControlFunctionRegistry;
import com.metaweb.gridworks.expr.Evaluable;
import com.metaweb.gridworks.expr.ExpressionUtils;
public class If implements Control {
public String checkArguments(Evaluable[] args) {
if (args.length != 3) {
return "if expects 3 arguments";
return ControlFunctionRegistry.getControlName(this) + " expects 3 arguments";
}
return null;
}

View File

@ -0,0 +1,15 @@
package com.metaweb.gridworks.expr.controls;
import com.metaweb.gridworks.expr.ExpressionUtils;
public class IsBlank extends IsTest {
@Override
protected String getDescription() {
return "Returns whether o is null or an empty string";
}
@Override
protected boolean test(Object o) {
return !ExpressionUtils.isNonBlankData(o);
}
}

View File

@ -0,0 +1,15 @@
package com.metaweb.gridworks.expr.controls;
import com.metaweb.gridworks.expr.ExpressionUtils;
public class IsNonBlank extends IsTest {
@Override
protected String getDescription() {
return "Returns whether o is not null and not an empty string";
}
@Override
protected boolean test(Object o) {
return ExpressionUtils.isNonBlankData(o);
}
}

View File

@ -0,0 +1,13 @@
package com.metaweb.gridworks.expr.controls;
public class IsNotNull extends IsTest {
@Override
protected String getDescription() {
return "Returns whether o is not null";
}
@Override
protected boolean test(Object o) {
return o != null;
}
}

View File

@ -0,0 +1,13 @@
package com.metaweb.gridworks.expr.controls;
public class IsNull extends IsTest {
@Override
protected String getDescription() {
return "Returns whether o is null";
}
@Override
protected boolean test(Object o) {
return o == null;
}
}

View File

@ -0,0 +1,19 @@
package com.metaweb.gridworks.expr.controls;
import org.apache.commons.lang.StringUtils;
public class IsNumeric extends IsTest {
@Override
protected String getDescription() {
return "Returns whether o can represent a number";
}
@Override
protected boolean test(Object o) {
if (o instanceof Number) return true;
String s = (o instanceof String) ? (String) o : o.toString();
return StringUtils.isNumeric(s);
}
}

View File

@ -0,0 +1,42 @@
package com.metaweb.gridworks.expr.controls;
import java.util.Properties;
import org.json.JSONException;
import org.json.JSONWriter;
import com.metaweb.gridworks.expr.Control;
import com.metaweb.gridworks.expr.ControlFunctionRegistry;
import com.metaweb.gridworks.expr.Evaluable;
abstract class IsTest implements Control {
public String checkArguments(Evaluable[] args) {
if (args.length != 1) {
return ControlFunctionRegistry.getControlName(this) + " expects one argument";
}
return null;
}
public Object call(Properties bindings, Evaluable[] args) {
Object o = args[0].evaluate(bindings);
return test(o);
}
public void write(JSONWriter writer, Properties options)
throws JSONException {
writer.object();
writer.key("description"); writer.value(
"Evaluates expression o. If it is true, evaluates expression eTrue and returns the result. " +
"Otherwise, evaluates expression eFalse and returns that result instead."
);
writer.key("params"); writer.value("expression o");
writer.key("returns"); writer.value("boolean");
writer.endObject();
}
abstract protected boolean test(Object v);
abstract protected String getDescription();
}

View File

@ -1,31 +1,27 @@
package com.metaweb.gridworks.expr.controls;
import java.util.Properties;
import java.util.Properties;
import org.json.JSONException;
import org.json.JSONWriter;
import com.metaweb.gridworks.expr.Control;
import com.metaweb.gridworks.expr.ControlFunctionRegistry;
import com.metaweb.gridworks.expr.Evaluable;
import com.metaweb.gridworks.expr.ExpressionUtils;
import com.metaweb.gridworks.expr.VariableExpr;
public class With implements Control {
public String checkArguments(Evaluable[] args) {
if (args.length != 3) {
return "with expects 3 arguments";
return ControlFunctionRegistry.getControlName(this) + " expects 3 arguments";
} else if (!(args[1] instanceof VariableExpr)) {
return "with expects second argument to be a variable name";
return ControlFunctionRegistry.getControlName(this) + " expects second argument to be a variable name";
}
return null;
}
public Object call(Properties bindings, Evaluable[] args) {
Object o = args[0].evaluate(bindings);
if (ExpressionUtils.isError(o)) {
return o;
}
String name = ((VariableExpr) args[1]).getName();
Object oldValue = bindings.get(name);

View File

@ -1,26 +0,0 @@
package com.metaweb.gridworks.expr.functions.tests;
import java.util.Properties;
import org.json.JSONException;
import org.json.JSONWriter;
import com.metaweb.gridworks.expr.ExpressionUtils;
import com.metaweb.gridworks.expr.Function;
public class IsBlank implements Function {
public Object call(Properties bindings, Object[] args) {
return args.length == 0 || !ExpressionUtils.isNonBlankData(args[0]);
}
public void write(JSONWriter writer, Properties options)
throws JSONException {
writer.object();
writer.key("description"); writer.value("Returns whether o is null or an empty string");
writer.key("params"); writer.value("o");
writer.key("returns"); writer.value("boolean");
writer.endObject();
}
}

View File

@ -1,26 +0,0 @@
package com.metaweb.gridworks.expr.functions.tests;
import java.util.Properties;
import org.json.JSONException;
import org.json.JSONWriter;
import com.metaweb.gridworks.expr.ExpressionUtils;
import com.metaweb.gridworks.expr.Function;
public class IsNotBlank implements Function {
public Object call(Properties bindings, Object[] args) {
return args.length > 0 && ExpressionUtils.isNonBlankData(args[0]);
}
public void write(JSONWriter writer, Properties options)
throws JSONException {
writer.object();
writer.key("description"); writer.value("Returns whether o is not null, not an error, and not an empty string");
writer.key("params"); writer.value("o");
writer.key("returns"); writer.value("boolean");
writer.endObject();
}
}

View File

@ -1,25 +0,0 @@
package com.metaweb.gridworks.expr.functions.tests;
import java.util.Properties;
import org.json.JSONException;
import org.json.JSONWriter;
import com.metaweb.gridworks.expr.Function;
public class IsNotNull implements Function {
public Object call(Properties bindings, Object[] args) {
return args.length > 0 && args[0] != null;
}
public void write(JSONWriter writer, Properties options)
throws JSONException {
writer.object();
writer.key("description"); writer.value("Returns whether o is not null");
writer.key("params"); writer.value("o");
writer.key("returns"); writer.value("boolean");
writer.endObject();
}
}

View File

@ -1,25 +0,0 @@
package com.metaweb.gridworks.expr.functions.tests;
import java.util.Properties;
import org.json.JSONException;
import org.json.JSONWriter;
import com.metaweb.gridworks.expr.Function;
public class IsNull implements Function {
public Object call(Properties bindings, Object[] args) {
return args.length == 0 || args[0] == null;
}
public void write(JSONWriter writer, Properties options)
throws JSONException {
writer.object();
writer.key("description"); writer.value("Returns whether o is null");
writer.key("params"); writer.value("o");
writer.key("returns"); writer.value("boolean");
writer.endObject();
}
}

View File

@ -1,34 +0,0 @@
package com.metaweb.gridworks.expr.functions.tests;
import java.util.Properties;
import org.apache.commons.lang.StringUtils;
import org.json.JSONException;
import org.json.JSONWriter;
import com.metaweb.gridworks.expr.ControlFunctionRegistry;
import com.metaweb.gridworks.expr.EvalError;
import com.metaweb.gridworks.expr.Function;
public class IsNumeric implements Function {
public Object call(Properties bindings, Object[] args) {
if (args.length == 1 && args[0] != null) {
Object o = args[0];
if (o instanceof Number) return true;
String s = (o instanceof String) ? (String) o : o.toString();
return StringUtils.isNumeric(s);
}
return new EvalError(ControlFunctionRegistry.getFunctionName(this) + " expects an argument");
}
public void write(JSONWriter writer, Properties options)
throws JSONException {
writer.object();
writer.key("description"); writer.value("Returns whether o can represent a number");
writer.key("params"); writer.value("o");
writer.key("returns"); writer.value("boolean");
writer.endObject();
}
}

View File

@ -7,6 +7,8 @@ import org.json.JSONException;
import org.json.JSONWriter;
import com.metaweb.gridworks.Jsonizable;
import com.metaweb.gridworks.expr.EvalError;
import com.metaweb.gridworks.expr.ExpressionUtils;
import com.metaweb.gridworks.expr.HasFields;
public class Cell implements Serializable, HasFields, Jsonizable {
@ -31,8 +33,13 @@ public class Cell implements Serializable, HasFields, Jsonizable {
public void write(JSONWriter writer, Properties options) throws JSONException {
writer.object();
writer.key("v");
writer.value(value);
if (ExpressionUtils.isError(value)) {
writer.key("e");
writer.value(((EvalError) value).message);
} else {
writer.key("v");
writer.value(value);
}
if (recon != null) {
writer.key("r");

View File

@ -189,13 +189,15 @@ ExpressionPreviewDialog.prototype._renderPreview = function(expression, data) {
if (v !== null && v !== undefined) {
if ($.isArray(v)) {
td.html(JSON.stringify(v));
} else if ($.isPlainObject(v)) {
$('<span></span>').addClass("expression-preview-special-value").text("Error: " + v.message).appendTo(td);
} else if (typeof v === "string" && v.length == 0) {
$('<span>empty string</span>').addClass("expression-preview-empty").appendTo(td);
$('<span>empty string</span>').addClass("expression-preview-special-value").appendTo(td);
} else {
td.html(v.toString());
}
} else {
$('<span>null</span>').addClass("expression-preview-empty").appendTo(td);
$('<span>null</span>').addClass("expression-preview-special-value").appendTo(td);
}
};
@ -214,7 +216,7 @@ ExpressionPreviewDialog.prototype._renderPreview = function(expression, data) {
// error
var message = (data.type == "parser") ? data.message : "internal error";
$('<span></span>').text(message).addClass("expression-preview-empty").appendTo(tdValue);
$('<span></span>').text(message).addClass("expression-preview-special-value").appendTo(tdValue);
}
}
};

View File

@ -15,13 +15,11 @@ DataTableCellUI.prototype._render = function() {
$(this._td).empty();
var divContent = $('<div></div>').appendTo(this._td);
if (cell == null || cell.v == null) {
if (cell == null || ("v" in cell && cell.v == null)) {
$(divContent).html("&nbsp;");
// TODO: content editing UI
return;
}
if (!("r" in cell) || cell.r == null) {
} else if ("e" in cell) {
$('<span>').addClass("data-table-error").text(cell.e).appendTo(divContent);
} else if (!("r" in cell) || cell.r == null) {
$(divContent).html(cell.v);
} else {
var r = cell.r;

View File

@ -60,6 +60,9 @@ img.column-header-menu {
}
.data-table-error {
color: red;
}
div.data-table-recon-candidates {
margin: 0.5em 0;
min-width: 15em;

View File

@ -19,7 +19,7 @@ td.expression-preview-heading {
font-weight: bold;
}
.expression-preview-empty {
.expression-preview-special-value {
color: #aaa;
}