Reputation: 21614
For the following a statement in a jexl script,
routeX = Telemat.locate(map, vehicle, newLatLong);
With that JEXL in the following order goes on to
That is, the RHS is resolved before, the has or get is called and therefore my Java code would not know the LHS varname while the RHS is evaluated.
Let's say I embedded an InAccessibleMapException cause in a RunTimeException when the map is inaccessible. And it so happens that at one instance the map was inaccessible. Which led to RunTimeException(InAccessibleMapException) being thrown.
I have try-catch
try {
jexlScript.execute(script);
} catch (RunTimeException rte) {
// but my code does not know the varname.
}
When JEXL throws the exception, my Java code needs to feedback an error message to the engineers I work for that includes the varname that caused the error in their script.
What is the best way or strategy, I could use to capture the varname, when an exception is thrown during the resolution of the RHS, so that the exception catcher could feed back the varname besides the error, in the log and error message?
Upvotes: 0
Views: 71
Reputation: 364
The easiest route is to use the JexlInfo instance in the exception to try and determine the variable whose assignment failed. It ain't pretty but it should work in simple cases.
/**
* Finds the variable name (if any) when failing on assignment
* @param xany the exception
* @param script the script
* @return the variable name or "" if not found
*/
private static String seekVariable(JexlException xany, JexlScript script) {
JexlInfo info = xany.getInfo();
int line = info.getLine();
int column = info.getColumn();
String src = script.getSourceText();
int end = src.length();
int pos = 0;
// find absolute position
while (pos < end){
char c = src.charAt(pos);
if (line == 1) {
pos += column;
break;
}
if (c == '\n') {
line -= 1;
}
pos += 1;
}
boolean fail = true;
// crawl backwards to find '='
while(pos > 0) {
char c = src.charAt(pos--);
if (c == '=') {
fail = false;
break;
}
}
if (fail) return "";
fail = true;
// skip potential spaces
while(pos > 0) {
char c = src.charAt(pos);
if (!Character.isSpaceChar(c)) {
break;
}
pos -= 1;
}
int endName = pos;
int beginName = --pos;
// read varname
while(pos > 0) {
char c = src.charAt(pos);
if (!Character.isJavaIdentifierPart(c)) {
break;
}
beginName = --pos;
}
return src.substring(beginName, endName + 1);
}
@Test
public void test339() throws Exception {
Map<String,Object> telemap = new HashMap<String,Object>() {
@Override public Object get(Object name) {
throw new IllegalStateException("inaccessible map");
}
};
JexlContext ctxt = new MapContext();
ctxt.set("tele", telemap);
ctxt.set("route", null);
JexlEngine jexl = new JexlBuilder().create();
String src = "route = tele.get('11')";
JexlScript script = jexl.createScript(src);
Object result;
try {
result = script.execute(ctxt);
} catch(JexlException xany) {
String varname = seekVariable(xany, script);
Assert.assertEquals("route", varname);
}
}
Upvotes: 0