/* * 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); } }