/**
* Copyright (c) 2013-2016 Angelo ZERR.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation
*/
package tern.server.rhino;
import java.io.IOException;
import java.util.List;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Function;
import org.mozilla.javascript.NativeObject;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.UniqueTag;
import tern.ITernProject;
import tern.TernException;
import tern.server.AbstractScriptEngineTernServer;
import tern.server.IResponseHandler;
import tern.server.protocol.IJSONObjectHelper;
import tern.server.protocol.TernDoc;
import tern.server.protocol.html.ScriptTagRegion;
import tern.server.rhino.loader.ClassPathScriptLoader;
public class RhinoTernServer extends AbstractScriptEngineTernServer {
private Scriptable ternScope;
private final String[] BEFORE_SCRIPTS = { "env.rhino.1.2.js", "json.js" };
private final String[] AFTER_SCRIPTS = { "tern-server.js" };
public RhinoTernServer(ITernProject project) {
super(project);
}
@Override
public void addFile(String name, String text, ScriptTagRegion[] tags) {
TernDoc doc = new TernDoc();
doc.addFile(name, text, tags, null);
request(doc, new IResponseHandler() {
@Override
public void onSuccess(Object data, String dataAsJsonString) {
//System.err.println(data);
}
@Override
public void onError(String error, Throwable t) {
// TODO Auto-generated method stub
}
@Override
public boolean isDataAsJsonString() {
// TODO Auto-generated method stub
return true;
}
});
}
@Override
public void request(TernDoc doc, IResponseHandler handler) {
Context cx = Context.enter();
try {
// tern.js checks if file.text is typeof string
// set java primitive wrap to false, otherwise tern.js throws error
// ".files[n].text must be a string"
cx.getWrapFactory().setJavaPrimitiveWrap(false);
Scriptable ternScope = getTernSope();
Object jsObject = Context.javaToJS(doc.toString(), ternScope);
Object functionArgs[] = { jsObject, handler, handler.isDataAsJsonString() };
Object fObj = ((Scriptable)ternScope.get("server", ternScope)).get("request2", ternScope);
Function f = (Function) fObj;
f.call(cx, ternScope, ternScope, functionArgs);
} catch (Exception e) {
handler.onError(e.getMessage(), e);
} finally {
// Exit from the context.
Context.exit();
}
}
@Override
public IJSONObjectHelper getJSONObjectHelper() {
return RhinoJSONHelper.INSTANCE;
}
@Override
protected void doDispose() {
fireEndServer();
}
private Scriptable loadRhinoTern() throws TernException, IOException {
Scriptable ternScope;
Context cx = Context.enter();
try {
// Initialize the standard objects (Object, Function, etc.)
// This must be done before scripts can be executed. Returns
// a scope object that we use in later calls.
ternScope = cx.initStandardObjects();
// acorn uses self variables, define it here.
cx.evaluateString(ternScope, "var self = this;", "", 1, null);
cx.setOptimizationLevel(-1);
// Env scripts
loadScripts(cx, ternScope, BEFORE_SCRIPTS);
// Tern, acorn scripts
TernResources resources = loadTern();
// Load tern scripts (acorn + ternjs) + plugins scripts
List<TernResource> scripts = resources.getScripts();
for (TernResource script : scripts) {
cx.evaluateString(ternScope, script.getContent(), script.getFilename(), 0, null);
}
// tern-server
String defs = resources.getDefsAsString();
loadScripts(cx, ternScope, AFTER_SCRIPTS);
StringBuilder script = new StringBuilder(
"var server = new J2V8TernServer(");
script.append("[");
script.append(defs.toString());
script.append("],");
script.append(getProject().getPlugins() != null ? getProject()
.getPlugins().toString() : "");
script.append(");");
cx.evaluateString(ternScope, script.toString(), "init", 0, null);
} finally {
// Exit from the context.
Context.exit();
}
return ternScope;
}
private void loadScripts(Context cx, Scriptable ternScope, String[] srcs) throws IOException {
for (int i = 0; i < srcs.length; i++) {
ClassPathScriptLoader.getInstance().loadScript(cx, ternScope, srcs[i]);
}
}
private synchronized Scriptable getTernSope() throws TernException, IOException {
if (ternScope == null) {
ternScope = loadRhinoTern();
}
return ternScope;
}
private static class RhinoJSONHelper implements IJSONObjectHelper {
public static RhinoJSONHelper INSTANCE = new RhinoJSONHelper();
@SuppressWarnings("unchecked")
@Override
public Iterable<Object> getList(Object jsonObj, String name) {
Object result = ((NativeObject) jsonObj).get(name, // $NON-NLS-1$
(NativeObject) jsonObj);
return (result instanceof Iterable<?>) ? (Iterable<Object>) result : null;
}
@Override
public Long getCh(Object data, String name) {
Double d = null;
if (data instanceof Double) {
d = (Double) data;
} else {
Object loc = ((NativeObject) data).get(name, (NativeObject) data);
if (loc instanceof NativeObject) {
d = (Double) ((NativeObject)loc).get("ch", ((NativeObject)loc)); //$NON-NLS-1$
} else if (loc instanceof Double) {
d = (Double) loc;
}
}
if (d != null) {
return d.longValue();
}
return null;
}
@Override
public String getText(Object jsonObj, String property) {
Object text = ((Scriptable) jsonObj).get(property, (Scriptable) jsonObj);
if (text == null || (text instanceof UniqueTag)) {
return null;
}
return text.toString();
}
@Override
public Long getLong(Object jsonObject, String name) {
return null;
}
@Override
public boolean isString(Object value) {
return value instanceof String;
}
@Override
public String getText(Object value) {
if (value == null) {
return null;
}
return value.toString();
}
@Override
public boolean getBoolean(Object jsonObject, String name, boolean defaultValue) {
String val = getText(jsonObject, name);
return val != null ? Boolean.valueOf(val) : defaultValue;
}
@Override
public Object getObject(Object jsonObj, String name) {
return ((Scriptable) jsonObj).get(name, (Scriptable) jsonObj);
}
}
}