package net.zhuoweizhang.mcpelauncher; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.nio.charset.Charset; import org.mozilla.javascript.Context; import org.mozilla.javascript.Function; import org.mozilla.javascript.Script; import org.mozilla.javascript.Scriptable; import org.mozilla.javascript.ScriptableObject; public class CoffeeScriptCompiler { public static boolean isCoffeeScript(File input) { return input.getName().toLowerCase().endsWith(".coffee") || isLiterateCoffeeScript(input); } public static boolean isLiterateCoffeeScript(File input) { return input.getName().toLowerCase().endsWith(".litcoffee"); } public static String outputName(String input) { return input.substring(0, input.lastIndexOf(".")) + ".js"; } public static void compileFile(File input, File output) throws IOException { //read the file //call compile with the string //write the string to the new file InputStream is = new FileInputStream(input); int length = (int) input.length(); byte[] data = new byte[length]; is.read(data); is.close(); Charset utf8 = Charset.forName("UTF-8"); String inputString = new String(data, utf8); String outputString = compile(inputString, isLiterateCoffeeScript(input)); OutputStream os = new FileOutputStream(output); os.write(outputString.getBytes(utf8)); os.close(); } public static String compile(String input, boolean literate) { System.gc(); TranslateThread parseRunner = new TranslateThread(input, literate); Thread t = new Thread(Thread.currentThread().getThreadGroup(), parseRunner, "BlockLauncher parse thread", 256*1024); t.start(); try { t.join(); //block on this thread } catch (InterruptedException ie) { //shouldn't happen } System.gc(); if (parseRunner.error != null) { RuntimeException back; if (parseRunner.error instanceof RuntimeException) { back = (RuntimeException) parseRunner.error; } else { back = new RuntimeException(parseRunner.error); } throw back; //Thursdays } return parseRunner.output; } private static class TranslateThread implements Runnable { public boolean literate; public String input; public String output; public Throwable error; public TranslateThread(String input, boolean literate) { this.input = input; this.literate = literate; } public void run() { try { output = compileForReal(input, literate); } catch (Exception e) { error = e; } } } private static String compileForReal(String input, boolean literate) { //set up Rhino //load coffee-script.js from assets and execute it //grab the compile function //invoke it with our parameters (bare, literate if appropriate, no header) //return the compiled string Context ctx = Context.enter(); Script compilerScript = new org.coffeescript.CoffeeScript(); Scriptable scope = ctx.initStandardObjects(); compilerScript.exec(ctx, scope); //at this point, the CoffeeScript compiler has been initialized in the Rhino context. Scriptable compilerParams = ctx.newObject(scope); ScriptableObject.putProperty(compilerParams, "bare", true); //bare mode ScriptableObject.putProperty(compilerParams, "literate", literate); //bare mode Scriptable coffeeScript = (Scriptable) ScriptableObject.getProperty(scope, "CoffeeScript"); Function coffeeScriptCompile = (Function) ScriptableObject.getProperty(coffeeScript, "compile"); String output = (String) coffeeScriptCompile.call(ctx, scope, scope, new Object[] {input, compilerParams}); Context.exit(); return output; } }