* Simplify argument passing in Skin by introducing RenderContext class

which is basically an argument holder.
This commit is contained in:
hns 2007-04-02 15:41:20 +00:00
parent ab4ea1e144
commit a88aebf2fb

View file

@ -266,13 +266,14 @@ public final class Skin {
if (macros.length > 3) { if (macros.length > 3) {
handlerCache = new HashMap(); handlerCache = new HashMap();
} }
RenderContext cx = new RenderContext(reval, thisObject, handlerCache);
for (int i = 0; i < macros.length; i++) { for (int i = 0; i < macros.length; i++) {
if (macros[i].start > written) { if (macros[i].start > written) {
res.write(source, written, macros[i].start - written); res.write(source, written, macros[i].start - written);
} }
macros[i].render(reval, thisObject, handlerCache); macros[i].render(cx);
written = macros[i].end; written = macros[i].end;
} }
@ -318,11 +319,10 @@ public final class Skin {
sandbox.add(macroname); sandbox.add(macroname);
} }
private Object invokeMacro(Object value, RequestEvaluator reval, private Object invokeMacro(Object value, RenderContext cx)
Object thisObj, Map handlerCache)
throws Exception { throws Exception {
if (value instanceof Macro) { if (value instanceof Macro) {
return ((Macro) value).invokeAsMacro(reval, thisObj, handlerCache, null); return ((Macro) value).invokeAsMacro(cx, null);
} else { } else {
return value; return value;
} }
@ -334,7 +334,7 @@ public final class Skin {
final int start, end; final int start, end;
String name; String name;
String[] path; String[] path;
int handler = HANDLER_OTHER; int handlerType = HANDLER_OTHER;
int encoding = ENCODE_NONE; int encoding = ENCODE_NONE;
boolean hasNestedMacros = false; boolean hasNestedMacros = false;
@ -543,19 +543,19 @@ public final class Skin {
path = StringUtils.split(name, "."); path = StringUtils.split(name, ".");
if (path.length <= 1) { if (path.length <= 1) {
handler = HANDLER_GLOBAL; handlerType = HANDLER_GLOBAL;
} else { } else {
String handlerName = path[0]; String handlerName = path[0];
if ("this".equalsIgnoreCase(handlerName)) { if ("this".equalsIgnoreCase(handlerName)) {
handler = HANDLER_THIS; handlerType = HANDLER_THIS;
} else if ("response".equalsIgnoreCase(handlerName)) { } else if ("response".equalsIgnoreCase(handlerName)) {
handler = HANDLER_RESPONSE; handlerType = HANDLER_RESPONSE;
} else if ("request".equalsIgnoreCase(handlerName)) { } else if ("request".equalsIgnoreCase(handlerName)) {
handler = HANDLER_REQUEST; handlerType = HANDLER_REQUEST;
} else if ("session".equalsIgnoreCase(handlerName)) { } else if ("session".equalsIgnoreCase(handlerName)) {
handler = HANDLER_SESSION; handlerType = HANDLER_SESSION;
} else if ("param".equalsIgnoreCase(handlerName)) { } else if ("param".equalsIgnoreCase(handlerName)) {
handler = HANDLER_PARAM; handlerType = HANDLER_PARAM;
} }
} }
} }
@ -601,8 +601,7 @@ public final class Skin {
namedParams.put(name, value); namedParams.put(name, value);
} }
private Object invokeAsMacro(RequestEvaluator reval, Object thisObject, private Object invokeAsMacro(RenderContext cx, StandardParams stdParams)
Map handlerCache, StandardParams stdParams)
throws Exception { throws Exception {
// immediately return for comment macros // immediately return for comment macros
@ -616,14 +615,14 @@ public final class Skin {
Object handlerObject = null; Object handlerObject = null;
Object value = null; Object value = null;
ScriptingEngine engine = reval.scriptingEngine; ScriptingEngine engine = cx.reval.scriptingEngine;
if (handler != HANDLER_GLOBAL) { if (handlerType != HANDLER_GLOBAL) {
handlerObject = resolveHandler(thisObject, reval, handlerCache); handlerObject = cx.resolveHandler(path[0], handlerType);
handlerObject = resolvePath(handlerObject, engine); handlerObject = resolvePath(handlerObject, engine);
} }
if (handler == HANDLER_GLOBAL || handlerObject != null) { if (handlerType == HANDLER_GLOBAL || handlerObject != null) {
// check if a function called name_macro is defined. // check if a function called name_macro is defined.
// if so, the macro evaluates to the function. Otherwise, // if so, the macro evaluates to the function. Otherwise,
// a property/field with the name is used, if defined. // a property/field with the name is used, if defined.
@ -631,14 +630,14 @@ public final class Skin {
String funcName = propName + "_macro"; String funcName = propName + "_macro";
if (engine.hasFunction(handlerObject, funcName)) { if (engine.hasFunction(handlerObject, funcName)) {
StringBuffer buffer = reval.getResponse().getBuffer(); StringBuffer buffer = cx.reval.getResponse().getBuffer();
// remember length of response buffer before calling macro // remember length of response buffer before calling macro
int bufLength = buffer.length(); int bufLength = buffer.length();
Object[] arguments = prepareArguments(0, reval, thisObject, handlerCache); Object[] arguments = prepareArguments(0, cx);
// get reference to rendered named params for after invocation // get reference to rendered named params for after invocation
Map params = (Map) arguments[0]; Map params = (Map) arguments[0];
value = reval.invokeDirectFunction(handlerObject, value = cx.reval.invokeDirectFunction(handlerObject,
funcName, funcName,
arguments); arguments);
@ -647,23 +646,21 @@ public final class Skin {
// if macro has a filter chain and didn't return anything, use output // if macro has a filter chain and didn't return anything, use output
// as filter argument. // as filter argument.
if (filterChain != null) { if (filterChain != null && value == null && buffer.length() > bufLength) {
if (value == null && buffer.length() > bufLength) {
value = buffer.substring(bufLength); value = buffer.substring(bufLength);
buffer.setLength(bufLength); buffer.setLength(bufLength);
} }
}
return filter(reval, value, thisObject, handlerCache); return filter(value, cx);
} else { } else {
if (handler == HANDLER_RESPONSE) { if (handlerType == HANDLER_RESPONSE) {
// some special handling for response handler // some special handling for response handler
if ("message".equals(propName)) if ("message".equals(propName))
value = reval.getResponse().getMessage(); value = cx.reval.getResponse().getMessage();
else if ("error".equals(propName)) else if ("error".equals(propName))
value = reval.getResponse().getError(); value = cx.reval.getResponse().getError();
if (value != null) if (value != null)
return filter(reval, value, thisObject, handlerCache); return filter(value, cx);
} }
// display error message unless silent failmode is on // display error message unless silent failmode is on
if (!engine.hasProperty(handlerObject, propName) if (!engine.hasProperty(handlerObject, propName)
@ -671,38 +668,38 @@ public final class Skin {
throw new MacroUnhandledException(name); throw new MacroUnhandledException(name);
} }
value = engine.getProperty(handlerObject, propName); value = engine.getProperty(handlerObject, propName);
return filter(reval, value, thisObject, handlerCache); return filter(value, cx);
} }
} else if (standardParams.verboseFailmode(handlerObject, engine)) { } else if (standardParams.verboseFailmode(handlerObject, engine)) {
throw new MacroUnhandledException(name); throw new MacroUnhandledException(name);
} }
return filter(reval, null, thisObject, handlerCache); return filter(null, cx);
} }
/** /**
* Render the macro given a handler object. * Render the macro given a handler object.
*/ */
public void render(RequestEvaluator reval, Object thisObject, Map handlerCache) public void render(RenderContext cx)
throws RedirectException, UnsupportedEncodingException { throws RedirectException, UnsupportedEncodingException {
StringBuffer buffer = reval.getResponse().getBuffer(); StringBuffer buffer = cx.reval.getResponse().getBuffer();
// remember length of response buffer before calling macro // remember length of response buffer before calling macro
int bufLength = buffer.length(); int bufLength = buffer.length();
try { try {
StandardParams stdParams = standardParams.render(reval, thisObject, handlerCache); StandardParams stdParams = standardParams.render(cx);
Object value = invokeAsMacro(reval, thisObject, handlerCache, stdParams); Object value = invokeAsMacro(cx, stdParams);
// check if macro wrote out to response buffer // check if macro wrote out to response buffer
if (buffer.length() == bufLength) { if (buffer.length() == bufLength) {
// If the macro function didn't write anything to the response itself, // If the macro function didn't write anything to the response itself,
// we interpret its return value as macro output. // we interpret its return value as macro output.
writeResponse(value, reval, stdParams, true); writeResponse(value, cx.reval, stdParams, true);
} else { } else {
// if an encoding is specified, re-encode the macro's output // if an encoding is specified, re-encode the macro's output
if (encoding != ENCODE_NONE) { if (encoding != ENCODE_NONE) {
String output = buffer.substring(bufLength); String output = buffer.substring(bufLength);
buffer.setLength(bufLength); buffer.setLength(bufLength);
writeResponse(output, reval, stdParams, false); writeResponse(output, cx.reval, stdParams, false);
} else { } else {
// insert prefix, // insert prefix,
if (stdParams.prefix != null) { if (stdParams.prefix != null) {
@ -717,7 +714,7 @@ public final class Skin {
// Append macro return value even if it wrote something to the response, // Append macro return value even if it wrote something to the response,
// but don't render default value in case it returned nothing. // but don't render default value in case it returned nothing.
// We do this for the sake of consistency. // We do this for the sake of consistency.
writeResponse(value, reval, stdParams, false); writeResponse(value, cx.reval, stdParams, false);
} }
} catch (RedirectException redir) { } catch (RedirectException redir) {
@ -728,7 +725,7 @@ public final class Skin {
throw timeout; throw timeout;
} catch (MacroUnhandledException unhandled) { } catch (MacroUnhandledException unhandled) {
String msg = "Macro unhandled: " + unhandled.getMessage(); String msg = "Macro unhandled: " + unhandled.getMessage();
reval.getResponse().write(" [" + msg + "] "); cx.reval.getResponse().write(" [" + msg + "] ");
app.logError(msg); app.logError(msg);
} catch (Exception x) { } catch (Exception x) {
String msg = x.getMessage(); String msg = x.getMessage();
@ -737,24 +734,22 @@ public final class Skin {
} }
msg = new StringBuffer("Macro error in ").append(name) msg = new StringBuffer("Macro error in ").append(name)
.append(": ").append(msg).toString(); .append(": ").append(msg).toString();
reval.getResponse().write(" [" + msg + "] "); cx.reval.getResponse().write(" [" + msg + "] ");
app.logError(msg, x); app.logError(msg, x);
} }
} }
private Object filter(RequestEvaluator reval, Object returnValue, private Object filter(Object returnValue, RenderContext cx)
Object thisObject, Map handlerCache)
throws Exception { throws Exception {
// invoke filter chain if defined // invoke filter chain if defined
if (filterChain != null) { if (filterChain != null) {
return filterChain.invokeAsFilter(reval, returnValue, thisObject, handlerCache); return filterChain.invokeAsFilter(returnValue, cx);
} else { } else {
return returnValue; return returnValue;
} }
} }
private Object invokeAsFilter(RequestEvaluator reval, Object returnValue, private Object invokeAsFilter(Object returnValue, RenderContext cx)
Object thisObject, Map handlerCache)
throws Exception { throws Exception {
if (name == null) { if (name == null) {
@ -764,28 +759,27 @@ public final class Skin {
} }
Object handlerObject = null; Object handlerObject = null;
if (handler != HANDLER_GLOBAL) { if (handlerType != HANDLER_GLOBAL) {
handlerObject = resolveHandler(thisObject, reval, handlerCache); handlerObject = cx.resolveHandler(path[0], handlerType);
handlerObject = resolvePath(handlerObject, reval.scriptingEngine); handlerObject = resolvePath(handlerObject, cx.reval.scriptingEngine);
} }
String funcName = path[path.length - 1] + "_filter"; String funcName = path[path.length - 1] + "_filter";
if (reval.scriptingEngine.hasFunction(handlerObject, funcName)) { if (cx.reval.scriptingEngine.hasFunction(handlerObject, funcName)) {
Object[] arguments = prepareArguments(1, reval, thisObject, handlerCache); Object[] arguments = prepareArguments(1, cx);
arguments[0] = returnValue; arguments[0] = returnValue;
Object retval = reval.invokeDirectFunction(handlerObject, Object retval = cx.reval.invokeDirectFunction(handlerObject,
funcName, funcName,
arguments); arguments);
return filter(reval, retval, thisObject, handlerCache); return filter(retval, cx);
} else { } else {
throw new RuntimeException("Undefined Filter " + name); throw new RuntimeException("Undefined Filter " + name);
} }
} }
private Object[] prepareArguments(int offset, RequestEvaluator reval, private Object[] prepareArguments(int offset, RenderContext cx)
Object thisObject, Map handlerCache)
throws Exception { throws Exception {
int nPosArgs = (positionalParams == null) ? 0 : positionalParams.size(); int nPosArgs = (positionalParams == null) ? 0 : positionalParams.size();
Object[] arguments = new Object[offset + 1 + nPosArgs]; Object[] arguments = new Object[offset + 1 + nPosArgs];
@ -797,7 +791,7 @@ public final class Skin {
for (Iterator it = namedParams.entrySet().iterator(); it.hasNext(); ) { for (Iterator it = namedParams.entrySet().iterator(); it.hasNext(); ) {
Map.Entry entry = (Map.Entry) it.next(); Map.Entry entry = (Map.Entry) it.next();
Object value = entry.getValue(); Object value = entry.getValue();
map.put(entry.getKey(), invokeMacro(value, reval, thisObject, handlerCache)); map.put(entry.getKey(), invokeMacro(value, cx));
} }
arguments[offset] = map; arguments[offset] = map;
} else { } else {
@ -808,7 +802,7 @@ public final class Skin {
for (int i = 0; i < nPosArgs; i++) { for (int i = 0; i < nPosArgs; i++) {
Object param = positionalParams.get(i); Object param = positionalParams.get(i);
if (param instanceof Macro) if (param instanceof Macro)
arguments[offset + 1 + i] = invokeMacro(param, reval, thisObject, handlerCache); arguments[offset + 1 + i] = invokeMacro(param, cx);
else else
arguments[offset + 1 + i] = param; arguments[offset + 1 + i] = param;
} }
@ -816,64 +810,6 @@ public final class Skin {
return arguments; return arguments;
} }
private Object resolveHandler(Object thisObject, RequestEvaluator reval, Map handlerCache) {
if (path.length < 2)
throw new RuntimeException("resolveHandler called on global macro");
switch (handler) {
case HANDLER_THIS:
return thisObject;
case HANDLER_RESPONSE:
return reval.getResponse().getResponseData();
case HANDLER_REQUEST:
return reval.getRequest().getRequestData();
case HANDLER_SESSION:
return reval.getSession().getCacheNode();
}
String handlerName = path[0];
// try to get handler from handlerCache first
if (handlerCache != null && handlerCache.containsKey(handlerName)) {
return handlerCache.get(handlerName);
}
// if handler object wasn't found in cache retrieve it
if (thisObject != null) {
// not a global macro - need to find handler object
// was called with this object - check it or its parents for matching prototype
if (handlerName.equalsIgnoreCase(app.getPrototypeName(thisObject))) {
// we already have the right handler object
// put the found handler object into the cache so we don't have to look again
if (handlerCache != null)
handlerCache.put(handlerName, thisObject);
return thisObject;
} else {
// the handler object is not what we want
Object obj = thisObject;
// walk down parent chain to find handler object
while (obj != null) {
Prototype proto = app.getPrototype(obj);
if ((proto != null) && proto.isInstanceOf(handlerName)) {
if (handlerCache != null)
handlerCache.put(handlerName, obj);
return obj;
}
obj = app.getParentElement(obj);
}
}
}
Map macroHandlers = reval.getResponse().getMacroHandlers();
Object obj = macroHandlers.get(handlerName);
if (handlerCache != null && obj != null) {
handlerCache.put(handlerName, obj);
}
return obj;
}
private Object resolvePath(Object handler, ScriptingEngine engine) { private Object resolvePath(Object handler, ScriptingEngine engine) {
if (path.length > 2 && !app.allowDeepMacros) { if (path.length > 2 && !app.allowDeepMacros) {
throw new RuntimeException("allowDeepMacros property must be true " + throw new RuntimeException("allowDeepMacros property must be true " +
@ -1005,18 +941,84 @@ public final class Skin {
engine.isTypedObject(handler))); engine.isTypedObject(handler)));
} }
StandardParams render(RequestEvaluator reval, Object thisObj, Map handlerCache) StandardParams render(RenderContext cx)
throws Exception { throws Exception {
if (!containsMacros()) if (!containsMacros())
return this; return this;
StandardParams stdParams = new StandardParams(); StandardParams stdParams = new StandardParams();
stdParams.prefix = invokeMacro(prefix, reval, thisObj, handlerCache); stdParams.prefix = invokeMacro(prefix, cx);
stdParams.suffix = invokeMacro(suffix, reval, thisObj, handlerCache); stdParams.suffix = invokeMacro(suffix, cx);
stdParams.defaultValue = invokeMacro(defaultValue, reval, thisObj, handlerCache); stdParams.defaultValue = invokeMacro(defaultValue, cx);
return stdParams; return stdParams;
} }
} }
class RenderContext {
final RequestEvaluator reval;
final Object thisObject;
final Map handlerCache;
RenderContext(RequestEvaluator reval, Object thisObject, Map handlerCache) {
this.reval = reval;
this.thisObject = thisObject;
this.handlerCache = handlerCache;
}
private Object resolveHandler(String handlerName, int handlerType) {
switch (handlerType) {
case HANDLER_THIS:
return thisObject;
case HANDLER_RESPONSE:
return reval.getResponse().getResponseData();
case HANDLER_REQUEST:
return reval.getRequest().getRequestData();
case HANDLER_SESSION:
return reval.getSession().getCacheNode();
}
// try to get handler from handlerCache first
if (handlerCache != null && handlerCache.containsKey(handlerName)) {
return handlerCache.get(handlerName);
}
// if handler object wasn't found in cache retrieve it
if (thisObject != null) {
// not a global macro - need to find handler object
// was called with this object - check it or its parents for matching prototype
if (handlerName.equalsIgnoreCase(app.getPrototypeName(thisObject))) {
// we already have the right handler object
// put the found handler object into the cache so we don't have to look again
if (handlerCache != null)
handlerCache.put(handlerName, thisObject);
return thisObject;
} else {
// the handler object is not what we want
Object obj = thisObject;
// walk down parent chain to find handler object
while (obj != null) {
Prototype proto = app.getPrototype(obj);
if ((proto != null) && proto.isInstanceOf(handlerName)) {
if (handlerCache != null)
handlerCache.put(handlerName, obj);
return obj;
}
obj = app.getParentElement(obj);
}
}
}
Map macroHandlers = reval.getResponse().getMacroHandlers();
Object obj = macroHandlers.get(handlerName);
if (handlerCache != null && obj != null) {
handlerCache.put(handlerName, obj);
}
return obj;
}
}
} }
/** /**