* Implement positional macro parameters.
* Resolve conflict between failmode and default attributes: failmode=silent now results in default value to be used if the macro fails to resolve or execute. * Extract and clean up argument preparation code.
This commit is contained in:
parent
159251911d
commit
984c6a62a4
1 changed files with 80 additions and 77 deletions
|
@ -34,8 +34,7 @@ import java.io.IOException;
|
||||||
*/
|
*/
|
||||||
public final class Skin {
|
public final class Skin {
|
||||||
static private final int PARSE_MACRONAME = 0;
|
static private final int PARSE_MACRONAME = 0;
|
||||||
static private final int PARSE_PARAMNAME = 1;
|
static private final int PARSE_PARAM = 1;
|
||||||
static private final int PARSE_PARAMVALUE = 2;
|
|
||||||
|
|
||||||
static private final int ENCODE_NONE = 0;
|
static private final int ENCODE_NONE = 0;
|
||||||
static private final int ENCODE_HTML = 1;
|
static private final int ENCODE_HTML = 1;
|
||||||
|
@ -269,7 +268,8 @@ public final class Skin {
|
||||||
// default render parameters - may be overridden if macro changes
|
// default render parameters - may be overridden if macro changes
|
||||||
// param.prefix/suffix/default
|
// param.prefix/suffix/default
|
||||||
StandardParams standardParams = new StandardParams();
|
StandardParams standardParams = new StandardParams();
|
||||||
Map params = null;
|
Map namedParams = null;
|
||||||
|
List positionalParams = null;
|
||||||
// filters defined via <% foo | bar %>
|
// filters defined via <% foo | bar %>
|
||||||
Macro filterChain;
|
Macro filterChain;
|
||||||
|
|
||||||
|
@ -298,13 +298,12 @@ public final class Skin {
|
||||||
|
|
||||||
case '<':
|
case '<':
|
||||||
|
|
||||||
if (state == PARSE_PARAMVALUE && quotechar == '\u0000' && source[i + 1] == '%') {
|
if (state == PARSE_PARAM && quotechar == '\u0000' && source[i + 1] == '%') {
|
||||||
Macro macro = new Macro(i, 2);
|
Macro macro = new Macro(i, 2);
|
||||||
hasNestedMacros = true;
|
hasNestedMacros = true;
|
||||||
addParameter(lastParamName, macro);
|
addParameter(lastParamName, macro);
|
||||||
lastParamName = null;
|
lastParamName = null;
|
||||||
b.setLength(0);
|
b.setLength(0);
|
||||||
state = PARSE_PARAMNAME;
|
|
||||||
i = macro.end - 1;
|
i = macro.end - 1;
|
||||||
} else {
|
} else {
|
||||||
b.append(source[i]);
|
b.append(source[i]);
|
||||||
|
@ -314,7 +313,7 @@ public final class Skin {
|
||||||
|
|
||||||
case '%':
|
case '%':
|
||||||
|
|
||||||
if ((state != PARSE_PARAMVALUE || quotechar == '\u0000') && source[i + 1] == '>') {
|
if ((state != PARSE_PARAM || quotechar == '\u0000') && source[i + 1] == '>') {
|
||||||
break loop;
|
break loop;
|
||||||
}
|
}
|
||||||
b.append(source[i]);
|
b.append(source[i]);
|
||||||
|
@ -341,7 +340,7 @@ public final class Skin {
|
||||||
|
|
||||||
case '|':
|
case '|':
|
||||||
|
|
||||||
if (state == PARSE_PARAMNAME) {
|
if (!escape && quotechar == '\u0000') {
|
||||||
filterChain = new Macro(i, 1);
|
filterChain = new Macro(i, 1);
|
||||||
i = filterChain.end - 2;
|
i = filterChain.end - 2;
|
||||||
lastParamName = null;
|
lastParamName = null;
|
||||||
|
@ -365,13 +364,12 @@ public final class Skin {
|
||||||
case '"':
|
case '"':
|
||||||
case '\'':
|
case '\'':
|
||||||
|
|
||||||
if (!escape && (state == PARSE_PARAMVALUE)) {
|
if (!escape && state == PARSE_PARAM) {
|
||||||
if (quotechar == source[i]) {
|
if (quotechar == source[i]) {
|
||||||
// add parameter
|
// add parameter
|
||||||
addParameter(lastParamName, b.toString());
|
addParameter(lastParamName, b.toString());
|
||||||
lastParamName = null;
|
lastParamName = null;
|
||||||
b.setLength(0);
|
b.setLength(0);
|
||||||
state = PARSE_PARAMNAME;
|
|
||||||
quotechar = '\u0000';
|
quotechar = '\u0000';
|
||||||
} else if (quotechar == '\u0000') {
|
} else if (quotechar == '\u0000') {
|
||||||
quotechar = source[i];
|
quotechar = source[i];
|
||||||
|
@ -396,28 +394,28 @@ public final class Skin {
|
||||||
if (state == PARSE_MACRONAME && b.length() > 0) {
|
if (state == PARSE_MACRONAME && b.length() > 0) {
|
||||||
name = b.toString().trim();
|
name = b.toString().trim();
|
||||||
b.setLength(0);
|
b.setLength(0);
|
||||||
state = PARSE_PARAMNAME;
|
state = PARSE_PARAM;
|
||||||
} else if ((state == PARSE_PARAMVALUE) && (quotechar == '\u0000')) {
|
} else if (state == PARSE_PARAM) {
|
||||||
// add parameter
|
if (quotechar == '\u0000') {
|
||||||
addParameter(lastParamName, b.toString());
|
if (b.length() > 0) {
|
||||||
lastParamName = null;
|
// add parameter
|
||||||
b.setLength(0);
|
addParameter(lastParamName, b.toString());
|
||||||
state = PARSE_PARAMNAME;
|
lastParamName = null;
|
||||||
} else if (state == PARSE_PARAMVALUE) {
|
b.setLength(0);
|
||||||
b.append(source[i]);
|
}
|
||||||
escape = false;
|
} else {
|
||||||
} else {
|
b.append(source[i]);
|
||||||
b.setLength(0);
|
escape = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '=':
|
case '=':
|
||||||
|
|
||||||
if (state == PARSE_PARAMNAME) {
|
if (!escape && quotechar == '\u0000' && state == PARSE_PARAM && lastParamName == null) {
|
||||||
lastParamName = b.toString().trim();
|
lastParamName = b.toString().trim();
|
||||||
b.setLength(0);
|
b.setLength(0);
|
||||||
state = PARSE_PARAMVALUE;
|
|
||||||
} else {
|
} else {
|
||||||
b.append(source[i]);
|
b.append(source[i]);
|
||||||
escape = false;
|
escape = false;
|
||||||
|
@ -461,12 +459,20 @@ public final class Skin {
|
||||||
}
|
}
|
||||||
// Set default failmode unless explicitly set:
|
// Set default failmode unless explicitly set:
|
||||||
// silent for default handlers, verbose
|
// silent for default handlers, verbose
|
||||||
if (params == null || !params.containsKey("failmode")) {
|
if (namedParams == null || !namedParams.containsKey("failmode")) {
|
||||||
standardParams.silentFailMode = (handler < HANDLER_GLOBAL);
|
standardParams.silentFailMode = (handler < HANDLER_GLOBAL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addParameter(String name, Object value) {
|
private void addParameter(String name, Object value) {
|
||||||
|
if (name == null) {
|
||||||
|
// take shortcut for positional parameters
|
||||||
|
if (positionalParams == null) {
|
||||||
|
positionalParams = new ArrayList();
|
||||||
|
}
|
||||||
|
positionalParams.add(value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
// check if this is parameter is relevant to us
|
// check if this is parameter is relevant to us
|
||||||
if ("prefix".equals(name)) {
|
if ("prefix".equals(name)) {
|
||||||
standardParams.prefix = value;
|
standardParams.prefix = value;
|
||||||
|
@ -493,10 +499,10 @@ public final class Skin {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add parameter to parameter map
|
// Add parameter to parameter map
|
||||||
if (params == null) {
|
if (namedParams == null) {
|
||||||
params = new HashMap();
|
namedParams = new HashMap();
|
||||||
}
|
}
|
||||||
params.put(name, value);
|
namedParams.put(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -531,42 +537,21 @@ public final class Skin {
|
||||||
|
|
||||||
if (reval.scriptingEngine.hasFunction(handlerObject, funcName)) {
|
if (reval.scriptingEngine.hasFunction(handlerObject, funcName)) {
|
||||||
StringBuffer buffer = reval.getResponse().getBuffer();
|
StringBuffer buffer = reval.getResponse().getBuffer();
|
||||||
StandardParams stdParams = standardParams;
|
|
||||||
|
|
||||||
// remember length of response buffer before calling macro
|
// remember length of response buffer before calling macro
|
||||||
int bufLength = buffer.length();
|
int bufLength = buffer.length();
|
||||||
|
|
||||||
// pass a clone/copy of the parameter map so if the script changes it,
|
Object[] arguments = prepareArguments(0, reval, thisObject, handlerCache);
|
||||||
CopyOnWriteMap wrappedParams = null;
|
// get reference to rendered named params for after invocation
|
||||||
Object[] arguments;
|
Map params = (Map) arguments[0];
|
||||||
|
|
||||||
if (params == null) {
|
|
||||||
arguments = new Object[] { new SystemMap(4) };
|
|
||||||
} else if (hasNestedMacros) {
|
|
||||||
SystemMap map = new SystemMap((int) (params.size() * 1.5));
|
|
||||||
ResponseTrans res = reval.getResponse();
|
|
||||||
for (Iterator it = params.entrySet().iterator(); it.hasNext(); ) {
|
|
||||||
Map.Entry entry = (Map.Entry) it.next();
|
|
||||||
Object value = entry.getValue();
|
|
||||||
map.put(entry.getKey(), renderMacro(value, reval, thisObject, handlerCache));
|
|
||||||
}
|
|
||||||
if (standardParams.containsMacros())
|
|
||||||
stdParams = new StandardParams(map);
|
|
||||||
arguments = new Object[] { map };
|
|
||||||
} else {
|
|
||||||
wrappedParams = new CopyOnWriteMap(params);
|
|
||||||
arguments = new Object[] { wrappedParams };
|
|
||||||
}
|
|
||||||
Object value = reval.invokeDirectFunction(handlerObject,
|
Object value = reval.invokeDirectFunction(handlerObject,
|
||||||
funcName,
|
funcName,
|
||||||
arguments);
|
arguments);
|
||||||
|
|
||||||
// if parameter map was modified create new renderParams to override defaults
|
// create new renderParams to override defaults in case the macro changed anything
|
||||||
if (wrappedParams != null && wrappedParams.wasModified()) {
|
StandardParams stdParams = new StandardParams(params);
|
||||||
stdParams = new StandardParams(wrappedParams);
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if this macro has a filter chain
|
// if macro has a filter chain and didn't return anything, use output
|
||||||
|
// as filter argument.
|
||||||
if (filterChain != null) {
|
if (filterChain != null) {
|
||||||
if (value == null && buffer.length() > bufLength) {
|
if (value == null && buffer.length() > bufLength) {
|
||||||
value = buffer.substring(bufLength);
|
value = buffer.substring(bufLength);
|
||||||
|
@ -622,8 +607,9 @@ public final class Skin {
|
||||||
String msg = "[Macro unhandled: " + name + "]";
|
String msg = "[Macro unhandled: " + name + "]";
|
||||||
reval.getResponse().write(" " + msg + " ");
|
reval.getResponse().write(" " + msg + " ");
|
||||||
app.logEvent(msg);
|
app.logEvent(msg);
|
||||||
|
} else {
|
||||||
|
writeResponse(null, reval, thisObject, handlerCache, standardParams, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
Object value = getProperty(handlerObject, propName, reval);
|
Object value = getProperty(handlerObject, propName, reval);
|
||||||
writeResponse(value, reval, thisObject, handlerCache, standardParams, true);
|
writeResponse(value, reval, thisObject, handlerCache, standardParams, true);
|
||||||
|
@ -635,6 +621,8 @@ public final class Skin {
|
||||||
String msg = "[Macro unhandled: " + name + "]";
|
String msg = "[Macro unhandled: " + name + "]";
|
||||||
reval.getResponse().write(" " + msg + " ");
|
reval.getResponse().write(" " + msg + " ");
|
||||||
app.logEvent(msg);
|
app.logEvent(msg);
|
||||||
|
} else {
|
||||||
|
writeResponse(null, reval, thisObject, handlerCache, standardParams, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (RedirectException redir) {
|
} catch (RedirectException redir) {
|
||||||
|
@ -672,25 +660,8 @@ public final class Skin {
|
||||||
String funcName = path[path.length - 1] + "_filter";
|
String funcName = path[path.length - 1] + "_filter";
|
||||||
|
|
||||||
if (reval.scriptingEngine.hasFunction(handlerObject, funcName)) {
|
if (reval.scriptingEngine.hasFunction(handlerObject, funcName)) {
|
||||||
// pass a clone/copy of the parameter map so if the script changes it,
|
Object[] arguments = prepareArguments(1, reval, thisObject, handlerCache);
|
||||||
CopyOnWriteMap wrappedParams = null;
|
arguments[0] = returnValue;
|
||||||
Object[] arguments;
|
|
||||||
|
|
||||||
if (params == null) {
|
|
||||||
arguments = new Object[] { returnValue, new SystemMap(4) };
|
|
||||||
} else if (hasNestedMacros) {
|
|
||||||
SystemMap map = new SystemMap((int) (params.size() * 1.5));
|
|
||||||
ResponseTrans res = reval.getResponse();
|
|
||||||
for (Iterator it = params.entrySet().iterator(); it.hasNext(); ) {
|
|
||||||
Map.Entry entry = (Map.Entry) it.next();
|
|
||||||
Object value = entry.getValue();
|
|
||||||
map.put(entry.getKey(), renderMacro(value, reval, thisObject, handlerCache));
|
|
||||||
}
|
|
||||||
arguments = new Object[] { map };
|
|
||||||
} else {
|
|
||||||
wrappedParams = new CopyOnWriteMap(params);
|
|
||||||
arguments = new Object[] { returnValue, wrappedParams };
|
|
||||||
}
|
|
||||||
Object retval = reval.invokeDirectFunction(handlerObject,
|
Object retval = reval.invokeDirectFunction(handlerObject,
|
||||||
funcName,
|
funcName,
|
||||||
arguments);
|
arguments);
|
||||||
|
@ -699,10 +670,42 @@ public final class Skin {
|
||||||
}
|
}
|
||||||
return filterChain.invokeFilter(reval, retval, thisObject, handlerCache);
|
return filterChain.invokeFilter(reval, retval, thisObject, handlerCache);
|
||||||
} else {
|
} else {
|
||||||
throw new RuntimeException("[Undefined Filter: " + name + "]");
|
throw new RuntimeException("Undefined Filter " + name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Object[] prepareArguments(int offset, RequestEvaluator reval,
|
||||||
|
Object thisObject, Map handlerCache)
|
||||||
|
throws UnsupportedEncodingException {
|
||||||
|
int nPosArgs = (positionalParams == null) ? 0 : positionalParams.size();
|
||||||
|
Object[] arguments = new Object[offset + 1 + nPosArgs];
|
||||||
|
|
||||||
|
if (namedParams == null) {
|
||||||
|
arguments[offset] = new SystemMap(4);
|
||||||
|
} else if (hasNestedMacros) {
|
||||||
|
SystemMap map = new SystemMap((int) (namedParams.size() * 1.5));
|
||||||
|
for (Iterator it = namedParams.entrySet().iterator(); it.hasNext(); ) {
|
||||||
|
Map.Entry entry = (Map.Entry) it.next();
|
||||||
|
Object value = entry.getValue();
|
||||||
|
map.put(entry.getKey(), renderMacro(value, reval, thisObject, handlerCache));
|
||||||
|
}
|
||||||
|
arguments[offset] = map;
|
||||||
|
} else {
|
||||||
|
// pass a clone/copy of the parameter map so if the script changes it,
|
||||||
|
arguments[offset] = new CopyOnWriteMap(namedParams);
|
||||||
|
}
|
||||||
|
if (positionalParams != null) {
|
||||||
|
for (int i = 0; i < nPosArgs; i++) {
|
||||||
|
Object param = positionalParams.get(i);
|
||||||
|
if (param instanceof Macro)
|
||||||
|
arguments[offset + 1 + i] = renderMacro(param, reval, thisObject, handlerCache);
|
||||||
|
else
|
||||||
|
arguments[offset + 1 + i] = param;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return arguments;
|
||||||
|
}
|
||||||
|
|
||||||
private Object resolveHandler(Object thisObject, RequestEvaluator reval, Map handlerCache) {
|
private Object resolveHandler(Object thisObject, RequestEvaluator reval, Map handlerCache) {
|
||||||
if (path.length < 2)
|
if (path.length < 2)
|
||||||
throw new RuntimeException("resolveHandler called on global macro");
|
throw new RuntimeException("resolveHandler called on global macro");
|
||||||
|
@ -803,7 +806,7 @@ public final class Skin {
|
||||||
value = filterChain.invokeFilter(reval, value, thisObject, handlerCache);
|
value = filterChain.invokeFilter(reval, value, thisObject, handlerCache);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value == null) {
|
if (value == null || "".equals(value)) {
|
||||||
if (useDefault) {
|
if (useDefault) {
|
||||||
text = (String) stdParams.defaultValue;
|
text = (String) stdParams.defaultValue;
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Add table
Reference in a new issue