package org.ringojs.engine;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Function;
import org.mozilla.javascript.ScriptRuntime;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import org.mozilla.javascript.Undefined;
import org.mozilla.javascript.annotations.JSFunction;
import org.mozilla.javascript.annotations.JSGetter;
import org.ringojs.repository.FileResource;
import org.ringojs.repository.Repository;
import org.ringojs.repository.Resource;
import org.ringojs.repository.Trackable;
import java.io.IOException;
public class ModuleObject extends ScriptableObject {
String id;
Trackable source;
Repository repository;
public ModuleObject() {}
ModuleObject(ModuleScope moduleScope) {
setParentScope(moduleScope);
setPrototype(ScriptableObject.getClassPrototype(moduleScope, "ModuleObject"));
this.id = moduleScope.getModuleName();
this.source = moduleScope.getSource();
this.repository = moduleScope.getRepository();
}
@JSFunction
public String resolve(Object path) throws IOException {
String _path = (path == null || path == Undefined.instance) ?
"" : ScriptRuntime.toString(path);
if (repository == null) {
return _path;
}
Resource res = repository.getResource(_path);
if (res == null) {
return _path;
} else if (res instanceof FileResource) {
// Return absolute path for file resources
return res.getPath();
} else {
// not a file, return relative path so it
return res.getRelativePath();
}
}
@JSFunction
public Object singleton(Object id, Object factory) {
if (!(id instanceof CharSequence)) {
throw ScriptRuntime.constructError("Error",
"singleton() requires a string id as first argument");
}
Function factoryFunction = null;
if (factory instanceof Function) {
factoryFunction = (Function)factory;
} else if (factory != Undefined.instance && factory != null) {
throw ScriptRuntime.constructError("Error",
"Expected function as second argument");
}
Scriptable scope = ScriptableObject.getTopLevelScope(getParentScope());
RhinoEngine engine = RhinoEngine.getEngine(scope);
Singleton singleton = engine.getSingleton(new Singleton(source, id.toString()));
return singleton.getValue(factoryFunction, scope, this);
}
@JSGetter
public Object getId() {
return id == null ? Undefined.instance : id;
}
@JSGetter
public Object getPath() {
return source == null ? Undefined.instance : source.getPath();
}
@JSGetter
public Object getUri() {
try {
return source == null ? Undefined.instance : source.getUrl().toString();
} catch (Exception x) {
return Undefined.instance;
}
}
@JSGetter
public Object getDirectory() {
return repository == null ? Undefined.instance : repository.getPath();
}
@Override
protected Object equivalentValues(Object value) {
if (value instanceof String) {
return value.equals(id) ? Boolean.TRUE : Boolean.FALSE;
}
return NOT_FOUND;
}
@Override
public String getClassName() {
return "ModuleObject";
}
}
/**
* A wrapper around a singleton id and value.
*/
class Singleton {
final String key;
boolean evaluated = false;
Object value = Undefined.instance;
Singleton(Trackable source, String id) {
this.key = source.getPath() + ":" + id;
}
synchronized Object getValue(Function function, Scriptable scope,
ModuleObject obj) {
if (!evaluated && function != null) {
Context cx = Context.getCurrentContext();
value = function.call(cx, scope, obj, ScriptRuntime.emptyArgs);
evaluated = true; // only if evaluation was successful
}
return value;
}
@Override
public boolean equals(Object obj) {
return obj instanceof Singleton
&& key.equals(((Singleton) obj).key);
}
@Override
public int hashCode() {
return key.hashCode();
}
}