2021SC@SDUSC
FreeMarker代码分析第六篇
rhino包
RhinoFunctionModel.java
代码分析
package freemarker.ext.rhino;
import java.util.List;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Function;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import freemarker.ext.beans.BeansWrapper;
import freemarker.template.TemplateMethodModelEx;
import freemarker.template.TemplateModel;
import freemarker.template.TemplateModelException;
/**
*/
public class RhinoFunctionModel extends RhinoScriptableModel
implements TemplateMethodModelEx {
private final Scriptable fnThis;
/*
构造函数
*/
public RhinoFunctionModel(Function function, Scriptable fnThis,
BeansWrapper wrapper) {
super(function, wrapper);
this.fnThis = fnThis;
}
/*
*/
@Override
public Object exec(List arguments) throws TemplateModelException {
Context cx = Context.getCurrentContext();
Object[] args = arguments.toArray();
BeansWrapper wrapper = getWrapper();
for (int i = 0; i < args.length; i++) {
args[i] = wrapper.unwrap((TemplateModel) args[i]);
}
return wrapper.wrap(((Function) getScriptable()).call(cx,
ScriptableObject.getTopLevelScope(fnThis), fnThis, args));
}
}
RhinoScriptableModel.java
代码分析
package freemarker.ext.rhino;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.EvaluatorException;
import org.mozilla.javascript.Function;
import org.mozilla.javascript.NativeJavaObject;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import freemarker.ext.beans.BeansWrapper;
import freemarker.ext.util.ModelFactory;
import freemarker.template.AdapterTemplateModel;
import freemarker.template.ObjectWrapper;
import freemarker.template.TemplateBooleanModel;
import freemarker.template.TemplateCollectionModel;
import freemarker.template.TemplateHashModelEx;
import freemarker.template.TemplateModel;
import freemarker.template.TemplateModelException;
import freemarker.template.TemplateNumberModel;
import freemarker.template.TemplateScalarModel;
import freemarker.template.TemplateSequenceModel;
/**
*/
public class RhinoScriptableModel implements TemplateHashModelEx,
TemplateSequenceModel, AdapterTemplateModel, TemplateScalarModel,
TemplateBooleanModel, TemplateNumberModel {
static final ModelFactory FACTORY = new ModelFactory() {
@Override
public TemplateModel create(Object object, ObjectWrapper wrapper) {
return new RhinoScriptableModel((Scriptable) object,
(BeansWrapper) wrapper);
}
};
private final Scriptable scriptable;
private final BeansWrapper wrapper;
/*
构造函数
*/
public RhinoScriptableModel(Scriptable scriptable, BeansWrapper wrapper) {
this.scriptable = scriptable;
this.wrapper = wrapper;
}
@Override
public TemplateModel get(String key) throws TemplateModelException {
Object retval = ScriptableObject.getProperty(scriptable, key);
if (retval instanceof Function) {
return new RhinoFunctionModel((Function) retval, scriptable, wrapper);
} else {
return wrapper.wrap(retval);
}
}
@Override
public TemplateModel get(int index) throws TemplateModelException {
Object retval = ScriptableObject.getProperty(scriptable, index);
if (retval instanceof Function) {
return new RhinoFunctionModel((Function) retval, scriptable, wrapper);
} else {
return wrapper.wrap(retval);
}
}
@Override
public boolean isEmpty() {
return scriptable.getIds().length == 0;
}
@Override
public TemplateCollectionModel keys() throws TemplateModelException {
return (TemplateCollectionModel) wrapper.wrap(scriptable.getIds());
}
@Override
public int size() {
return scriptable.getIds().length;
}
@Override
public TemplateCollectionModel values() throws TemplateModelException {
Object[] ids = scriptable.getIds();
Object[] values = new Object[ids.length];
for (int i = 0; i < values.length; i++) {
Object id = ids[i];
if (id instanceof Number) {
values[i] = ScriptableObject.getProperty(scriptable,
((Number) id).intValue());
} else {
values[i] = ScriptableObject.getProperty(scriptable,
String.valueOf(id));
}
}
return (TemplateCollectionModel) wrapper.wrap(values);
}
@Override
public boolean getAsBoolean() {
return Context.toBoolean(scriptable);
}
@Override
public Number getAsNumber() {
return Double.valueOf(Context.toNumber(scriptable));
}
@Override
public String getAsString() {
return Context.toString(scriptable);
}
Scriptable getScriptable() {
return scriptable;
}
BeansWrapper getWrapper() {
return wrapper;
}
@Override
public Object getAdaptedObject(Class hint) {
// FIXME: This does LS3 conversion, which is not very useful for us. Like it won't convert to List, Map, etc.
try {
return NativeJavaObject.coerceType(hint, scriptable);
} catch (EvaluatorException e) {
return NativeJavaObject.coerceType(Object.class, scriptable);
}
}
}
RhinoWrapper.java
代码分析
package freemarker.ext.rhino;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.Undefined;
import org.mozilla.javascript.UniqueTag;
import org.mozilla.javascript.Wrapper;
import freemarker.ext.beans.BeansWrapper;
import freemarker.ext.util.ModelFactory;
import freemarker.template.TemplateModel;
import freemarker.template.TemplateModelException;
import freemarker.template.utility.UndeclaredThrowableException;
/**
*/
public class RhinoWrapper extends BeansWrapper {
// The type of the "instance" field changed between Rhino versions, so a
// GETSTATIC with wrong type declaration would cause a NoSuchFieldError;
// we're avoiding it by acquiring it reflectively.
private static final Object UNDEFINED_INSTANCE;
static {
try {
UNDEFINED_INSTANCE = AccessController.doPrivileged(new PrivilegedExceptionAction() {
@Override
public Object run() throws Exception {
return Undefined.class.getField("instance").get(null);
}
});
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new UndeclaredThrowableException(e);
}
}
@Override
public TemplateModel wrap(Object obj) throws TemplateModelException {
// So our existence builtins work as expected.
if (obj == UNDEFINED_INSTANCE || obj == UniqueTag.NOT_FOUND) {
return null;
}
// UniqueTag.NULL_VALUE represents intentionally set null in Rhino, and
// BeansWrapper#nullModel also represents intentionally returned null.
// I [A.Sz.] am fairly certain that this value is never passed out of
// any of the Rhino code back to clients, but is instead always being
// converted back to null. However, since this object is available to
// any 3rd party Scriptable implementations as well, they might return
// it, so we'll just be on the safe side, and handle it.
if (obj == UniqueTag.NULL_VALUE) {
return super.wrap(null);
}
// So, say, a JavaAdapter for FreeMarker interfaces works
if (obj instanceof Wrapper) {
obj = ((Wrapper) obj).unwrap();
}
return super.wrap(obj);
}
@Override
protected ModelFactory getModelFactory(Class clazz) {
if (Scriptable.class.isAssignableFrom(clazz)) {
return RhinoScriptableModel.FACTORY;
}
return super.getModelFactory(clazz);
}
}