/** * 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; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; import java.util.List; import tern.EcmaVersion; import tern.ITernProject; import tern.TernException; import tern.repository.ITernRepository; import tern.utils.IOUtils; import com.eclipsesource.json.JsonArray; import com.eclipsesource.json.JsonObject; import com.eclipsesource.json.JsonValue; /** * Abstract class for tern server implemented with a script engine. * */ public abstract class AbstractScriptEngineTernServer extends AbstractTernServer { private final String[] ACORN_SCRIPTS = { "acorn/dist/acorn.js", "acorn/dist/acorn_loose.js", "acorn/dist/walk.js" }; private final String[] TERN_SCRIPTS = { "tern/lib/signal.js", "tern/lib/tern.js", "tern/lib/def.js", "tern/lib/comment.js", "tern/lib/infer.js" }; protected class TernResource { private final String content; private final String filename; public TernResource(String content, String filename) { this.content = content; this.filename = filename; } public String getContent() { return content; } public String getFilename() { return filename; } } protected class TernResources { private final List<TernResource> scripts; private final List<TernResource> defs; public TernResources(List<TernResource> scripts, List<TernResource> defs) { this.scripts = scripts; this.defs = defs; } public List<TernResource> getScripts() { return scripts; } public List<TernResource> getDefs() { return defs; } public String getDefsAsString() { StringBuilder s = new StringBuilder(); for (int i = 0; i < defs.size(); i++) { if (i > 0) { s.append(","); } s.append(defs.get(i).getContent()); } return s.toString(); } } public AbstractScriptEngineTernServer(ITernProject project) { super(project); } protected TernResources loadTern() throws TernException { ITernRepository repository = getProject().getRepository(); if (repository == null) { throw new TernException("Tern repository must be initialized."); } try { List<TernResource> scripts = new ArrayList<TernResource>(); List<TernResource> defs = new ArrayList<TernResource>(); // Load acorn for (int i = 0; i < ACORN_SCRIPTS.length; i++) { scripts.add(getResource(new File(repository.getNodeModulesDir(), ACORN_SCRIPTS[i]))); } // Load ternjs for (int i = 0; i < TERN_SCRIPTS.length; i++) { scripts.add(getResource(new File(repository.getNodeModulesDir(), TERN_SCRIPTS[i]))); } // load ECMAScript defs EcmaVersion ecmaVersion = super.getProject().getEcmaVersion(); if (ecmaVersion == null) { ecmaVersion = EcmaVersion.ES5; } switch (ecmaVersion) { case ES5: addDef(TernDef.ecmascript.getName(), repository, defs, false); break; case ES6: addDef(TernDef.ecmascript.getName(), repository, defs, false); break; case ES7: addDef(TernDef.ecmascript.getName(), repository, defs, false); break; } // Load defs JsonArray libs = getProject().getLibs(); if (libs != null) { for (JsonValue lib : libs) { addDef(lib.asString(), repository, defs, true); } } // Load plugins JsonObject plugins = getProject().getPlugins(); if (plugins != null) { ITernModule module = null; File pluginFile = null; List<String> names = plugins.names(); for (String name : names) { module = repository.getModule(name); if (module != null) { pluginFile = repository.getFile(module); if (pluginFile != null && pluginFile.exists()) { scripts.add(getResource(pluginFile)); } } } } return new TernResources(scripts, defs); } catch (IOException e) { throw new TernException(e); } } protected void addDef(String def, ITernRepository repository, List<TernResource> defs, boolean ignoreEcma) throws IOException { if (ignoreEcma && (def.equals(TernDef.ecmascript.getName()))) { return; } ITernModule module = repository.getModule(def); if (module != null) { File defFile = repository.getFile(module); if (defFile != null && defFile.exists()) { defs.add(getResource(defFile)); } } } protected TernResource getResource(File scriptFile) throws IOException { // Use FileInputStream (instead of FileReader) to set encoding to UTF-8, // to avoid exception of acorn.js loading : // ternjs\node_modules\tern\node_modules\acorn\dist\acorn.js:877: // SyntaxError: Invalid regular expression: // /[ªµºÀ-ÖØ-öø-Ë?ˆ-Ë‘Ë -ˤˬˮ�°-�´�¶�·�º-�½�¿Î†Îˆ-ΊΌΎ-ΡΣ-�µ�·-Ò?ÒŠ-Ô¯Ô±-Õ–Õ™Õ¡-Ö‡×?-תװ-ײؠ-يٮٯٱ-Û“Û•Û¥Û¦Û®Û¯Ûº-Û¼Û¿Ü?Ü’-ܯ�?-ޥޱߊ-ߪߴߵߺࠀ-ࠕࠚࠤࠨࡀ-ࡘࢠ-ࢲऄ-हऽà¥?क़-ॡॱ-ঀঅ-ঌà¦?à¦?ও-নপ...<omitted>...œ]/: // Range out of order in character class String script = getScriptContent(scriptFile); String filename = getFilename(scriptFile); return new TernResource(script, filename); } protected String getScriptContent(File scriptFile) throws IOException, FileNotFoundException { return IOUtils.toString(new FileInputStream(scriptFile), "UTF-8"); } protected String getFilename(File scriptFile) { try { return scriptFile.getCanonicalPath().toString(); } catch (Throwable e) { return scriptFile.getPath().toString(); } } }