* Refactor and enhance Skin class:
- implement macro/filter pipes <% foo | bar %> - implement deep macros <% foo.bar.foo %> - implement nested macros <% foo x=<% bar %> %> - implement failmode=silent|verbose attribute * Refactor ScriptingEngine interface and implementation to support new skinning features.
This commit is contained in:
parent
1ef63471aa
commit
e4784f870d
3 changed files with 471 additions and 282 deletions
|
@ -20,7 +20,6 @@ import helma.framework.*;
|
||||||
import helma.framework.repository.Resource;
|
import helma.framework.repository.Resource;
|
||||||
import helma.objectmodel.ConcurrencyException;
|
import helma.objectmodel.ConcurrencyException;
|
||||||
import helma.util.*;
|
import helma.util.*;
|
||||||
import helma.scripting.ScriptingException;
|
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
|
@ -34,16 +33,25 @@ import java.io.IOException;
|
||||||
* from the RequestEvaluator object to resolve Macro handlers by type name.
|
* from the RequestEvaluator object to resolve Macro handlers by type name.
|
||||||
*/
|
*/
|
||||||
public final class Skin {
|
public final class Skin {
|
||||||
static final int HANDLER = 0;
|
static private final int PARSE_MACRONAME = 0;
|
||||||
static final int MACRO = 1;
|
static private final int PARSE_PARAMNAME = 1;
|
||||||
static final int PARAMNAME = 2;
|
static private final int PARSE_PARAMVALUE = 2;
|
||||||
static final int PARAMVALUE = 3;
|
|
||||||
static final int ENCODE_NONE = 0;
|
static private final int ENCODE_NONE = 0;
|
||||||
static final int ENCODE_HTML = 1;
|
static private final int ENCODE_HTML = 1;
|
||||||
static final int ENCODE_XML = 2;
|
static private final int ENCODE_XML = 2;
|
||||||
static final int ENCODE_FORM = 3;
|
static private final int ENCODE_FORM = 3;
|
||||||
static final int ENCODE_URL = 4;
|
static private final int ENCODE_URL = 4;
|
||||||
static final int ENCODE_ALL = 5;
|
static private final int ENCODE_ALL = 5;
|
||||||
|
|
||||||
|
static private final int HANDLER_RESPONSE = 0;
|
||||||
|
static private final int HANDLER_REQUEST = 1;
|
||||||
|
static private final int HANDLER_SESSION = 2;
|
||||||
|
static private final int HANDLER_PARAM = 3;
|
||||||
|
static private final int HANDLER_GLOBAL = 4;
|
||||||
|
static private final int HANDLER_THIS = 5;
|
||||||
|
static private final int HANDLER_OTHER = 6;
|
||||||
|
|
||||||
private Macro[] macros;
|
private Macro[] macros;
|
||||||
private Application app;
|
private Application app;
|
||||||
private char[] source;
|
private char[] source;
|
||||||
|
@ -114,22 +122,15 @@ public final class Skin {
|
||||||
private void parse() {
|
private void parse() {
|
||||||
ArrayList partBuffer = new ArrayList();
|
ArrayList partBuffer = new ArrayList();
|
||||||
|
|
||||||
|
boolean escape = false;
|
||||||
for (int i = 0; i < (sourceLength - 1); i++) {
|
for (int i = 0; i < (sourceLength - 1); i++) {
|
||||||
if ((source[i] == '<') && (source[i + 1] == '%')) {
|
if (source[i] == '<' && source[i + 1] == '%' && !escape) {
|
||||||
// found macro start tag
|
// found macro start tag
|
||||||
int j = i + 2;
|
Macro macro = new Macro(i, 2);
|
||||||
|
partBuffer.add(macro);
|
||||||
// search macr end tag
|
i = macro.end - 1;
|
||||||
while ((j < (sourceLength - 1)) &&
|
} else {
|
||||||
((source[j] != '%') || (source[j + 1] != '>'))) {
|
escape = source[i] == '\\' && !escape;
|
||||||
j++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (j > (i + 2)) {
|
|
||||||
partBuffer.add(new Macro(i, j + 2));
|
|
||||||
}
|
|
||||||
|
|
||||||
i = j + 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,7 +223,7 @@ public final class Skin {
|
||||||
if (macros[i] instanceof Macro) {
|
if (macros[i] instanceof Macro) {
|
||||||
Macro m = macros[i];
|
Macro m = macros[i];
|
||||||
|
|
||||||
if (macroname.equals(m.fullName)) {
|
if (macroname.equals(m.name)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -242,58 +243,113 @@ public final class Skin {
|
||||||
sandbox.add(macroname);
|
sandbox.add(macroname);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Object renderMacro(Object value, RequestEvaluator reval,
|
||||||
|
Object thisObj, Map handlerCache)
|
||||||
|
throws UnsupportedEncodingException {
|
||||||
|
if (value instanceof Macro) {
|
||||||
|
ResponseTrans res = reval.getResponse();
|
||||||
|
res.pushStringBuffer();
|
||||||
|
((Macro) value).render(reval, thisObj, handlerCache);
|
||||||
|
return res.popStringBuffer();
|
||||||
|
} else {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class Macro {
|
class Macro {
|
||||||
final int start;
|
final int start, end;
|
||||||
final int end;
|
|
||||||
String handler;
|
|
||||||
String name;
|
String name;
|
||||||
String fullName;
|
String[] path;
|
||||||
|
int handler = HANDLER_OTHER;
|
||||||
int encoding = ENCODE_NONE;
|
int encoding = ENCODE_NONE;
|
||||||
|
boolean hasNestedMacros = false;
|
||||||
|
|
||||||
// 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
|
||||||
RenderParameters defaultRenderParams = new RenderParameters();
|
StandardParams standardParams = new StandardParams();
|
||||||
Map params = null;
|
Map params = null;
|
||||||
|
// filters defined via <% foo | bar %>
|
||||||
|
Macro filterChain;
|
||||||
|
|
||||||
// comment macros are silently dropped during rendering
|
// comment macros are silently dropped during rendering
|
||||||
boolean isCommentMacro = false;
|
boolean isCommentMacro = false;
|
||||||
|
|
||||||
public Macro(int start, int end) {
|
/**
|
||||||
|
* Create and parse a new macro.
|
||||||
|
* @param start the start of the macro within the skin source
|
||||||
|
* @param offset offset of the macro content from the start index
|
||||||
|
*/
|
||||||
|
public Macro(int start, int offset) {
|
||||||
this.start = start;
|
this.start = start;
|
||||||
this.end = end;
|
|
||||||
|
|
||||||
int state = HANDLER;
|
int state = PARSE_MACRONAME;
|
||||||
boolean escape = false;
|
boolean escape = false;
|
||||||
char quotechar = '\u0000';
|
char quotechar = '\u0000';
|
||||||
String lastParamName = null;
|
String lastParamName = null;
|
||||||
StringBuffer b = new StringBuffer();
|
StringBuffer b = new StringBuffer();
|
||||||
|
int i;
|
||||||
|
|
||||||
|
loop:
|
||||||
|
for (i = start + offset; i < sourceLength - 1; i++) {
|
||||||
|
|
||||||
for (int i = start + 2; i < (end - 2); i++) {
|
|
||||||
switch (source[i]) {
|
switch (source[i]) {
|
||||||
|
|
||||||
|
case '<':
|
||||||
|
|
||||||
|
if (state == PARSE_PARAMVALUE && quotechar == '\u0000' && source[i + 1] == '%') {
|
||||||
|
Macro macro = new Macro(i, 2);
|
||||||
|
hasNestedMacros = true;
|
||||||
|
addParameter(lastParamName, macro);
|
||||||
|
lastParamName = null;
|
||||||
|
b.setLength(0);
|
||||||
|
state = PARSE_PARAMNAME;
|
||||||
|
i = macro.end - 1;
|
||||||
|
} else {
|
||||||
|
b.append(source[i]);
|
||||||
|
escape = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '%':
|
||||||
|
|
||||||
|
if ((state != PARSE_PARAMVALUE || quotechar == '\u0000') && source[i + 1] == '>') {
|
||||||
|
break loop;
|
||||||
|
}
|
||||||
|
b.append(source[i]);
|
||||||
|
escape = false;
|
||||||
|
break;
|
||||||
|
|
||||||
case '/':
|
case '/':
|
||||||
|
|
||||||
b.append(source[i]);
|
b.append(source[i]);
|
||||||
escape = false;
|
escape = false;
|
||||||
|
|
||||||
if (state == HANDLER && "//".equals(b.toString())) {
|
if (state == PARSE_MACRONAME && "//".equals(b.toString())) {
|
||||||
isCommentMacro = true;
|
isCommentMacro = true;
|
||||||
return;
|
// search macro end tag
|
||||||
|
while (i < sourceLength - 1 &&
|
||||||
|
(source[i] != '%' || source[i + 1] != '>')) {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
break loop;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '.':
|
case '|':
|
||||||
|
|
||||||
if (state == HANDLER) {
|
if (state == PARSE_PARAMNAME) {
|
||||||
handler = b.toString().trim();
|
filterChain = new Macro(i, 1);
|
||||||
|
i = filterChain.end - 2;
|
||||||
|
lastParamName = null;
|
||||||
b.setLength(0);
|
b.setLength(0);
|
||||||
state = MACRO;
|
break loop;
|
||||||
} else {
|
}
|
||||||
b.append(source[i]);
|
b.append(source[i]);
|
||||||
escape = false;
|
escape = false;
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '\\':
|
case '\\':
|
||||||
|
@ -309,13 +365,13 @@ public final class Skin {
|
||||||
case '"':
|
case '"':
|
||||||
case '\'':
|
case '\'':
|
||||||
|
|
||||||
if (!escape && (state == PARAMVALUE)) {
|
if (!escape && (state == PARSE_PARAMVALUE)) {
|
||||||
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 = PARAMNAME;
|
state = PARSE_PARAMNAME;
|
||||||
quotechar = '\u0000';
|
quotechar = '\u0000';
|
||||||
} else if (quotechar == '\u0000') {
|
} else if (quotechar == '\u0000') {
|
||||||
quotechar = source[i];
|
quotechar = source[i];
|
||||||
|
@ -337,17 +393,17 @@ public final class Skin {
|
||||||
case '\r':
|
case '\r':
|
||||||
case '\f':
|
case '\f':
|
||||||
|
|
||||||
if ((state == MACRO) || ((state == HANDLER) && (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 = PARAMNAME;
|
state = PARSE_PARAMNAME;
|
||||||
} else if ((state == PARAMVALUE) && (quotechar == '\u0000')) {
|
} else if ((state == PARSE_PARAMVALUE) && (quotechar == '\u0000')) {
|
||||||
// add parameter
|
// add parameter
|
||||||
addParameter(lastParamName, b.toString());
|
addParameter(lastParamName, b.toString());
|
||||||
lastParamName = null;
|
lastParamName = null;
|
||||||
b.setLength(0);
|
b.setLength(0);
|
||||||
state = PARAMNAME;
|
state = PARSE_PARAMNAME;
|
||||||
} else if (state == PARAMVALUE) {
|
} else if (state == PARSE_PARAMVALUE) {
|
||||||
b.append(source[i]);
|
b.append(source[i]);
|
||||||
escape = false;
|
escape = false;
|
||||||
} else {
|
} else {
|
||||||
|
@ -358,10 +414,10 @@ public final class Skin {
|
||||||
|
|
||||||
case '=':
|
case '=':
|
||||||
|
|
||||||
if (state == PARAMNAME) {
|
if (state == PARSE_PARAMNAME) {
|
||||||
lastParamName = b.toString().trim();
|
lastParamName = b.toString().trim();
|
||||||
b.setLength(0);
|
b.setLength(0);
|
||||||
state = PARAMVALUE;
|
state = PARSE_PARAMVALUE;
|
||||||
} else {
|
} else {
|
||||||
b.append(source[i]);
|
b.append(source[i]);
|
||||||
escape = false;
|
escape = false;
|
||||||
|
@ -375,42 +431,65 @@ public final class Skin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.end = Math.min(sourceLength, i + 2);
|
||||||
|
|
||||||
if (b.length() > 0) {
|
if (b.length() > 0) {
|
||||||
if (lastParamName != null) {
|
if (lastParamName != null) {
|
||||||
// add parameter
|
// add parameter
|
||||||
addParameter(lastParamName, b.toString());
|
addParameter(lastParamName, b.toString());
|
||||||
} else if (state <= MACRO) {
|
} else if (state == PARSE_MACRONAME) {
|
||||||
name = b.toString().trim();
|
name = b.toString().trim();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (handler == null) {
|
path = StringUtils.split(name, ".");
|
||||||
fullName = name;
|
if (path.length <= 1) {
|
||||||
|
handler = HANDLER_GLOBAL;
|
||||||
} else {
|
} else {
|
||||||
fullName = handler + "." + name;
|
String handlerName = path[0];
|
||||||
|
if ("this".equalsIgnoreCase(handlerName)) {
|
||||||
|
handler = HANDLER_THIS;
|
||||||
|
} else if ("response".equalsIgnoreCase(handlerName)) {
|
||||||
|
handler = HANDLER_RESPONSE;
|
||||||
|
} else if ("request".equalsIgnoreCase(handlerName)) {
|
||||||
|
handler = HANDLER_REQUEST;
|
||||||
|
} else if ("session".equalsIgnoreCase(handlerName)) {
|
||||||
|
handler = HANDLER_SESSION;
|
||||||
|
} else if ("param".equalsIgnoreCase(handlerName)) {
|
||||||
|
handler = HANDLER_PARAM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Set default failmode unless explicitly set:
|
||||||
|
// silent for default handlers, verbose
|
||||||
|
if (params == null || !params.containsKey("failmode")) {
|
||||||
|
standardParams.silentFailMode = (handler < HANDLER_GLOBAL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addParameter(String name, String value) {
|
private void addParameter(String name, Object value) {
|
||||||
// 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)) {
|
||||||
defaultRenderParams.prefix = value;
|
standardParams.prefix = value;
|
||||||
} else if ("suffix".equals(name)) {
|
} else if ("suffix".equals(name)) {
|
||||||
defaultRenderParams.suffix = value;
|
standardParams.suffix = value;
|
||||||
} else if ("encoding".equals(name)) {
|
} else if ("encoding".equals(name)) {
|
||||||
if ("html".equalsIgnoreCase(value)) {
|
if ("html".equals(value)) {
|
||||||
encoding = ENCODE_HTML;
|
encoding = ENCODE_HTML;
|
||||||
} else if ("xml".equalsIgnoreCase(value)) {
|
} else if ("xml".equals(value)) {
|
||||||
encoding = ENCODE_XML;
|
encoding = ENCODE_XML;
|
||||||
} else if ("form".equalsIgnoreCase(value)) {
|
} else if ("form".equals(value)) {
|
||||||
encoding = ENCODE_FORM;
|
encoding = ENCODE_FORM;
|
||||||
} else if ("url".equalsIgnoreCase(value)) {
|
} else if ("url".equals(value)) {
|
||||||
encoding = ENCODE_URL;
|
encoding = ENCODE_URL;
|
||||||
} else if ("all".equalsIgnoreCase(value)) {
|
} else if ("all".equals(value)) {
|
||||||
encoding = ENCODE_ALL;
|
encoding = ENCODE_ALL;
|
||||||
|
} else {
|
||||||
|
app.logEvent("Unrecognized encoding in skin macro: " + value);
|
||||||
}
|
}
|
||||||
} else if ("default".equals(name)) {
|
} else if ("default".equals(name)) {
|
||||||
defaultRenderParams.defaultValue = value;
|
standardParams.defaultValue = value;
|
||||||
|
} else if ("failmode".equals(name)) {
|
||||||
|
standardParams.setFailMode(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add parameter to parameter map
|
// Add parameter to parameter map
|
||||||
|
@ -430,94 +509,29 @@ public final class Skin {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((sandbox != null) && !sandbox.contains(fullName)) {
|
if ((sandbox != null) && !sandbox.contains(name)) {
|
||||||
reval.getResponse().write("[Macro " + fullName + " not allowed in sandbox]");
|
throw new RuntimeException("Macro " + name + " not allowed in sandbox");
|
||||||
|
|
||||||
return;
|
|
||||||
} else if ("response".equalsIgnoreCase(handler)) {
|
|
||||||
renderFromResponse(reval);
|
|
||||||
|
|
||||||
return;
|
|
||||||
} else if ("request".equalsIgnoreCase(handler)) {
|
|
||||||
renderFromRequest(reval);
|
|
||||||
|
|
||||||
return;
|
|
||||||
} else if ("session".equalsIgnoreCase(handler)) {
|
|
||||||
renderFromSession(reval);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
|
||||||
Object handlerObject = null;
|
Object handlerObject = null;
|
||||||
|
|
||||||
// flag to tell whether we found our invocation target object
|
try {
|
||||||
boolean objectFound = true;
|
|
||||||
|
|
||||||
if (handler != null) {
|
if (handler != HANDLER_GLOBAL) {
|
||||||
// try to get handler from handlerCache first
|
handlerObject = resolveHandler(thisObject, reval, handlerCache);
|
||||||
if (handlerCache != null) {
|
handlerObject = resolvePath(handlerObject, reval);
|
||||||
handlerObject = handlerCache.get(handler);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (handlerObject == null) {
|
if (handler == HANDLER_GLOBAL || handlerObject != null) {
|
||||||
// if handler object wasn't found in cache retrieve it
|
|
||||||
if ((handlerObject == null) && (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 (!handler.equals("this") &&
|
|
||||||
!handler.equalsIgnoreCase(app.getPrototypeName(thisObject))) {
|
|
||||||
// the handler object is not what we want
|
|
||||||
Object n = thisObject;
|
|
||||||
|
|
||||||
// walk down parent chain to find handler object
|
|
||||||
while (n != null) {
|
|
||||||
Prototype proto = app.getPrototype(n);
|
|
||||||
|
|
||||||
if ((proto != null) && proto.isInstanceOf(handler)) {
|
|
||||||
handlerObject = n;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
n = app.getParentElement(n);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// we already have the right handler object
|
|
||||||
handlerObject = thisObject;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (handlerObject == null) {
|
|
||||||
// eiter because thisObject == null or the right object wasn't found
|
|
||||||
// in the object's parent path. Check if a matching macro handler
|
|
||||||
// is registered with the response object (res.handlers).
|
|
||||||
handlerObject = reval.getResponse().getMacroHandlers().get(handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
// the macro handler object couldn't be found
|
|
||||||
if (handlerObject == null) {
|
|
||||||
objectFound = false;
|
|
||||||
}
|
|
||||||
// else put the found handler object into the cache so we don't have to look again
|
|
||||||
else if (handlerCache != null) {
|
|
||||||
handlerCache.put(handler, handlerObject);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// this is a global macro with no handler specified
|
|
||||||
handlerObject = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (objectFound) {
|
|
||||||
// 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.
|
||||||
String funcName = name + "_macro";
|
String propName = path[path.length - 1];
|
||||||
|
String funcName = propName + "_macro";
|
||||||
|
|
||||||
if (reval.scriptingEngine.hasFunction(handlerObject, funcName)) {
|
if (reval.scriptingEngine.hasFunction(handlerObject, funcName)) {
|
||||||
StringBuffer buffer = reval.getResponse().getBuffer();
|
StringBuffer buffer = reval.getResponse().getBuffer();
|
||||||
RenderParameters renderParams = defaultRenderParams;
|
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();
|
||||||
|
@ -528,67 +542,100 @@ public final class Skin {
|
||||||
|
|
||||||
if (params == null) {
|
if (params == null) {
|
||||||
arguments = new Object[] { new SystemMap(4) };
|
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 {
|
} else {
|
||||||
wrappedParams = new CopyOnWriteMap(params);
|
wrappedParams = new CopyOnWriteMap(params);
|
||||||
arguments = new Object[] { wrappedParams };
|
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
|
// if parameter map was modified create new renderParams to override defaults
|
||||||
if (wrappedParams != null && wrappedParams.wasModified()) {
|
if (wrappedParams != null && wrappedParams.wasModified()) {
|
||||||
renderParams = new RenderParameters(wrappedParams);
|
stdParams = new StandardParams(wrappedParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if this macro has a filter chain
|
||||||
|
if (filterChain != null) {
|
||||||
|
if (value == null && buffer.length() > bufLength) {
|
||||||
|
value = buffer.substring(bufLength);
|
||||||
|
buffer.setLength(bufLength);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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, buffer, renderParams, true);
|
writeResponse(value, reval, thisObject, handlerCache, 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, buffer, renderParams, false);
|
writeResponse(output, reval, thisObject, handlerCache, stdParams, false);
|
||||||
} else {
|
} else {
|
||||||
// insert prefix,
|
// insert prefix,
|
||||||
if (renderParams.prefix != null) {
|
if (stdParams.prefix != null) {
|
||||||
buffer.insert(bufLength, renderParams.prefix);
|
buffer.insert(bufLength, stdParams.prefix);
|
||||||
}
|
}
|
||||||
// append suffix
|
// append suffix
|
||||||
if (renderParams.suffix != null) {
|
if (stdParams.suffix != null) {
|
||||||
buffer.append(renderParams.suffix);
|
buffer.append(stdParams.suffix);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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, buffer, renderParams, false);
|
writeResponse(value, reval, thisObject, handlerCache, stdParams, false);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// for unhandled global macros display error message,
|
if (handler == HANDLER_RESPONSE) {
|
||||||
// otherwise try property lookup
|
// some special handling for response handler
|
||||||
if (handlerObject == null) {
|
Object value = null;
|
||||||
String msg = "[Macro unhandled: " + fullName + "]";
|
if ("message".equals(propName)) {
|
||||||
|
value = reval.getResponse().getMessage();
|
||||||
|
} else if ("error".equals(propName)) {
|
||||||
|
value = reval.getResponse().getError();
|
||||||
|
}
|
||||||
|
if (value != null) {
|
||||||
|
writeResponse(value, reval, thisObject, handlerCache, standardParams, true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// display error message unless silent failmode is on
|
||||||
|
if (handlerObject == null || !hasProperty(handlerObject, propName, reval)) {
|
||||||
|
if (!standardParams.silentFailMode) {
|
||||||
|
String msg = "[Macro unhandled: " + name + "]";
|
||||||
reval.getResponse().write(" " + msg + " ");
|
reval.getResponse().write(" " + msg + " ");
|
||||||
app.logEvent(msg);
|
app.logEvent(msg);
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
Object value = reval.scriptingEngine.get(handlerObject, name);
|
Object value = getProperty(handlerObject, propName, reval);
|
||||||
writeResponse(value, reval.getResponse().getBuffer(), defaultRenderParams, true);
|
writeResponse(value, reval, thisObject, handlerCache, standardParams, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
String msg = "[Macro unhandled: " + fullName + "]";
|
if (!standardParams.silentFailMode) {
|
||||||
|
String msg = "[Macro unhandled: " + name + "]";
|
||||||
reval.getResponse().write(" " + msg + " ");
|
reval.getResponse().write(" " + msg + " ");
|
||||||
app.logEvent(msg);
|
app.logEvent(msg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (RedirectException redir) {
|
} catch (RedirectException redir) {
|
||||||
throw redir;
|
throw redir;
|
||||||
|
@ -601,98 +648,176 @@ public final class Skin {
|
||||||
if ((msg == null) || (msg.length() < 10)) {
|
if ((msg == null) || (msg.length() < 10)) {
|
||||||
msg = x.toString();
|
msg = x.toString();
|
||||||
}
|
}
|
||||||
msg = new StringBuffer("Macro error in ").append(fullName)
|
msg = new StringBuffer("Macro error in ").append(name)
|
||||||
.append(": ").append(msg).toString();
|
.append(": ").append(msg).toString();
|
||||||
reval.getResponse().write(" [" + msg + "] ");
|
reval.getResponse().write(" [" + msg + "] ");
|
||||||
app.logError(msg, x);
|
app.logError(msg, x);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderFromResponse(RequestEvaluator reval)
|
private Object invokeFilter(RequestEvaluator reval, Object returnValue,
|
||||||
throws UnsupportedEncodingException {
|
Object thisObject, Map handlerCache)
|
||||||
Object value = null;
|
throws Exception {
|
||||||
|
|
||||||
if ("message".equals(name)) {
|
if ((sandbox != null) && !sandbox.contains(name)) {
|
||||||
value = reval.getResponse().getMessage();
|
throw new RuntimeException("Macro " + name + " not allowed in sandbox");
|
||||||
} else if ("error".equals(name)) {
|
}
|
||||||
value = reval.getResponse().getError();
|
Object handlerObject = null;
|
||||||
|
|
||||||
|
if (handler != HANDLER_GLOBAL) {
|
||||||
|
handlerObject = resolveHandler(thisObject, reval, handlerCache);
|
||||||
|
handlerObject = resolvePath(handlerObject, reval);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value == null) {
|
String funcName = name + "_filter";
|
||||||
value = reval.getResponse().get(name);
|
|
||||||
|
if (reval.scriptingEngine.hasFunction(handlerObject, funcName)) {
|
||||||
|
// pass a clone/copy of the parameter map so if the script changes it,
|
||||||
|
CopyOnWriteMap wrappedParams = null;
|
||||||
|
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 };
|
||||||
writeResponse(value, reval.getResponse().getBuffer(), defaultRenderParams, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void renderFromRequest(RequestEvaluator reval)
|
|
||||||
throws UnsupportedEncodingException {
|
|
||||||
if (reval.getRequest() == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Object value = reval.getRequest().get(name);
|
|
||||||
|
|
||||||
writeResponse(value, reval.getResponse().getBuffer(), defaultRenderParams, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void renderFromSession(RequestEvaluator reval)
|
|
||||||
throws UnsupportedEncodingException {
|
|
||||||
if (reval.getSession() == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Object value = reval.getSession().getCacheNode().getString(name);
|
|
||||||
|
|
||||||
writeResponse(value, reval.getResponse().getBuffer(), defaultRenderParams, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void renderFromParam(RequestEvaluator reval, Map paramObject)
|
|
||||||
throws UnsupportedEncodingException {
|
|
||||||
if (paramObject == null) {
|
|
||||||
reval.getResponse().write("[Macro error: Skin requires a parameter object]");
|
|
||||||
} else {
|
} else {
|
||||||
Object value = paramObject.get(name);
|
wrappedParams = new CopyOnWriteMap(params);
|
||||||
|
arguments = new Object[] { returnValue, wrappedParams };
|
||||||
|
}
|
||||||
|
Object retval = reval.invokeDirectFunction(handlerObject,
|
||||||
|
funcName,
|
||||||
|
arguments);
|
||||||
|
if (filterChain == null) {
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
return filterChain.invokeFilter(reval, retval, thisObject, handlerCache);
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException("[Undefined Filter: " + name + "]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
writeResponse(value, reval.getResponse().getBuffer(), defaultRenderParams, true);
|
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, RequestEvaluator reval) {
|
||||||
|
for (int i = 1; i < path.length - 1; i++) {
|
||||||
|
handler = getProperty(handler, path[i], reval);
|
||||||
|
if (handler == null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object getProperty(Object obj, String name,
|
||||||
|
RequestEvaluator reval) {
|
||||||
|
if (obj instanceof Map) {
|
||||||
|
return ((Map) obj).get(name);
|
||||||
|
} else {
|
||||||
|
return reval.getScriptingEngine().getProperty(obj, name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasProperty(Object obj, String name, RequestEvaluator reval) {
|
||||||
|
if (obj instanceof Map) {
|
||||||
|
return ((Map) obj).containsKey(name);
|
||||||
|
} else {
|
||||||
|
return reval.getScriptingEngine().hasProperty(obj, name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility method for writing text out to the response object.
|
* Utility method for writing text out to the response object.
|
||||||
*/
|
*/
|
||||||
void writeResponse(Object value, StringBuffer buffer,
|
void writeResponse(Object value, RequestEvaluator reval,
|
||||||
RenderParameters renderParams, boolean useDefault)
|
Object thisObject, Map handlerCache,
|
||||||
throws UnsupportedEncodingException {
|
StandardParams stdParams, boolean useDefault)
|
||||||
|
throws Exception {
|
||||||
String text;
|
String text;
|
||||||
|
StringBuffer buffer = reval.getResponse().getBuffer();
|
||||||
|
|
||||||
|
// invoke filter chain if defined
|
||||||
|
if (filterChain != null) {
|
||||||
|
value = filterChain.invokeFilter(reval, value, thisObject, handlerCache);
|
||||||
|
}
|
||||||
|
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
if (useDefault) {
|
if (useDefault) {
|
||||||
text = renderParams.defaultValue;
|
text = (String) stdParams.defaultValue;
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// do not render doubles as doubles unless
|
text = reval.getScriptingEngine().toString(value);
|
||||||
// they actually have a decimal place. This is necessary because
|
|
||||||
// all numbers are handled as Double in JavaScript.
|
|
||||||
if (value instanceof Double) {
|
|
||||||
Double d = (Double) value;
|
|
||||||
if (d.longValue() == d.doubleValue()) {
|
|
||||||
text = Long.toString(d.longValue());
|
|
||||||
} else {
|
|
||||||
text = d.toString();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
text = value.toString();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((text != null) && (text.length() > 0)) {
|
if ((text != null) && (text.length() > 0)) {
|
||||||
// only write prefix/suffix if value is not null, if we write the default
|
// only write prefix/suffix if value is not null, if we write the default
|
||||||
// value provided by the macro tag, we assume it's already complete
|
// value provided by the macro tag, we assume it's already complete
|
||||||
if (renderParams.prefix != null && value != null) {
|
if (stdParams.prefix != null && value != null) {
|
||||||
buffer.append(renderParams.prefix);
|
buffer.append(stdParams.prefix);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (encoding) {
|
switch (encoding) {
|
||||||
|
@ -727,36 +852,65 @@ public final class Skin {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (renderParams.suffix != null && value != null) {
|
if (stdParams.suffix != null && value != null) {
|
||||||
buffer.append(renderParams.suffix);
|
buffer.append(stdParams.suffix);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "[Macro: " + fullName + "]";
|
return "[Macro: " + name + "]";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the full name of the macro in handler.name notation
|
* Return the full name of the macro in handler.name notation
|
||||||
|
* @return the macro name
|
||||||
*/
|
*/
|
||||||
public String getFullName() {
|
public String getName() {
|
||||||
return fullName;
|
return name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class RenderParameters {
|
class StandardParams {
|
||||||
String prefix = null;
|
Object prefix = null;
|
||||||
String suffix = null;
|
Object suffix = null;
|
||||||
String defaultValue = null;
|
Object defaultValue = null;
|
||||||
|
boolean silentFailMode = false;
|
||||||
|
|
||||||
RenderParameters() {
|
StandardParams() {}
|
||||||
|
|
||||||
|
StandardParams(Map map) {
|
||||||
|
prefix = map.get("prefix");
|
||||||
|
suffix = map.get("suffix");
|
||||||
|
defaultValue = map.get("default");
|
||||||
|
silentFailMode = "silent".equals(map.get("failmode"));
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean containsMacros() {
|
||||||
|
return prefix instanceof Macro
|
||||||
|
|| suffix instanceof Macro
|
||||||
|
|| defaultValue instanceof Macro;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setFailMode(Object value) {
|
||||||
|
if ("silent".equals(value))
|
||||||
|
silentFailMode = true;
|
||||||
|
else if ("verbose".equals(value))
|
||||||
|
silentFailMode = false;
|
||||||
|
else
|
||||||
|
app.logEvent("unrecognized failmode value: " + value);
|
||||||
|
}
|
||||||
|
|
||||||
|
StandardParams render(RequestEvaluator reval, Object thisObj, Map handlerCache)
|
||||||
|
throws UnsupportedEncodingException {
|
||||||
|
if (!containsMacros())
|
||||||
|
return this;
|
||||||
|
StandardParams stdParams = new StandardParams();
|
||||||
|
stdParams.prefix = renderMacro(prefix, reval, thisObj, handlerCache);
|
||||||
|
stdParams.suffix = renderMacro(suffix, reval, thisObj, handlerCache);
|
||||||
|
stdParams.defaultValue = renderMacro(defaultValue, reval, thisObj, handlerCache);
|
||||||
|
return stdParams;
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderParameters(Map map) {
|
|
||||||
prefix = (String) map.get("prefix");
|
|
||||||
suffix = (String) map.get("suffix");
|
|
||||||
defaultValue = (String) map.get("default");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -109,14 +109,35 @@ public interface ScriptingEngine {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get a property on an object
|
* Get a property on an object
|
||||||
|
* @param thisObject the object
|
||||||
|
* @param key the property name
|
||||||
|
* @return true the property value, or null
|
||||||
*/
|
*/
|
||||||
public Object get(Object thisObject, String key);
|
public Object getProperty(Object thisObject, String key);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return true if a function by that name is defined for that object.
|
* Return true if a function by that name is defined for that object.
|
||||||
|
* @param thisObject the object
|
||||||
|
* @param functionName the function name
|
||||||
|
* @return true if the function is defined on the object
|
||||||
*/
|
*/
|
||||||
public boolean hasFunction(Object thisObject, String functionName);
|
public boolean hasFunction(Object thisObject, String functionName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if a property by that name is defined for that object.
|
||||||
|
* @param thisObject the object
|
||||||
|
* @param propertyName the property name
|
||||||
|
* @return true if the function is defined on the object
|
||||||
|
*/
|
||||||
|
public boolean hasProperty(Object thisObject, String propertyName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a string representation for the given object
|
||||||
|
* @param obj an object
|
||||||
|
* @return a string representing the object
|
||||||
|
*/
|
||||||
|
public String toString(Object obj);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an IPathElement that offers introspection services into the application.
|
* Get an IPathElement that offers introspection services into the application.
|
||||||
* If this method returns null, no introspection is available for this kind of engine.
|
* If this method returns null, no introspection is available for this kind of engine.
|
||||||
|
|
|
@ -255,7 +255,7 @@ public class RhinoEngine implements ScriptingEngine {
|
||||||
}
|
}
|
||||||
Object f = ScriptableObject.getProperty(obj, functionName);
|
Object f = ScriptableObject.getProperty(obj, functionName);
|
||||||
|
|
||||||
if ((f == ScriptableObject.NOT_FOUND) || !(f instanceof Function)) {
|
if (!(f instanceof Function)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -353,51 +353,69 @@ public class RhinoEngine implements ScriptingEngine {
|
||||||
|
|
||||||
Object func = ScriptableObject.getProperty(op, fname);
|
Object func = ScriptableObject.getProperty(op, fname);
|
||||||
|
|
||||||
return func instanceof Function;
|
return func instanceof Callable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if an object has a value property defined with that name.
|
||||||
|
*/
|
||||||
|
public boolean hasProperty(Object obj, String propname) {
|
||||||
|
if ((obj == null) || (propname == null)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
String prototypeName = app.getPrototypeName(obj);
|
||||||
|
if ("user".equalsIgnoreCase(prototypeName)
|
||||||
|
&& "password".equalsIgnoreCase(propname)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// if this is a HopObject, check if the property is defined
|
||||||
|
// in the type.properties db-mapping.
|
||||||
|
if (obj instanceof INode && ! "hopobject".equalsIgnoreCase(prototypeName)) {
|
||||||
|
DbMapping dbm = app.getDbMapping(prototypeName);
|
||||||
|
if (dbm != null) {
|
||||||
|
Relation rel = dbm.propertyToRelation(propname);
|
||||||
|
return rel != null && (rel.isPrimitive() || rel.isCollection());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Scriptable wrapped = Context.toObject(obj, global);
|
||||||
|
return wrapped.has(propname, wrapped);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if an object has a defined property (public field if it
|
* Check if an object has a defined property (public field if it
|
||||||
* is a java object) with that name.
|
* is a java object) with that name.
|
||||||
*/
|
*/
|
||||||
public Object get(Object obj, String propname) {
|
public Object getProperty(Object obj, String propname) {
|
||||||
if ((obj == null) || (propname == null)) {
|
if ((obj == null) || (propname == null))
|
||||||
return null;
|
return null;
|
||||||
}
|
|
||||||
|
|
||||||
String prototypeName = app.getPrototypeName(obj);
|
|
||||||
|
|
||||||
if ("user".equalsIgnoreCase(prototypeName) &&
|
|
||||||
"password".equalsIgnoreCase(propname)) {
|
|
||||||
return "[macro access to password property not allowed]";
|
|
||||||
}
|
|
||||||
|
|
||||||
// if this is a HopObject, check if the property is defined
|
|
||||||
// in the type.properties db-mapping.
|
|
||||||
if (obj instanceof INode) {
|
|
||||||
DbMapping dbm = app.getDbMapping(prototypeName);
|
|
||||||
|
|
||||||
if (dbm != null) {
|
|
||||||
Relation rel = dbm.propertyToRelation(propname);
|
|
||||||
|
|
||||||
if ((rel == null) || !rel.isPrimitive()) {
|
|
||||||
return "[property \"" + propname + "\" is not defined for " +
|
|
||||||
prototypeName + "]";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Scriptable so = Context.toObject(obj, global);
|
Scriptable so = Context.toObject(obj, global);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Object prop = so.get(propname, so);
|
Object prop = so.get(propname, so);
|
||||||
|
|
||||||
if ((prop == null) || (prop == Undefined.instance)
|
if ((prop == null)
|
||||||
|
|| (prop == Undefined.instance)
|
||||||
|| (prop == ScriptableObject.NOT_FOUND)) {
|
|| (prop == ScriptableObject.NOT_FOUND)) {
|
||||||
return null;
|
return null;
|
||||||
} else if (prop instanceof Wrapper) {
|
} else if (prop instanceof Wrapper) {
|
||||||
return ((Wrapper) prop).unwrap();
|
return ((Wrapper) prop).unwrap();
|
||||||
} else {
|
} else {
|
||||||
|
return prop;
|
||||||
|
}
|
||||||
|
} catch (Exception esx) {
|
||||||
|
app.logError("Error getting property " + propname + ": " + esx);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a string representation for the given object
|
||||||
|
* @param obj an object
|
||||||
|
* @return a string representing the object
|
||||||
|
*/
|
||||||
|
public String toString(Object obj) {
|
||||||
// not all Rhino types convert to a string as expected
|
// not all Rhino types convert to a string as expected
|
||||||
// when calling toString() - try to do better by using
|
// when calling toString() - try to do better by using
|
||||||
// Rhino's ScriptRuntime.toString(). Note that this
|
// Rhino's ScriptRuntime.toString(). Note that this
|
||||||
|
@ -405,15 +423,11 @@ public class RhinoEngine implements ScriptingEngine {
|
||||||
// a string representation of the object - which is
|
// a string representation of the object - which is
|
||||||
// currently the case since it's only used in Skin rendering.
|
// currently the case since it's only used in Skin rendering.
|
||||||
try {
|
try {
|
||||||
return ScriptRuntime.toString(prop);
|
return ScriptRuntime.toString(obj);
|
||||||
} catch (Exception x) {
|
} catch (Exception x) {
|
||||||
// just return original property object
|
// just return original property object
|
||||||
}
|
}
|
||||||
return prop;
|
return obj.toString();
|
||||||
}
|
|
||||||
} catch (Exception esx) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Add table
Reference in a new issue