* Implement subskins

* Fix skin failmode levels
* Add Resource.getOverloadedResource()
* Implement ScriptingEngine.isTypedObject(Object)
* Fix skin length bug with non-ASCII characters
This commit is contained in:
hns 2007-03-22 15:34:10 +00:00
parent 0b363a4b96
commit 4531ef6e4b
9 changed files with 315 additions and 87 deletions

View file

@ -324,10 +324,36 @@ public final class Prototype {
/** /**
* Get a skin for this prototype. This only works for skins * Get a skin for this prototype. This only works for skins
* residing in the prototype directory, not for skins files in * residing in the prototype directory, not for skins files in
* other locations or database stored skins. * other locations or database stored skins. If parentName and
* subName are defined, the skin may be a subskin of another skin.
*/ */
public Skin getSkin(String skinName) throws IOException { public Skin getSkin(String skinName, String parentName, String subName)
return skinMap.getSkin(skinName); throws IOException {
Skin skin = null;
Resource res = skinMap.getResource(skinName);
while (res != null) {
skin = Skin.getSkin(res, app);
if (skin.hasMainskin())
break;
res = res.getOverloadedResource();
}
if (parentName != null) {
Skin parentSkin = null;
Resource parent = skinMap.getResource(parentName);
while (parent != null) {
parentSkin = Skin.getSkin(parent, app);
if (parentSkin.hasSubskin(subName))
break;
parent = parent.getOverloadedResource();
}
if (parent != null) {
if (res != null && app.getResourceComparator().compare(res, parent) > 0)
return skin;
else
return parentSkin.getSubskin(subName);
}
}
return skin;
} }
/** /**
@ -494,6 +520,10 @@ public final class Prototype {
} }
} }
public Resource getResource(Object key) {
return (Resource) get(key);
}
public Object get(Object key) { public Object get(Object key) {
checkForUpdates(); checkForUpdates();
return super.get(key); return super.get(key);
@ -559,7 +589,8 @@ public final class Prototype {
// load Skins // load Skins
for (Iterator i = skins.iterator(); i.hasNext();) { for (Iterator i = skins.iterator(); i.hasNext();) {
Resource res = (Resource) i.next(); Resource res = (Resource) i.next();
super.put(res.getBaseName(), res); Resource prev = (Resource) super.put(res.getBaseName(), res);
res.setOverloadedResource(prev);
} }
// if skinpath is not null, overload/add skins from there // if skinpath is not null, overload/add skins from there
@ -594,7 +625,9 @@ public final class Prototype {
String name = skinNames[i].substring(0, skinNames[i].length() - 5); String name = skinNames[i].substring(0, skinNames[i].length() - 5);
File file = new File(dir, skinNames[i]); File file = new File(dir, skinNames[i]);
super.put(name, (new FileResource(file))); Resource res = new FileResource(file);
Resource prev = (Resource) super.put(name, res);
res.setOverloadedResource(prev);
} }
} }

View file

@ -33,6 +33,15 @@ 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 {
private Macro[] macros;
private Application app;
private char[] source;
private int offset, length; // start and end index of skin content
private HashSet sandbox;
private HashMap subskins;
private Skin parentSkin = this;
static private final int PARSE_MACRONAME = 0; static private final int PARSE_MACRONAME = 0;
static private final int PARSE_PARAM = 1; static private final int PARSE_PARAM = 1;
static private final int PARSE_DONE = 2; static private final int PARSE_DONE = 2;
@ -52,20 +61,19 @@ public final class Skin {
static private final int HANDLER_THIS = 5; static private final int HANDLER_THIS = 5;
static private final int HANDLER_OTHER = 6; static private final int HANDLER_OTHER = 6;
private Macro[] macros; static private final int FAIL_DEFAULT = 0;
private Application app; static private final int FAIL_SILENT = 1;
private char[] source; static private final int FAIL_VERBOSE = 2;
private int sourceLength;
private HashSet sandbox;
/** /**
* Create a skin without any restrictions on which macros are allowed to be called from it * Create a skin without any restrictions on which macros are allowed to be called from it
*/ */
public Skin(String content, Application app) { public Skin(String content, Application app) {
this.app = app; this.app = app;
sandbox = null; this.sandbox = null;
source = content.toCharArray(); this.source = content.toCharArray();
sourceLength = source.length; this.offset = 0;
this.length = source.length;
parse(); parse();
} }
@ -75,8 +83,9 @@ public final class Skin {
public Skin(String content, Application app, HashSet sandbox) { public Skin(String content, Application app, HashSet sandbox) {
this.app = app; this.app = app;
this.sandbox = sandbox; this.sandbox = sandbox;
source = content.toCharArray(); this.source = content.toCharArray();
sourceLength = source.length; this.offset = 0;
length = source.length;
parse(); parse();
} }
@ -86,8 +95,23 @@ public final class Skin {
public Skin(char[] content, int length, Application app) { public Skin(char[] content, int length, Application app) {
this.app = app; this.app = app;
this.sandbox = null; this.sandbox = null;
source = content; this.source = content;
sourceLength = length; this.offset = 0;
this.length = length;
parse();
}
/**
* Subskin constructor.
*/
private Skin(Skin parentSkin, Macro anchorMacro) {
this.parentSkin = parentSkin;
this.app = parentSkin.app;
this.sandbox = parentSkin.sandbox;
this.source = parentSkin.source;
this.offset = anchorMacro.end;
this.length = parentSkin.length;
parentSkin.addSubskin(anchorMacro.name, this);
parse(); parse();
} }
@ -113,7 +137,7 @@ public final class Skin {
} finally { } finally {
reader.close(); reader.close();
} }
return new Skin(characterBuffer, length, app); return new Skin(characterBuffer, read, app);
} }
/** /**
@ -123,11 +147,17 @@ public final class Skin {
ArrayList partBuffer = new ArrayList(); ArrayList partBuffer = new ArrayList();
boolean escape = false; boolean escape = false;
for (int i = 0; i < (sourceLength - 1); i++) { for (int i = offset; i < (length - 1); i++) {
if (source[i] == '<' && source[i + 1] == '%' && !escape) { if (source[i] == '<' && source[i + 1] == '%' && !escape) {
// found macro start tag // found macro start tag
Macro macro = new Macro(i, 2); Macro macro = new Macro(i, 2);
partBuffer.add(macro); if (macro.isSubskinMacro) {
new Skin(parentSkin, macro);
length = i;
break;
} else {
partBuffer.add(macro);
}
i = macro.end - 1; i = macro.end - 1;
} else { } else {
escape = source[i] == '\\' && !escape; escape = source[i] == '\\' && !escape;
@ -138,11 +168,54 @@ public final class Skin {
partBuffer.toArray(macros); partBuffer.toArray(macros);
} }
private void addSubskin(String name, Skin subskin) {
if (subskins == null) {
subskins = new HashMap();
}
subskins.put(name, subskin);
}
/**
* Check if this skin has a main skin, as opposed to consisting just of subskins
* @return true if this skin contains a main skin
*/
public boolean hasMainskin() {
return length - offset > 0;
}
/**
* Check if this skin contains a subskin with the given name
* @param name a subskin name
* @return true if the given subskin exists
*/
public boolean hasSubskin(String name) {
return subskins != null && subskins.containsKey(name);
}
/**
* Get a subskin by name
* @param name the subskin name
* @return the subskin
*/
public Skin getSubskin(String name) {
return subskins == null ? null : (Skin) subskins.get(name);
}
/**
* Return an array of subskin names defined in this skin
* @return a string array containing this skin's substrings
*/
public String[] getSubskinNames() {
return subskins == null ?
new String[0] :
(String[]) subskins.keySet().toArray(new String[0]);
}
/** /**
* Get the raw source text this skin was parsed from * Get the raw source text this skin was parsed from
*/ */
public String getSource() { public String getSource() {
return new String(source, 0, sourceLength); return new String(source, offset, length - offset);
} }
/** /**
@ -175,9 +248,8 @@ public final class Skin {
ResponseTrans res = reval.getResponse(); ResponseTrans res = reval.getResponse();
if (macros == null) { if (macros == null) {
res.write(source, 0, sourceLength); res.write(source, offset, length - offset);
reval.skinDepth--; reval.skinDepth--;
return; return;
} }
@ -186,7 +258,7 @@ public final class Skin {
Object previousParam = handlers.put("param", paramObject); Object previousParam = handlers.put("param", paramObject);
try { try {
int written = 0; int written = offset;
Map handlerCache = null; Map handlerCache = null;
if (macros.length > 3) { if (macros.length > 3) {
@ -202,8 +274,8 @@ public final class Skin {
written = macros[i].end; written = macros[i].end;
} }
if (written < sourceLength) { if (written < length) {
res.write(source, written, sourceLength - written); res.write(source, written, length - written);
} }
} finally { } finally {
reval.skinDepth--; reval.skinDepth--;
@ -276,6 +348,8 @@ public final class Skin {
// comment macros are silently dropped during rendering // comment macros are silently dropped during rendering
boolean isCommentMacro = false; boolean isCommentMacro = false;
// subskin macros delimits the beginning of a new subskin
boolean isSubskinMacro = false;
/** /**
* Create and parse a new macro. * Create and parse a new macro.
@ -293,7 +367,7 @@ public final class Skin {
int i; int i;
loop: loop:
for (i = start + offset; i < sourceLength - 1; i++) { for (i = start + offset; i < length - 1; i++) {
switch (source[i]) { switch (source[i]) {
@ -330,7 +404,7 @@ public final class Skin {
if (state == PARSE_MACRONAME && "//".equals(b.toString())) { if (state == PARSE_MACRONAME && "//".equals(b.toString())) {
isCommentMacro = true; isCommentMacro = true;
// search macro end tag // search macro end tag
while (i < sourceLength - 1 && while (i < length - 1 &&
(source[i] != '%' || source[i + 1] != '>')) { (source[i] != '%' || source[i + 1] != '>')) {
i++; i++;
} }
@ -339,6 +413,16 @@ public final class Skin {
} }
break; break;
case '#':
if (state == PARSE_MACRONAME && b.length() == 0) {
// this is a subskin/skinlet
isSubskinMacro = true;
break;
}
b.append(source[i]);
escape = false;
case '|': case '|':
if (!escape && quotechar == '\u0000') { if (!escape && quotechar == '\u0000') {
@ -431,7 +515,17 @@ public final class Skin {
} }
} }
this.end = Math.min(sourceLength, i + 2); i += 2;
if (isSubskinMacro) {
if (i + 1 < length && source[i] == '\r' && source[i + 1] == '\n')
end = Math.min(length, i + 2);
else if (i < length && (source[i] == '\r' || source[i] == '\n'))
end = Math.min(length, i + 1);
else
end = Math.min(length, i);
} else {
end = Math.min(length, i);
}
if (b.length() > 0) { if (b.length() > 0) {
if (name == null) { if (name == null) {
@ -462,11 +556,6 @@ public final class Skin {
handler = HANDLER_PARAM; handler = HANDLER_PARAM;
} }
} }
// Set default failmode unless explicitly set:
// silent for default handlers, verbose
if (namedParams == null || !namedParams.containsKey("failmode")) {
standardParams.silentFailMode = (handler < HANDLER_GLOBAL);
}
} }
private void addParameter(String name, Object value) { private void addParameter(String name, Object value) {
@ -607,8 +696,8 @@ public final class Skin {
} }
} }
// display error message unless silent failmode is on // display error message unless silent failmode is on
if (handlerObject == null || !hasProperty(handlerObject, propName, reval)) { if (handlerObject == null || !reval.scriptingEngine.hasProperty(handlerObject, propName)) {
if (!standardParams.silentFailMode) { if (standardParams.verboseFailmode(handlerObject, reval)) {
String msg = "[Macro unhandled: " + name + "]"; String msg = "[Macro unhandled: " + name + "]";
reval.getResponse().write(" " + msg + " "); reval.getResponse().write(" " + msg + " ");
app.logEvent(msg); app.logEvent(msg);
@ -616,13 +705,13 @@ public final class Skin {
writeResponse(null, reval, thisObject, handlerCache, standardParams, true); writeResponse(null, reval, thisObject, handlerCache, standardParams, true);
} }
} else { } else {
Object value = getProperty(handlerObject, propName, reval); Object value = reval.scriptingEngine.getProperty(handlerObject, propName);
writeResponse(value, reval, thisObject, handlerCache, standardParams, true); writeResponse(value, reval, thisObject, handlerCache, standardParams, true);
} }
} }
} else { } else {
if (!standardParams.silentFailMode) { if (standardParams.verboseFailmode(handlerObject, reval)) {
String msg = "[Macro unhandled: " + name + "]"; String msg = "[Macro unhandled: " + name + "]";
reval.getResponse().write(" " + msg + " "); reval.getResponse().write(" " + msg + " ");
app.logEvent(msg); app.logEvent(msg);
@ -770,12 +859,12 @@ public final class Skin {
} }
private Object resolvePath(Object handler, RequestEvaluator reval) { private Object resolvePath(Object handler, RequestEvaluator reval) {
if (!app.allowDeepMacros && path.length > 2) { if (path.length > 2 && !app.allowDeepMacros) {
throw new RuntimeException("allowDeepMacros property must be true " + throw new RuntimeException("allowDeepMacros property must be true " +
"in order to enable deep macro paths."); "in order to enable deep macro paths.");
} }
for (int i = 1; i < path.length - 1; i++) { for (int i = 1; i < path.length - 1; i++) {
handler = getProperty(handler, path[i], reval); handler = reval.scriptingEngine.getProperty(handler, path[i]);
if (handler == null) { if (handler == null) {
break; break;
} }
@ -783,23 +872,6 @@ public final class Skin {
return handler; 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.
*/ */
@ -822,7 +894,7 @@ public final class Skin {
return; return;
} }
} else { } else {
text = reval.getScriptingEngine().toString(value); text = reval.scriptingEngine.toString(value);
} }
if ((text != null) && (text.length() > 0)) { if ((text != null) && (text.length() > 0)) {
@ -887,7 +959,7 @@ public final class Skin {
Object prefix = null; Object prefix = null;
Object suffix = null; Object suffix = null;
Object defaultValue = null; Object defaultValue = null;
boolean silentFailMode = false; int failmode = FAIL_DEFAULT;
StandardParams() {} StandardParams() {}
@ -895,7 +967,6 @@ public final class Skin {
prefix = map.get("prefix"); prefix = map.get("prefix");
suffix = map.get("suffix"); suffix = map.get("suffix");
defaultValue = map.get("default"); defaultValue = map.get("default");
silentFailMode = "silent".equals(map.get("failmode"));
} }
boolean containsMacros() { boolean containsMacros() {
@ -906,13 +977,18 @@ public final class Skin {
void setFailMode(Object value) { void setFailMode(Object value) {
if ("silent".equals(value)) if ("silent".equals(value))
silentFailMode = true; failmode = FAIL_SILENT;
else if ("verbose".equals(value)) else if ("verbose".equals(value))
silentFailMode = false; failmode = FAIL_VERBOSE;
else else if (value != null)
app.logEvent("unrecognized failmode value: " + value); app.logEvent("unrecognized failmode value: " + value);
} }
boolean verboseFailmode(Object handler, RequestEvaluator reval) {
return (failmode == FAIL_VERBOSE) ||
(failmode == FAIL_DEFAULT && reval.scriptingEngine.isTypedObject(handler));
}
StandardParams render(RequestEvaluator reval, Object thisObj, Map handlerCache) StandardParams render(RequestEvaluator reval, Object thisObj, Map handlerCache)
throws UnsupportedEncodingException { throws UnsupportedEncodingException {
if (!containsMacros()) if (!containsMacros())

View file

@ -42,12 +42,22 @@ public final class SkinManager implements FilenameFilter {
skinExtension = ".skin"; skinExtension = ".skin";
} }
protected Skin getSkin(Prototype proto, String skinname, Object[] skinpath) throws IOException { protected Skin getSkin(Prototype prototype, String skinname, Object[] skinpath)
if (proto == null) { throws IOException {
if (prototype == null) {
return null; return null;
} }
Skin skin = null; Skin skin;
Prototype proto = prototype;
// if name contains dot, this might be a substring of some other string
String parentName = null, subskinName = null;
int hash = skinname.indexOf('#');
if (hash > -1) {
parentName = skinname.substring(0, hash);
subskinName = skinname.substring(hash + 1);
}
// First check if the skin has been already used within the execution of this request // First check if the skin has been already used within the execution of this request
// check for skinsets set via res.skinpath property // check for skinsets set via res.skinpath property
@ -55,16 +65,23 @@ public final class SkinManager implements FilenameFilter {
if (skinpath != null) { if (skinpath != null) {
for (int i = 0; i < skinpath.length; i++) { for (int i = 0; i < skinpath.length; i++) {
skin = getSkinInPath(skinpath[i], proto.getName(), skinname); skin = getSkinInPath(skinpath[i], proto.getName(), skinname);
if (skin != null && skin.hasMainskin()) {
if (skin != null) { // check if skin skin contains main skin
return skin; return skin;
} else if (parentName != null) {
// get parent skin
skin = getSkinInPath(skinpath[i], proto.getName(), parentName);
// check if it contains subskin
if (skin != null && skin.hasSubskin(subskinName)) {
return skin.getSubskin(subskinName);
}
} }
} }
} }
// skin for this prototype wasn't found in the skinsets. // skin for this prototype wasn't found in the skinsets.
// the next step is to look if it is defined as skin file in the application directory // the next step is to look if it is defined as skin file in the application directory
skin = proto.getSkin(skinname); skin = proto.getSkin(skinname, parentName, subskinName);
if (skin != null) { if (skin != null) {
return skin; return skin;
@ -78,7 +95,7 @@ public final class SkinManager implements FilenameFilter {
return null; return null;
} }
protected Skin getSkinInPath(Object skinset, String prototype, String skinname) throws IOException { private Skin getSkinInPath(Object skinset, String prototype, String skinname) throws IOException {
if ((prototype == null) || (skinset == null)) { if ((prototype == null) || (skinset == null)) {
return null; return null;
} }

View file

@ -0,0 +1,43 @@
/*
* Helma License Notice
*
* The contents of this file are subject to the Helma License
* Version 2.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://adele.helma.org/download/helma/license.txt
*
* Copyright 1998-2003 Helma Software. All Rights Reserved.
*
* $RCSfile$
* $Author$
* $Revision$
* $Date$
*/
package helma.framework.repository;
/**
* Abstract resource base class that implents get/setOverloadedResource.
*/
public abstract class AbstractResource implements Resource {
protected Resource overloaded = null;
/**
* Method for registering a Resource this Resource is overloading
*
* @param res the overloaded resource
*/
public void setOverloadedResource(Resource res) {
overloaded = res;
}
/**
* Get a Resource this Resource is overloading
*
* @return the overloaded resource
*/
public Resource getOverloadedResource() {
return overloaded;
}
}

View file

@ -19,7 +19,7 @@ package helma.framework.repository;
import java.net.*; import java.net.*;
import java.io.*; import java.io.*;
public class FileResource implements Resource { public class FileResource extends AbstractResource {
File file; File file;
Repository repository; Repository repository;

View file

@ -41,25 +41,29 @@ public interface Resource {
/** /**
* Returns the lengh of the resource's content * Returns the lengh of the resource's content
* @return content length * @return content length
* @throws IOException I/O related problem
*/ */
public long getLength() throws IOException; public long getLength() throws IOException;
/** /**
* Returns an input stream to the content of the resource * Returns an input stream to the content of the resource
* @return content input stream * @return content input stream
* @throws IOException I/O related problem
*/ */
public InputStream getInputStream() throws IOException; public InputStream getInputStream() throws IOException;
/** /**
* Returns the content of the resource in a given encoding * Returns the content of the resource in a given encoding
* @param encoding * @param encoding the character encoding
* @return content * @return content
* @throws IOException I/O related problem
*/ */
public String getContent(String encoding) throws IOException; public String getContent(String encoding) throws IOException;
/** /**
* Returns the content of the resource * Returns the content of the resource
* @return content * @return content
* @throws IOException I/O related problem
*/ */
public String getContent() throws IOException; public String getContent() throws IOException;
@ -88,9 +92,22 @@ public interface Resource {
* Returns an url to the resource if the repository of this resource is * Returns an url to the resource if the repository of this resource is
* able to provide urls * able to provide urls
* @return url to the resource * @return url to the resource
* @throws UnsupportedOperationException if resource does not support URL schema
*/ */
public URL getUrl() throws UnsupportedOperationException; public URL getUrl() throws UnsupportedOperationException;
/**
* Get a Resource this Resource is overloading
* @return the overloaded resource
*/
public Resource getOverloadedResource();
/**
* Method for registering a Resource this Resource is overloading
* @param res the overloaded resource
*/
public void setOverloadedResource(Resource res);
/** /**
* Returns the repository the resource does belong to * Returns the repository the resource does belong to
* @return upper repository * @return upper repository

View file

@ -21,7 +21,7 @@ import java.net.URL;
import java.util.zip.ZipEntry; import java.util.zip.ZipEntry;
import java.util.zip.ZipFile; import java.util.zip.ZipFile;
public final class ZipResource implements Resource { public final class ZipResource extends AbstractResource {
private String entryName; private String entryName;
private ZipRepository repository; private ZipRepository repository;

View file

@ -110,10 +110,10 @@ public interface ScriptingEngine {
/** /**
* Get a property on an object * Get a property on an object
* @param thisObject the object * @param thisObject the object
* @param key the property name * @param propertyName the property name
* @return true the property value, or null * @return true the property value, or null
*/ */
public Object getProperty(Object thisObject, String key); public Object getProperty(Object thisObject, String propertyName);
/** /**
* 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.
@ -131,6 +131,13 @@ public interface ScriptingEngine {
*/ */
public boolean hasProperty(Object thisObject, String propertyName); public boolean hasProperty(Object thisObject, String propertyName);
/**
* Determine if the given object is mapped to a type of the scripting engine
* @param obj an object
* @return true if the object is mapped to a type
*/
public boolean isTypedObject(Object obj);
/** /**
* Return a string representation for the given object * Return a string representation for the given object
* @param obj an object * @param obj an object

View file

@ -346,28 +346,31 @@ public class RhinoEngine implements ScriptingEngine {
// references/child objects just to check for function properties. // references/child objects just to check for function properties.
if (obj instanceof INode) { if (obj instanceof INode) {
String protoname = ((INode) obj).getPrototype(); String protoname = ((INode) obj).getPrototype();
return core.hasFunction(protoname, fname); if (protoname != null && core.hasFunction(protoname, fname))
return true;
} }
Scriptable op = obj == null ? global : Context.toObject(obj, global); Scriptable op = obj == null ? global : Context.toObject(obj, global);
return ScriptableObject.getProperty(op, fname) instanceof Callable;
Object func = ScriptableObject.getProperty(op, fname);
return func instanceof Callable;
} }
/** /**
* Check if an object has a value property defined with that name. * Check if an object has a value property defined with that name.
*/ */
public boolean hasProperty(Object obj, String propname) { public boolean hasProperty(Object obj, String propname) {
if ((obj == null) || (propname == null)) { if (obj == null || propname == null) {
return false; return false;
} else if (obj instanceof Map) {
return ((Map) obj).containsKey(propname);
} }
String prototypeName = app.getPrototypeName(obj); String prototypeName = app.getPrototypeName(obj);
if ("user".equalsIgnoreCase(prototypeName) if ("user".equalsIgnoreCase(prototypeName)
&& "password".equalsIgnoreCase(propname)) { && "password".equalsIgnoreCase(propname)) {
return false; return false;
} }
// if this is a HopObject, check if the property is defined // if this is a HopObject, check if the property is defined
// in the type.properties db-mapping. // in the type.properties db-mapping.
if (obj instanceof INode && ! "hopobject".equalsIgnoreCase(prototypeName)) { if (obj instanceof INode && ! "hopobject".equalsIgnoreCase(prototypeName)) {
@ -386,22 +389,36 @@ public class RhinoEngine implements ScriptingEngine {
* is a java object) with that name. * is a java object) with that name.
*/ */
public Object getProperty(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;
} else if (obj instanceof Map) {
Object prop = ((Map) obj).get(propname);
// Do not return functions as properties as this
// is a potential security problem
return (prop instanceof Function) ? null : prop;
} else if (obj instanceof INode) {
IProperty prop = ((INode) obj).get(propname);
if (prop == null) return null;
Object value = prop.getValue();
return (value instanceof Function) ? null : value;
}
// use Rhino wrappers and methods to get property
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) if (prop == null
|| (prop == Undefined.instance) || 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; // Do not return functions as properties as this
// is a potential security problem
return (prop instanceof Function) ? null : prop;
} }
} catch (Exception esx) { } catch (Exception esx) {
app.logError("Error getting property " + propname + ": " + esx); app.logError("Error getting property " + propname + ": " + esx);
@ -410,6 +427,22 @@ public class RhinoEngine implements ScriptingEngine {
} }
/**
* Determine if the given object is mapped to a type of the scripting engine
* @param obj an object
* @return true if the object is mapped to a type
*/
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);
return protoName != null && !"hopobject".equalsIgnoreCase(protoName);
}
// assume java object is typed
return true;
}
/** /**
* Return a string representation for the given object * Return a string representation for the given object
* @param obj an object * @param obj an object
@ -544,7 +577,9 @@ public class RhinoEngine implements ScriptingEngine {
* Try to get a skin from the parameter object. * Try to get a skin from the parameter object.
*/ */
public Skin toSkin(Object skinobj, String protoName) throws IOException { public Skin toSkin(Object skinobj, String protoName) throws IOException {
if (skinobj instanceof Wrapper) { if (skinobj == null) {
return null;
} else if (skinobj instanceof Wrapper) {
skinobj = ((Wrapper) skinobj).unwrap(); skinobj = ((Wrapper) skinobj).unwrap();
} }