/*
* 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.util.EmptyStackException;
import java.util.Stack;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
public class MagmaContext extends Context {
// private static final Logger log = LoggerFactory.getLogger(MagmaContext.class);
MagmaContext(MagmaContextFactory factory) {
super(factory);
}
public static MagmaContext getCurrentMagmaContext() {
return asMagmaContext(getCurrentContext());
}
public static MagmaContext asMagmaContext(Context ctx) {
try {
return (MagmaContext) ctx;
} catch(ClassCastException e) {
throw new MagmaJsRuntimeException("No MagmaContext available. " +
"Make sure MagmaJsExtension has been initialized before using JavascriptValueSource instances.", e);
}
}
/**
* Returns the instance of the {@code sharedScope} configured by {@code #initStandardObjects()}. A call to {@code
* #initStandardObjects()} must be made prior to invoking this method, otherwise an {@code IllegalStateException} is
* thrown.
*
* @return the {@code sharedScope}
*/
public ScriptableObject sharedScope() {
return getMagmaContextFactory().sharedScope();
}
/**
* Creates a new {@code Scriptable} instance for use as a transient scope. The returned {@code Scriptable} has no
* parent scope and has the {@code sharedScope} has prototype.
* <p/>
* The purpose of this method is to obtain a scope instance that extends the global scope and into which new objects
* and properties can be defined without polluting the global scope.
*
* @return a new instance of {@code Scriptable} for use as a top-level scope.
*/
public Scriptable newLocalScope() {
// Create a new object within the sharedScope
Scriptable scope = newObject(sharedScope());
// Set its prototype
scope.setPrototype(sharedScope());
// Remove its parent scope (makes it a top-level scope)
scope.setParentScope(null);
return scope;
}
@SuppressWarnings("unchecked")
public <T> void push(Class<T> type, T value) {
Stack<T> stack = (Stack<T>) getThreadLocal(type);
if(stack == null) {
stack = new Stack<>();
putThreadLocal(type, stack);
}
stack.push(value);
}
/**
* Removes the object at the top of this stack and returns that object as the value of this function.
*
* @param type
* @param <T>
* @return The object at the top of this stack (the last item of the <tt>Vector</tt> object).
*/
@SuppressWarnings("unchecked")
public <T> T pop(Class<T> type) {
Stack<T> stack = (Stack<T>) getThreadLocal(type);
if(stack == null) {
throw new IllegalStateException("Cannot pop stack for type " + type.getName());
}
try {
return stack.pop();
} catch(EmptyStackException e) {
throw new IllegalStateException("Cannot pop stack for type " + type.getName());
}
}
/**
* Looks at the object at the top of this stack without removing it from the stack.
*
* @param type
* @param <T>
* @return the object at the top of this stack (the last item of the <tt>Vector</tt> object).
*/
@SuppressWarnings("unchecked")
public <T> T peek(Class<T> type) {
Stack<T> stack = (Stack<T>) getThreadLocal(type);
if(stack == null) {
throw new IllegalStateException("Cannot peek stack for type " + type.getName());
}
try {
return stack.peek();
} catch(EmptyStackException e) {
return null;
}
}
public <T> boolean has(Class<T> type) {
return getThreadLocal(type) != null && peek(type) != null;
}
protected MagmaContextFactory getMagmaContextFactory() {
return (MagmaContextFactory) getFactory();
}
}