/*
* Copyright 2004-2012 the Seasar Foundation and the Others.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
package org.seasar.mayaa.impl.cycle.script.rhino;
import java.util.Iterator;
import java.util.Map;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.NativeArray;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import org.mozilla.javascript.Undefined;
import org.mozilla.javascript.WrapFactory;
import org.seasar.mayaa.PositionAware;
import org.seasar.mayaa.cycle.ServiceCycle;
import org.seasar.mayaa.cycle.scope.ApplicationScope;
import org.seasar.mayaa.cycle.scope.AttributeScope;
import org.seasar.mayaa.cycle.script.CompiledScript;
import org.seasar.mayaa.impl.IllegalParameterValueException;
import org.seasar.mayaa.impl.cycle.CycleUtil;
import org.seasar.mayaa.impl.cycle.DefaultCycleLocalInstantiator;
import org.seasar.mayaa.impl.cycle.script.AbstractScriptEnvironment;
import org.seasar.mayaa.impl.cycle.script.LiteralScript;
import org.seasar.mayaa.impl.cycle.script.ScriptBlock;
import org.seasar.mayaa.impl.cycle.script.rhino.direct.GetterScriptFactory;
import org.seasar.mayaa.impl.util.ObjectUtil;
import org.seasar.mayaa.impl.util.StringUtil;
import org.seasar.mayaa.impl.util.WeakValueHashMap;
import org.seasar.mayaa.source.SourceDescriptor;
/**
* Rhino用のスクリプト環境。
*
* @author Masataka Kurihara (Gluegent, Inc.)
*/
public class ScriptEnvironmentImpl extends AbstractScriptEnvironment {
private static final long serialVersionUID = -4067264733660357274L;
private static Scriptable _standardObjects;
private static final WeakValueHashMap/*<String, CompiledScript>*/ scriptCache = new WeakValueHashMap(128);
private static final boolean CONSTRAINT_GLOBAL_PROPERTY_DEFINE = true;
// singleton
private static WrapFactory _wrap;
private boolean _useGetterScriptEmulation;
protected CompiledScript compile(
ScriptBlock scriptBlock, PositionAware position, int offsetLine) {
if (scriptBlock == null) {
throw new IllegalArgumentException();
}
String text = scriptBlock.getBlockString();
String trimmed = text.trim();
CompiledScript script = (CompiledScript) scriptCache.get(trimmed);
if (script == null) {
synchronized (scriptCache) {
script = (CompiledScript) scriptCache.get(trimmed);
if (script == null) {
if (scriptBlock.isLiteral()) {
script = new LiteralScript(text);
}
if (script == null) {
if (_useGetterScriptEmulation) {
script = GetterScriptFactory.create(text, position, offsetLine);
}
if (script == null) {
script = new TextCompiledScriptImpl(text, position, offsetLine);
}
}
scriptCache.put(trimmed, script);
}
}
}
return script;
}
// ScriptEnvironment implements ----------------------------------
protected String getSourceMimeType(SourceDescriptor source) {
if (source == null) {
throw new IllegalArgumentException();
}
String systemID = source.getSystemID();
ServiceCycle cycle = CycleUtil.getServiceCycle();
ApplicationScope application = cycle.getApplicationScope();
return application.getMimeType(systemID);
}
public CompiledScript compile(
SourceDescriptor source, String encoding) {
if (source == null) {
throw new IllegalArgumentException();
}
return new SourceCompiledScriptImpl(source, encoding);
}
protected static Scriptable getStandardObjects() {
if (_standardObjects == null) {
Context cx = Context.enter();
try {
_standardObjects = cx.initStandardObjects(null, true);
if (CONSTRAINT_GLOBAL_PROPERTY_DEFINE) {
ScriptableObject scope = (ScriptableObject)_standardObjects;
scope.defineProperty("__global__", scope,
ScriptableObject.READONLY | ScriptableObject.DONTENUM);
}
} finally {
Context.exit();
}
}
return _standardObjects;
}
private static final String PARENT_SCRIPTABLE_KEY =
ScriptEnvironmentImpl.class.getName() + "#parentScriptable";
static {
CycleUtil.registVariableFactory(PARENT_SCRIPTABLE_KEY,
new DefaultCycleLocalInstantiator() {
public Object create(Object[] params) {
ServiceCycle cycle = CycleUtil.getServiceCycle();
Scriptable parent;
Context cx = RhinoUtil.enter();
try {
Scriptable standard = getStandardObjects();
parent = cx.getWrapFactory().wrapAsJavaObject(
cx, standard, cycle, ServiceCycle.class);
} finally {
Context.exit();
}
return parent;
}
});
}
public void initScope() {
ServiceCycle cycle = CycleUtil.getServiceCycle();
cycle.setPageScope(null);
CycleUtil.clearGlobalVariable(PARENT_SCRIPTABLE_KEY);
}
public void startScope(Map variables) {
ServiceCycle cycle = CycleUtil.getServiceCycle();
AttributeScope scope = cycle.getPageScope();
Scriptable parent;
if (scope == null) {
parent = (Scriptable) CycleUtil.getGlobalVariable(PARENT_SCRIPTABLE_KEY, null);
} else if (scope instanceof PageAttributeScope) {
PageAttributeScope pageTop = (PageAttributeScope) scope;
parent = (PageAttributeScope)pageTop.getAttribute(
PageAttributeScope.KEY_CURRENT);
} else {
throw new IllegalStateException();
}
PageAttributeScope pageScope = new PageAttributeScope();
pageScope.setParentScope(parent);
if (variables != null) {
RhinoUtil.enter();
try {
for (Iterator it = variables.keySet().iterator(); it.hasNext();) {
Object key = it.next();
Object value = variables.get(key);
Object variable = Context.javaToJS(value, pageScope);
ScriptableObject.putProperty(pageScope, key.toString(), variable);
}
} finally {
Context.exit();
}
}
// only first page scope
if (cycle.getPageScope() == null) {
cycle.setPageScope(pageScope);
}
cycle.getPageScope().setAttribute(
PageAttributeScope.KEY_CURRENT, pageScope);
}
public void endScope() {
ServiceCycle cycle = CycleUtil.getServiceCycle();
AttributeScope scope = cycle.getPageScope();
if (scope instanceof PageAttributeScope) {
PageAttributeScope pageScope = (PageAttributeScope) scope;
pageScope = (PageAttributeScope) pageScope.getAttribute(
PageAttributeScope.KEY_CURRENT);
Scriptable current = pageScope.getParentScope();
if (current instanceof PageAttributeScope) {
PageAttributeScope parentScope =
(PageAttributeScope) current;
cycle.getPageScope().setAttribute(
PageAttributeScope.KEY_CURRENT, parentScope);
return;
} else if (current != null) {
cycle.setPageScope(null);
return;
}
}
throw new IllegalStateException();
}
public Object convertFromScriptObject(Object scriptObject) {
return convertFromScriptObject(scriptObject, Object.class);
}
/**
* {@link Undefined}は空と見なす。
* @param scriptResult 判定するオブジェクト
* @return {@code scriptResult}がJavaの{@code null}か、{@link Undefined}オブジェクトなら{@code true}。
*/
public boolean isEmpty(Object scriptResult) {
return scriptResult == null
|| scriptResult instanceof Undefined;
}
public Object convertFromScriptObject(Object scriptObject, Class expectedClass) {
if (scriptObject != null && conversionRequires(scriptObject, expectedClass)) {
Object result = RhinoUtil.convertResult(null, expectedClass, scriptObject);
if (result instanceof NativeArray) {
NativeArray jsArray = (NativeArray) result;
int length = (int) jsArray.getLength();
Object[] array = new Object[length];
for (int i = 0; i < length; i++) {
array[i] = jsArray.get(i, null);
}
result = array;
}
return result;
}
return scriptObject;
}
private boolean conversionRequires(Object scriptObject, Class expectedClass) {
// PageAttributeScopeは呼ばれる数が多い
if (scriptObject instanceof PageAttributeScope) {
return false;
}
return (scriptObject instanceof Scriptable);
}
static void setWrapFactory(WrapFactory wrap) {
_wrap = wrap;
}
static WrapFactory getWrapFactory() {
return _wrap;
}
// Parameterizable implements ------------------------------------
public void setParameter(String name, String value) {
if ("wrapFactory".equals(name)) {
if (StringUtil.isEmpty(value)) {
throw new IllegalParameterValueException(getClass(), name);
}
Class clazz = ObjectUtil.loadClass(value, WrapFactory.class);
setWrapFactory((WrapFactory) ObjectUtil.newInstance(clazz));
} else if ("blockSign".equals(name)) {
if (StringUtil.isEmpty(value)) {
throw new IllegalParameterValueException(getClass(), name);
}
setBlockSign(value);
} else if ("useGetterScriptEmulation".equals("name")) {
if (StringUtil.isEmpty(value)) {
throw new IllegalParameterValueException(getClass(), name);
}
_useGetterScriptEmulation = ObjectUtil.booleanValue(value, false);
}
super.setParameter(name, value);
}
}