Implement new skin features:
* Set namespace for global macros using app.globalMacroPath * Implement macro parameter processing using app.processMacroParameter() callback and $(...) parameter syntax * Implement unhandled macro handling using unhandledMacro() callback * Implement deep macro lookup using getMacroHandler() callback, and drop allowDeepMacros app property * Allow access to HopObject properties that aren't defined in type.properties
This commit is contained in:
parent
7f58c102bf
commit
0559d2d53e
6 changed files with 221 additions and 90 deletions
|
@ -173,7 +173,16 @@ public final class Application implements Runnable {
|
|||
// Field to cache unmapped java classes
|
||||
private final static String CLASS_NOT_MAPPED = "(unmapped)";
|
||||
|
||||
protected boolean allowDeepMacros = false;
|
||||
|
||||
/**
|
||||
* Function object for macro processing callback
|
||||
*/
|
||||
Object processMacroParameter = null;
|
||||
|
||||
/**
|
||||
* Namespace search path for global macros
|
||||
*/
|
||||
String[] globalMacroPath = null;
|
||||
|
||||
/**
|
||||
* Simple constructor for dead application instances.
|
||||
|
@ -1641,15 +1650,7 @@ public final class Application implements Runnable {
|
|||
* for it in the class.properties file.
|
||||
*/
|
||||
public boolean isJavaPrototype(String typename) {
|
||||
for (Enumeration en = classMapping.elements(); en.hasMoreElements();) {
|
||||
String value = (String) en.nextElement();
|
||||
|
||||
if (typename.equals(value)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return classMapping.contains(typename);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1873,8 +1874,6 @@ public final class Application implements Runnable {
|
|||
((Logger) eventLog).setLogLevel(debug ? Logger.DEBUG : Logger.INFO);
|
||||
}
|
||||
|
||||
allowDeepMacros = "true".equalsIgnoreCase(props.getProperty("allowDeepMacros"));
|
||||
|
||||
// set prop read timestamp
|
||||
lastPropertyRead = props.lastModified();
|
||||
}
|
||||
|
|
|
@ -623,6 +623,42 @@ public class ApplicationBean implements Serializable {
|
|||
return app.getCharset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the path for global macro resolution
|
||||
* @param path an array of global namespaces, or null
|
||||
*/
|
||||
public void setGlobalMacroPath(String[] path) {
|
||||
app.globalMacroPath = path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path for global macro resolution
|
||||
* @return an array of global namespaces, or null
|
||||
*/
|
||||
public String[] getGlobalMacroPath() {
|
||||
return app.globalMacroPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a function for processing macro parameters formatted as $(value).
|
||||
* The function is expected to take the parameter value as string argument,
|
||||
* and return the processed parameter
|
||||
* @param obj a callback function
|
||||
*/
|
||||
public void setProcessMacroParameter(Object obj) {
|
||||
app.processMacroParameter = obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the function for processing macro parameters formatted as $(value).
|
||||
* The function is expected to take the parameter value as string argument,
|
||||
* and return the processed parameter
|
||||
* @return the current macro processor callback function or null
|
||||
*/
|
||||
public Object getProcessMacroParameter() {
|
||||
return app.processMacroParameter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger a synchronous Helma invocation with a default timeout of 30 seconds.
|
||||
*
|
||||
|
|
|
@ -179,8 +179,7 @@ public final class RequestEvaluator implements Runnable {
|
|||
}
|
||||
}
|
||||
// If function doesn't exist, return immediately
|
||||
if (functionName != null && functionName.indexOf('.') < 0 &&
|
||||
!scriptingEngine.hasFunction(thisObject, functionName)) {
|
||||
if (functionName != null && !scriptingEngine.hasFunction(thisObject, functionName, true)) {
|
||||
app.logEvent(missingFunctionMessage(thisObject, functionName));
|
||||
done = true;
|
||||
reqtype = NONE;
|
||||
|
@ -448,7 +447,7 @@ public final class RequestEvaluator implements Runnable {
|
|||
|
||||
// reset skin recursion detection counter
|
||||
skinDepth = 0;
|
||||
if (!scriptingEngine.hasFunction(currentElement, functionName)) {
|
||||
if (!scriptingEngine.hasFunction(currentElement, functionName, false)) {
|
||||
throw new FrameworkException(missingFunctionMessage(currentElement, functionName));
|
||||
}
|
||||
result = scriptingEngine.invoke(currentElement,
|
||||
|
@ -486,10 +485,6 @@ public final class RequestEvaluator implements Runnable {
|
|||
// reset skin recursion detection counter
|
||||
skinDepth = 0;
|
||||
|
||||
if (functionName != null && !scriptingEngine.hasFunction(thisObject, functionName)) {
|
||||
throw new FrameworkException(missingFunctionMessage(thisObject, functionName));
|
||||
}
|
||||
|
||||
result = scriptingEngine.invoke(thisObject,
|
||||
function,
|
||||
args,
|
||||
|
@ -868,7 +863,7 @@ public final class RequestEvaluator implements Runnable {
|
|||
public Object invokeDirectFunction(Object obj, Object function, Object[] args)
|
||||
throws Exception {
|
||||
return scriptingEngine.invoke(obj, function, args,
|
||||
ScriptingEngine.ARGS_WRAP_DEFAULT, false);
|
||||
ScriptingEngine.ARGS_WRAP_DEFAULT, true);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -993,7 +988,7 @@ public final class RequestEvaluator implements Runnable {
|
|||
* @throws ScriptingException
|
||||
*/
|
||||
private Object getChildElement(Object obj, String name) throws ScriptingException {
|
||||
if (scriptingEngine.hasFunction(obj, "getChildElement")) {
|
||||
if (scriptingEngine.hasFunction(obj, "getChildElement", false)) {
|
||||
return scriptingEngine.invoke(obj, "getChildElement", new Object[] {name},
|
||||
ScriptingEngine.ARGS_WRAP_DEFAULT, false);
|
||||
}
|
||||
|
@ -1037,7 +1032,7 @@ public final class RequestEvaluator implements Runnable {
|
|||
if (req.checkXmlRpc()) {
|
||||
// append _methodname
|
||||
buffer.append("_xmlrpc");
|
||||
if (scriptingEngine.hasFunction(obj, buffer.toString())) {
|
||||
if (scriptingEngine.hasFunction(obj, buffer.toString(), false)) {
|
||||
// handle as XML-RPC request
|
||||
req.setMethod(RequestTrans.XMLRPC);
|
||||
return buffer.toString();
|
||||
|
@ -1051,7 +1046,7 @@ public final class RequestEvaluator implements Runnable {
|
|||
if (method != null) {
|
||||
// append _methodname
|
||||
buffer.append('_').append(method.toLowerCase());
|
||||
if (scriptingEngine.hasFunction(obj, buffer.toString()))
|
||||
if (scriptingEngine.hasFunction(obj, buffer.toString(), false))
|
||||
return buffer.toString();
|
||||
|
||||
// cut off method in case it has been appended
|
||||
|
@ -1062,7 +1057,7 @@ public final class RequestEvaluator implements Runnable {
|
|||
if (method == null || "GET".equalsIgnoreCase(method) ||
|
||||
"POST".equalsIgnoreCase(method) ||
|
||||
"HEAD".equalsIgnoreCase(method)) {
|
||||
if (scriptingEngine.hasFunction(obj, buffer.toString()))
|
||||
if (scriptingEngine.hasFunction(obj, buffer.toString(), false))
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
|
|
|
@ -319,10 +319,12 @@ public final class Skin {
|
|||
sandbox.add(macroname);
|
||||
}
|
||||
|
||||
private Object invokeMacro(Object value, RenderContext cx)
|
||||
private Object processParameter(Object value, RenderContext cx)
|
||||
throws Exception {
|
||||
if (value instanceof Macro) {
|
||||
return ((Macro) value).invokeAsMacro(cx, null);
|
||||
} else if (value instanceof ProcessedParameter) {
|
||||
return ((ProcessedParameter) value).process(cx.reval);
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
|
@ -376,7 +378,6 @@ public final class Skin {
|
|||
if (state == PARSE_PARAM && quotechar == '\u0000'
|
||||
&& b.length() == 0 && source[i + 1] == '%') {
|
||||
Macro macro = new Macro(i, 2);
|
||||
hasNestedMacros = true;
|
||||
addParameter(lastParamName, macro);
|
||||
lastParamName = null;
|
||||
b.setLength(0);
|
||||
|
@ -455,7 +456,7 @@ public final class Skin {
|
|||
if (!escape && state == PARSE_PARAM) {
|
||||
if (quotechar == source[i]) {
|
||||
// add parameter
|
||||
addParameter(lastParamName, b.toString());
|
||||
addParameter(lastParamName, parseParameter(b.toString()));
|
||||
lastParamName = null;
|
||||
b.setLength(0);
|
||||
quotechar = '\u0000';
|
||||
|
@ -487,7 +488,7 @@ public final class Skin {
|
|||
if (quotechar == '\u0000') {
|
||||
if (b.length() > 0) {
|
||||
// add parameter
|
||||
addParameter(lastParamName, b.toString());
|
||||
addParameter(lastParamName, parseParameter(b.toString()));
|
||||
lastParamName = null;
|
||||
b.setLength(0);
|
||||
}
|
||||
|
@ -533,7 +534,7 @@ public final class Skin {
|
|||
if (name == null) {
|
||||
name = b.toString().trim();
|
||||
} else {
|
||||
addParameter(lastParamName, b.toString());
|
||||
addParameter(lastParamName, parseParameter(b.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -560,7 +561,21 @@ public final class Skin {
|
|||
}
|
||||
}
|
||||
|
||||
private Object parseParameter(String str) {
|
||||
int length = str.length();
|
||||
if (length > 3 && str.charAt(0) == '$') {
|
||||
if (str.charAt(1) == '[' && str.charAt(length - 1) == ']')
|
||||
return new ProcessedParameter(str.substring(2, str.length()-1), 0);
|
||||
else if (str.charAt(1) == '(' && str.charAt(length - 1) == ')')
|
||||
return new ProcessedParameter(str.substring(2, str.length()-1), 1);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
private void addParameter(String name, Object value) {
|
||||
if (!(value instanceof String)) {
|
||||
hasNestedMacros = true;
|
||||
}
|
||||
if (name == null) {
|
||||
// take shortcut for positional parameters
|
||||
if (positionalParams == null) {
|
||||
|
@ -613,31 +628,32 @@ public final class Skin {
|
|||
throw new RuntimeException("Macro " + name + " not allowed in sandbox");
|
||||
}
|
||||
|
||||
Object handlerObject = null;
|
||||
Object handler = null;
|
||||
Object value = null;
|
||||
ScriptingEngine engine = cx.reval.scriptingEngine;
|
||||
|
||||
if (handlerType != HANDLER_GLOBAL) {
|
||||
handlerObject = cx.resolveHandler(path[0], handlerType);
|
||||
handlerObject = resolvePath(handlerObject, engine);
|
||||
handler = cx.resolveHandler(path[0], handlerType);
|
||||
handler = resolvePath(handler, cx.reval);
|
||||
}
|
||||
|
||||
if (handlerType == HANDLER_GLOBAL || handlerObject != null) {
|
||||
if (handlerType == HANDLER_GLOBAL || handler != null) {
|
||||
// check if a function called name_macro is defined.
|
||||
// if so, the macro evaluates to the function. Otherwise,
|
||||
// a property/field with the name is used, if defined.
|
||||
String propName = path[path.length - 1];
|
||||
String funcName = propName + "_macro";
|
||||
String funcName = resolveFunctionName(handler, propName + "_macro", engine);
|
||||
|
||||
if (engine.hasFunction(handlerObject, funcName)) {
|
||||
StringBuffer buffer = cx.reval.getResponse().getBuffer();
|
||||
// remember length of response buffer before calling macro
|
||||
int bufLength = buffer.length();
|
||||
// remember length of response buffer before calling macro
|
||||
StringBuffer buffer = cx.reval.getResponse().getBuffer();
|
||||
int bufLength = buffer.length();
|
||||
|
||||
if (funcName != null) {
|
||||
|
||||
Object[] arguments = prepareArguments(0, cx);
|
||||
// get reference to rendered named params for after invocation
|
||||
Map params = (Map) arguments[0];
|
||||
value = cx.reval.invokeDirectFunction(handlerObject,
|
||||
value = cx.reval.invokeDirectFunction(handler,
|
||||
funcName,
|
||||
arguments);
|
||||
|
||||
|
@ -662,16 +678,28 @@ public final class Skin {
|
|||
if (value != null)
|
||||
return filter(value, cx);
|
||||
}
|
||||
// display error message unless silent failmode is on
|
||||
if (!engine.hasProperty(handlerObject, propName)
|
||||
&& standardParams.verboseFailmode(handlerObject, engine)) {
|
||||
throw new MacroUnhandledException(name);
|
||||
// display error message unless unhandledMacro is defined or silent failmode is on
|
||||
if (!engine.hasProperty(handler, propName)) {
|
||||
if (engine.hasFunction(handler, "unhandledMacro", false)) {
|
||||
Object[] arguments = prepareArguments(1, cx);
|
||||
arguments[0] = propName;
|
||||
value = cx.reval.invokeDirectFunction(handler, "unhandledMacro", arguments);
|
||||
// if macro has a filter chain and didn't return anything, use output
|
||||
// as filter argument.
|
||||
if (filterChain != null && value == null && buffer.length() > bufLength) {
|
||||
value = buffer.substring(bufLength);
|
||||
buffer.setLength(bufLength);
|
||||
}
|
||||
} else if (standardParams.verboseFailmode(handler, engine)) {
|
||||
throw new UnhandledMacroException(name);
|
||||
}
|
||||
} else {
|
||||
value = engine.getProperty(handler, propName);
|
||||
}
|
||||
value = engine.getProperty(handlerObject, propName);
|
||||
return filter(value, cx);
|
||||
}
|
||||
} else if (standardParams.verboseFailmode(handlerObject, engine)) {
|
||||
throw new MacroUnhandledException(name);
|
||||
} else if (standardParams.verboseFailmode(handler, engine)) {
|
||||
throw new UnhandledMacroException(name);
|
||||
}
|
||||
return filter(null, cx);
|
||||
}
|
||||
|
@ -723,8 +751,8 @@ public final class Skin {
|
|||
throw concur;
|
||||
} catch (TimeoutException timeout) {
|
||||
throw timeout;
|
||||
} catch (MacroUnhandledException unhandled) {
|
||||
String msg = "Macro unhandled: " + unhandled.getMessage();
|
||||
} catch (UnhandledMacroException unhandled) {
|
||||
String msg = "Unhandled Macro: " + unhandled.getMessage();
|
||||
cx.reval.getResponse().write(" [" + msg + "] ");
|
||||
app.logError(msg);
|
||||
} catch (Exception x) {
|
||||
|
@ -761,12 +789,14 @@ public final class Skin {
|
|||
|
||||
if (handlerType != HANDLER_GLOBAL) {
|
||||
handlerObject = cx.resolveHandler(path[0], handlerType);
|
||||
handlerObject = resolvePath(handlerObject, cx.reval.scriptingEngine);
|
||||
handlerObject = resolvePath(handlerObject, cx.reval);
|
||||
}
|
||||
|
||||
String funcName = path[path.length - 1] + "_filter";
|
||||
String propName = path[path.length - 1] + "_filter";
|
||||
String funcName = resolveFunctionName(handlerObject, propName,
|
||||
cx.reval.scriptingEngine);
|
||||
|
||||
if (cx.reval.scriptingEngine.hasFunction(handlerObject, funcName)) {
|
||||
if (funcName != null) {
|
||||
Object[] arguments = prepareArguments(1, cx);
|
||||
arguments[0] = returnValue;
|
||||
Object retval = cx.reval.invokeDirectFunction(handlerObject,
|
||||
|
@ -791,7 +821,9 @@ public final class Skin {
|
|||
for (Iterator it = namedParams.entrySet().iterator(); it.hasNext(); ) {
|
||||
Map.Entry entry = (Map.Entry) it.next();
|
||||
Object value = entry.getValue();
|
||||
map.put(entry.getKey(), invokeMacro(value, cx));
|
||||
if (!(value instanceof String))
|
||||
value = processParameter(value, cx);
|
||||
map.put(entry.getKey(), value);
|
||||
}
|
||||
arguments[offset] = map;
|
||||
} else {
|
||||
|
@ -800,30 +832,54 @@ public final class Skin {
|
|||
}
|
||||
if (positionalParams != null) {
|
||||
for (int i = 0; i < nPosArgs; i++) {
|
||||
Object param = positionalParams.get(i);
|
||||
if (param instanceof Macro)
|
||||
arguments[offset + 1 + i] = invokeMacro(param, cx);
|
||||
else
|
||||
arguments[offset + 1 + i] = param;
|
||||
Object value = positionalParams.get(i);
|
||||
if (!(value instanceof String))
|
||||
value = processParameter(value, cx);
|
||||
arguments[offset + 1 + i] = value;
|
||||
}
|
||||
}
|
||||
return arguments;
|
||||
}
|
||||
|
||||
private Object resolvePath(Object handler, ScriptingEngine engine) {
|
||||
if (path.length > 2 && !app.allowDeepMacros) {
|
||||
throw new RuntimeException("allowDeepMacros property must be true " +
|
||||
"in order to enable deep macro paths.");
|
||||
}
|
||||
private Object resolvePath(Object handler, RequestEvaluator reval) throws Exception {
|
||||
for (int i = 1; i < path.length - 1; i++) {
|
||||
handler = engine.getProperty(handler, path[i]);
|
||||
if (handler == null) {
|
||||
break;
|
||||
Object[] arguments = {path[i]};
|
||||
Object next = reval.invokeDirectFunction(handler, "getMacroHandler", arguments);
|
||||
if (next != null) {
|
||||
handler = next;
|
||||
} else if (!reval.scriptingEngine.isTypedObject(handler)) {
|
||||
handler = reval.scriptingEngine.getProperty(handler, path[i]);
|
||||
if (handler == null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return handler;
|
||||
}
|
||||
|
||||
private String resolveFunctionName(Object handler, String functionName,
|
||||
ScriptingEngine engine) {
|
||||
if (handlerType == HANDLER_GLOBAL) {
|
||||
String[] macroPath = app.globalMacroPath;
|
||||
if (macroPath == null || macroPath.length == 0) {
|
||||
if (engine.hasFunction(null, functionName, false))
|
||||
return functionName;
|
||||
} else {
|
||||
for (int i = 0; i < macroPath.length; i++) {
|
||||
String path = macroPath[i];
|
||||
String funcName = path == null || path.length() == 0 ?
|
||||
functionName : path + "." + functionName;
|
||||
if (engine.hasFunction(null, funcName, true))
|
||||
return funcName;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (engine.hasFunction(handler, functionName, false))
|
||||
return functionName;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method for writing text out to the response object.
|
||||
*/
|
||||
|
@ -920,9 +976,9 @@ public final class Skin {
|
|||
}
|
||||
|
||||
boolean containsMacros() {
|
||||
return prefix instanceof Macro
|
||||
|| suffix instanceof Macro
|
||||
|| defaultValue instanceof Macro;
|
||||
return !(prefix instanceof String)
|
||||
|| !(suffix instanceof String)
|
||||
|| !(defaultValue instanceof String);
|
||||
}
|
||||
|
||||
void setFailMode(Object value) {
|
||||
|
@ -946,9 +1002,9 @@ public final class Skin {
|
|||
if (!containsMacros())
|
||||
return this;
|
||||
StandardParams stdParams = new StandardParams();
|
||||
stdParams.prefix = invokeMacro(prefix, cx);
|
||||
stdParams.suffix = invokeMacro(suffix, cx);
|
||||
stdParams.defaultValue = invokeMacro(defaultValue, cx);
|
||||
stdParams.prefix = processParameter(prefix, cx);
|
||||
stdParams.suffix = processParameter(suffix, cx);
|
||||
stdParams.defaultValue = processParameter(defaultValue, cx);
|
||||
return stdParams;
|
||||
}
|
||||
|
||||
|
@ -1019,13 +1075,39 @@ public final class Skin {
|
|||
return obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exception type for unhandled macros
|
||||
*/
|
||||
class MacroUnhandledException extends Exception {
|
||||
MacroUnhandledException(String name) {
|
||||
super(name);
|
||||
/**
|
||||
* Processed macro parameter
|
||||
*/
|
||||
class ProcessedParameter {
|
||||
String value;
|
||||
int type;
|
||||
|
||||
ProcessedParameter(String value, int type) {
|
||||
this.value = value;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
Object process(RequestEvaluator reval) throws Exception {
|
||||
switch (type) {
|
||||
case 1:
|
||||
Object function = app.processMacroParameter;
|
||||
Object[] args = {value};
|
||||
return reval.invokeDirectFunction(null, function, args);
|
||||
case 0:
|
||||
default:
|
||||
return reval.getResponse().getMacroHandlers().get(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exception type for unhandled macros
|
||||
*/
|
||||
class UnhandledMacroException extends Exception {
|
||||
UnhandledMacroException(String name) {
|
||||
super(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -119,9 +119,10 @@ public interface ScriptingEngine {
|
|||
* Return true if a function by that name is defined for that object.
|
||||
* @param thisObject the object
|
||||
* @param functionName the function name
|
||||
* @param resolve if member path in function name should be resolved
|
||||
* @return true if the function is defined on the object
|
||||
*/
|
||||
public boolean hasFunction(Object thisObject, String functionName);
|
||||
public boolean hasFunction(Object thisObject, String functionName, boolean resolve);
|
||||
|
||||
/**
|
||||
* Return true if a property by that name is defined for that object.
|
||||
|
|
|
@ -28,6 +28,7 @@ import helma.objectmodel.db.DbMapping;
|
|||
import helma.objectmodel.db.Relation;
|
||||
import helma.scripting.*;
|
||||
import helma.scripting.rhino.debug.Tracer;
|
||||
import helma.util.StringUtils;
|
||||
import org.mozilla.javascript.*;
|
||||
import org.mozilla.javascript.serialize.ScriptableOutputStream;
|
||||
import org.mozilla.javascript.serialize.ScriptableInputStream;
|
||||
|
@ -246,10 +247,9 @@ public class RhinoEngine implements ScriptingEngine {
|
|||
// otherwise replace dots with underscores.
|
||||
if (resolve) {
|
||||
if (funcName.indexOf('.') > 0) {
|
||||
StringTokenizer st = new StringTokenizer(funcName, ".");
|
||||
for (int i=0; i<st.countTokens()-1; i++) {
|
||||
String propName = st.nextToken();
|
||||
Object propValue = ScriptableObject.getProperty(obj, propName);
|
||||
String[] path = StringUtils.split(funcName, ".");
|
||||
for (int i = 0; i < path.length - 1; i++) {
|
||||
Object propValue = ScriptableObject.getProperty(obj, path[i]);
|
||||
if (propValue instanceof Scriptable) {
|
||||
obj = (Scriptable) propValue;
|
||||
} else {
|
||||
|
@ -257,7 +257,7 @@ public class RhinoEngine implements ScriptingEngine {
|
|||
funcName + " in " + thisObject);
|
||||
}
|
||||
}
|
||||
funcName = st.nextToken();
|
||||
funcName = path[path.length - 1];
|
||||
}
|
||||
} else {
|
||||
funcName = funcName.replace('.', '_');
|
||||
|
@ -357,9 +357,26 @@ public class RhinoEngine implements ScriptingEngine {
|
|||
* Check if an object has a function property (public method if it
|
||||
* is a java object) with that name.
|
||||
*/
|
||||
public boolean hasFunction(Object obj, String fname) {
|
||||
// Convert '.' to '_' in function name
|
||||
fname = fname.replace('.', '_');
|
||||
public boolean hasFunction(Object obj, String fname, boolean resolve) {
|
||||
if (resolve) {
|
||||
if (fname.indexOf('.') > 0) {
|
||||
Scriptable op = obj == null ? global : Context.toObject(obj, global);
|
||||
String[] path = StringUtils.split(fname, ".");
|
||||
for (int i = 0; i < path.length; i++) {
|
||||
Object value = ScriptableObject.getProperty(op, path[i]);
|
||||
if (value instanceof Scriptable) {
|
||||
op = (Scriptable) value;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return (op instanceof Function);
|
||||
}
|
||||
} else {
|
||||
// Convert '.' to '_' in function name
|
||||
fname = fname.replace('.', '_');
|
||||
}
|
||||
|
||||
// Treat HopObjects separately - otherwise we risk to fetch database
|
||||
// references/child objects just to check for function properties.
|
||||
if (obj instanceof INode) {
|
||||
|
@ -395,7 +412,8 @@ public class RhinoEngine implements ScriptingEngine {
|
|||
DbMapping dbm = app.getDbMapping(prototypeName);
|
||||
if (dbm != null) {
|
||||
Relation rel = dbm.propertyToRelation(propname);
|
||||
return rel != null && (rel.isPrimitive() || rel.isCollection());
|
||||
if (rel != null && (rel.isPrimitive() || rel.isCollection()))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Scriptable wrapped = Context.toObject(obj, global);
|
||||
|
@ -448,8 +466,8 @@ public class RhinoEngine implements ScriptingEngine {
|
|||
public boolean isTypedObject(Object obj) {
|
||||
if (obj == null || obj instanceof Map || obj instanceof NativeObject)
|
||||
return false;
|
||||
if (obj instanceof INode) {
|
||||
String protoName = app.getPrototypeName(obj);
|
||||
if (obj instanceof IPathElement) {
|
||||
String protoName = ((IPathElement) obj).getPrototype();
|
||||
return protoName != null && !"hopobject".equalsIgnoreCase(protoName);
|
||||
}
|
||||
// assume java object is typed
|
||||
|
|
Loading…
Add table
Reference in a new issue