* 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:
hns 2007-03-19 16:46:45 +00:00
parent 159251911d
commit 984c6a62a4

View file

@ -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 {