/* * Copyright (c) 2017 OBiBa. All rights reserved. * * This program and the accompanying materials * are made available under the terms of the GNU Public License v3.0. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.obiba.magma.js; import java.lang.reflect.Method; import java.util.Collection; import java.util.Collections; import java.util.Set; import javax.validation.constraints.NotNull; import org.mozilla.javascript.Context; import org.mozilla.javascript.ContextAction; import org.mozilla.javascript.ContextFactory; import org.mozilla.javascript.FunctionObject; import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.ScriptableObject; import org.obiba.magma.Initialisable; import org.obiba.magma.js.methods.GlobalMethods; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; /** * Creates instances of {@code MagmaContext} */ public class MagmaContextFactory extends ContextFactory implements Initialisable { /** * The global scope shared by all evaluated scripts. Should contain top-level functions and prototypes. */ private ScriptableObject sharedScope; @NotNull private ScriptableValuePrototypeFactory scriptableValuePrototypeFactory = new ScriptableValuePrototypeFactory(); private final ScriptableVariablePrototypeFactory scriptableVariablePrototypeFactory = new ScriptableVariablePrototypeFactory(); @NotNull private Set<GlobalMethodProvider> globalMethodProviders = Collections.emptySet(); @Override protected Context makeContext() { return new MagmaContext(this); } public ScriptableObject sharedScope() { if(sharedScope == null) { throw new MagmaJsRuntimeException( "Shared scope not initialised. Make sure the MagmaJsExtension has been added to the MagmaEngine before evaluating scripts."); } return sharedScope; } public void setGlobalMethodProviders(@NotNull Collection<GlobalMethodProvider> globalMethodProviders) { //noinspection ConstantConditions if(globalMethodProviders == null) throw new IllegalArgumentException("globalMethodProviders cannot be null"); this.globalMethodProviders = ImmutableSet.copyOf(globalMethodProviders); } @NotNull public ScriptableValuePrototypeFactory getScriptableValuePrototypeFactory() { return scriptableValuePrototypeFactory; } public void setScriptableValuePrototypeFactory(@NotNull ScriptableValuePrototypeFactory factory) { //noinspection ConstantConditions if(factory == null) throw new IllegalArgumentException("factory cannot be null"); scriptableValuePrototypeFactory = factory; } @Override public void initialise() { sharedScope = (ScriptableObject) ContextFactory.getGlobal().call(new ContextAction() { @Override public Object run(Context cx) { ScriptableObject scriptableObject = cx.initStandardObjects(null, true); // Register Global methods for(GlobalMethodProvider provider : Iterables .concat(ImmutableSet.of(new GlobalMethods()), globalMethodProviders)) { for(Method globalMethod : provider.getJavaScriptExtensionMethods()) { String name = provider.getJavaScriptMethodName(globalMethod); FunctionObject fo = new FunctionObject(name, globalMethod, scriptableObject); scriptableObject.defineProperty(name, fo, ScriptableObject.DONTENUM); } } Scriptable valuePrototype = scriptableValuePrototypeFactory.buildPrototype(); ScriptableObject.putProperty(scriptableObject, valuePrototype.getClassName(), valuePrototype); Scriptable variablePrototype = scriptableVariablePrototypeFactory.buildPrototype(); ScriptableObject.putProperty(scriptableObject, variablePrototype.getClassName(), variablePrototype); scriptableObject.sealObject(); return scriptableObject; } }); } }