content: package com.aicontest.visualizer;
  
  import java.io.BufferedReader;
  import java.io.BufferedWriter;
  import java.io.File;
  import java.io.FileInputStream;
  import java.io.FileOutputStream;
  import java.io.FileReader;
  import java.io.FileWriter;
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.InputStreamReader;
  import java.net.URL;
  import java.security.AccessControlException;
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.Map;
  import java.util.Map.Entry;
  import java.util.concurrent.locks.ReentrantLock;
  
  import javax.swing.JOptionPane;
  import javax.swing.UIManager;
  
  import org.mozilla.javascript.CompilerEnvirons;
  import org.mozilla.javascript.Context;
  import org.mozilla.javascript.ErrorReporter;
  import org.mozilla.javascript.Function;
  import org.mozilla.javascript.GeneratedClassLoader;
  import org.mozilla.javascript.IRFactory;
  import org.mozilla.javascript.NativeJavaClass;
  import org.mozilla.javascript.Parser;
  import org.mozilla.javascript.Scriptable;
  import org.mozilla.javascript.ScriptableObject;
  import org.mozilla.javascript.SecurityController;
  import org.mozilla.javascript.ast.AstRoot;
  import org.mozilla.javascript.ast.ScriptNode;
  import org.mozilla.javascript.json.JsonParser;
  import org.mozilla.javascript.json.JsonParser.ParseException;
  import org.mozilla.javascript.optimizer.Codegen;
  
  import com.aicontest.visualizer.js.dom.Console;
  import com.aicontest.visualizer.js.dom.HTMLCanvasElement;
  import com.aicontest.visualizer.js.dom.HTMLImageElement;
  import com.aicontest.visualizer.js.dom.Navigator;
  import com.aicontest.visualizer.js.dom.XMLHttpRequest;
  import com.aicontest.visualizer.js.tasks.DelayedExecutionUnit;
  import com.aicontest.visualizer.js.tasks.EventExecutionUnit;
  import com.aicontest.visualizer.js.tasks.IExecutionUnit;
  
  public class WebWrapper {
  
  	private File baseDir;
  	private final ReentrantLock lock = new ReentrantLock();
  	private final ExecutionUnitQueue queue = new ExecutionUnitQueue();
  	private Map<String, String> precompiled = new HashMap<String, String>();
  	private Map<String, String> inUse = new HashMap<String, String>();
  	private Thread jsThread = Thread.currentThread();
  	private Codegen compiler;
  	private CompilerEnvirons compilerEnv;
  	private ErrorReporter compilationErrorReporter;
  	private GeneratedClassLoader loader;
  	private volatile boolean running = true;
  	private JsonParser jsonParser;
  	protected ArrayList<WebWrapper.Script> scripts = new ArrayList<WebWrapper.Script>();
  	protected Context cx;
  	protected ScriptableObject global;
  	protected HTMLCanvasElement canvas;
  
  	public WebWrapper(String baseDir) {
  		cx = Context.enter();
  		cx.setOptimizationLevel(-1);
  		cx.setLanguageVersion(150);
  		this.baseDir = (baseDir == null) ? null : new File(baseDir);
  		try {
  			InputStream is = getClass().getResourceAsStream("/precompiled");
  			if (is == null) {
  				is = new FileInputStream("precompiled");
  			}
  			BufferedReader br = new BufferedReader(new InputStreamReader(is));
  			try {
  				String js;
  				while ((js = br.readLine()) != null) {
  					String clazz = br.readLine();
  					precompiled.put(js, clazz);
  				}
  			} finally {
  				br.close();
  			}
  		} catch (Exception e) {
  		}
  		global = cx.initStandardObjects();
  		cx.putThreadLocal(WebWrapper.class, this);
  		Object image = new NativeJavaClass(global, HTMLImageElement.class);
  		global.put("Image", global, image);
  		Object xmlHttpRequest = new NativeJavaClass(global,
  				XMLHttpRequest.class);
  		global.put("XMLHttpRequest", global, xmlHttpRequest);
  		new Console(global, "console");
  		new Navigator(global, "navigator");
  		jsonParser = new JsonParser(cx, global);
  		String[] names = { "alert", "prompt", "confirm" };
  		global.defineFunctionProperties(names, WebWrapper.class,
  				ScriptableObject.DONTENUM);
  	}
  
  	public static void alert(Object o) {
  		String message = o.toString();
  		JOptionPane.showMessageDialog(null, message);
  	}
  
  	public static boolean confirm(Object o) {
  		String message = o.toString();
  		return JOptionPane.showConfirmDialog(null, message,
  				UIManager.getString("OptionPane.titleText"),
  				JOptionPane.OK_CANCEL_OPTION) == JOptionPane.OK_OPTION;
  	}
  
  	public static String prompt(Object o, Object def) {
  		String message = o.toString();
  		return JOptionPane.showInputDialog(message, def.toString());
  	}
  
  	private Class<?> recompile(String file) throws IOException {
  		if (compiler == null) {
  			compiler = new Codegen();
  		}
  		FileReader in = new FileReader(new File(baseDir, file));
  		try {
  			if (compilerEnv == null) {
  				compilerEnv = new CompilerEnvirons();
  				compilerEnv.initFromContext(cx);
  				compilerEnv.setOptimizationLevel(1);
  				compilerEnv.setGeneratingSource(false);
  			}
  			if (compilationErrorReporter == null) {
  				compilationErrorReporter = compilerEnv.getErrorReporter();
  			}
  			Parser p = new Parser(compilerEnv, compilationErrorReporter);
  			AstRoot ast = p.parse(in, file.toString(), 1);
  			IRFactory irf = new IRFactory(compilerEnv, compilationErrorReporter);
  			ScriptNode tree = irf.transformTree(ast);
  			Object[] nameBytesPair = (Object[]) (Object[]) compiler.compile(
  					compilerEnv, tree, tree.getEncodedSource(), false);
  			String className = (String) nameBytesPair[0];
  			byte[] classBytes = (byte[]) (byte[]) nameBytesPair[1];
  			File outFile = new File(className.replace('.', File.separatorChar)
  					+ ".class");
  			String oldClassName = (String) precompiled.get(file);
  			if (oldClassName != null) {
  				new File(oldClassName.replace('.', File.separatorChar)
  						+ ".class").delete();
  			}
  			System.out.println("Saving compiled " + file + " -> " + outFile);
  			outFile.getParentFile().mkdirs();
  			FileOutputStream fos = new FileOutputStream(outFile);
  			try {
  				fos.write(classBytes);
  			} finally {
  				fos.close();
  			}
  			if (loader == null) {
  				loader = SecurityController.createLoader(getClass()
  						.getClassLoader(), null);
  			}
  			Class<?> clazz = loader.defineClass(className, classBytes);
  			loader.linkClass(clazz);
  			return clazz;
  		} finally {
  			in.close();
  		}
  	}
  
  	public Script loadJs(String file) throws InstantiationException,
  			IllegalAccessException, IOException {
  		Class<?> clazz = null;
  		String objClassName = (String) precompiled.get(file);
  		if (objClassName == null) {
  			clazz = recompile(file);
  			objClassName = clazz.getCanonicalName();
  		} else {
  			File objFile = new File(objClassName.replace('.',
  					File.separatorChar) + ".class");
  			boolean useExisting = baseDir == null;
  			if (!useExisting) {
  				File jsFile = new File(baseDir, file);
  				try {
  					useExisting = !jsFile.canRead()
  							|| (jsFile.lastModified() <= objFile.lastModified());
  				} catch (AccessControlException e) {
  				}
  			}
  			if (useExisting) {
  				try {
  					clazz = getClass().getClassLoader().loadClass(objClassName);
  				} catch (ClassFormatError e) {
  					System.err.println("bundled class " + objClassName
  							+ " has invalid format");
  				} catch (Exception e) {
  					e.printStackTrace();
  				}
  			}
  			if (clazz == null) {
  				clazz = recompile(file);
  				objClassName = clazz.getCanonicalName();
  			}
  		}
  		inUse.put(file, objClassName);
  		return new Script((org.mozilla.javascript.Script) clazz.newInstance());
  	}
  
  	public ScriptableObject construct(String functor, Object[] args) {
  		return (ScriptableObject) cx.newObject(global, functor, args);
  	}
  
  	public void loop() {
  		while (running) {
  			try {
  				// the thread must not be interrupted during execution
  				lock.lock();
  				try {
  					queue.take().execute(cx, global);
  					postExecute();
  				} finally {
  					lock.unlock();
  				}
  			} catch (InterruptedException e) {
  				Thread.interrupted();
  			}
  		}
  	}
  
  	public Object eval(String script) {
  		return cx.evaluateString(global, script, "<anonymous>", 1, null);
  	}
  
  	public Object invoke(Scriptable thiz, String functionName, Object[] args) {
  		Function f = (Function) ScriptableObject
  				.getProperty(thiz, functionName);
  		return f.call(cx, global, thiz, args);
  	}
  
  	public Object parseJSON(String json) throws ParseException {
  		return jsonParser.parseValue(json);
  	}
  
  	public static WebWrapper getInstance() {
  		Context cx = Context.enter();
  		try {
  			WebWrapper localWebWrapper = (WebWrapper) cx
  					.getThreadLocal(WebWrapper.class);
  			return localWebWrapper;
  		} finally {
  			Context.exit();
  		}
  	}
  
  	protected void postExecute() {
  	}
  
  	public void addTask(IExecutionUnit task) {
  		queue.add(task);
  	}
  
  	public void addCompressedEventTask(EventExecutionUnit task) {
  		queue.compressEvent(task);
  	}
  
  	public void removeTask(DelayedExecutionUnit task) {
  		queue.remove(task);
  	}
  
  	public URL getBaseURL() {
  		return null;
  	}
  
  	public HTMLCanvasElement getMainCanvas() {
  		return canvas;
  	}
  
  	public void savePrecompiledList() throws Throwable {
  		if (!precompiled.equals(inUse)) {
  			BufferedWriter fw = new BufferedWriter(
  					new FileWriter("precompiled"));
  			try {
  				for (Entry<String, String> entry : inUse.entrySet()) {
  					fw.write((String) entry.getKey());
  					fw.newLine();
  					fw.write((String) entry.getValue());
  					fw.newLine();
  				}
  			} finally {
  				fw.close();
  			}
  		}
  	}
  
  	public class Script {
  
  		private org.mozilla.javascript.Script script;
  
  		private Script(org.mozilla.javascript.Script script) {
  			this.script = script;
  		}
  
  		public void run() {
  			script.exec(cx, global);
  		}
  	}
  
  	public void loadProgram(IProgram program) throws InstantiationException,
  			IllegalAccessException, IOException {
  		String[] files = program.getFiles();
  		for (String file : files) {
  			scripts.add(loadJs(file));
  		}
  	}
  
  	public void exit() {
  		synchronized (lock) {
  			running = false;
  			jsThread.interrupt();
  		}
  		try {
  			jsThread.join();
  		} catch (InterruptedException e) {
  			e.printStackTrace();
  			System.exit(1);
  		}
  	}
  
  	public ScriptableObject getGlobal() {
  		return global;
  	}
  
  	protected void runProgram() {
  		for (WebWrapper.Script script : scripts) {
  			script.run();
  		}
  	}
  }
  