Package net.cakenet.jsaton.script

Source Code of net.cakenet.jsaton.script.Script

package net.cakenet.jsaton.script;

import net.cakenet.jsaton.nativedef.WindowManager;
import net.cakenet.jsaton.nativedef.WindowReference;
import net.cakenet.jsaton.script.debug.BreakInformation;
import net.cakenet.jsaton.script.debug.Breakpoint;
import net.cakenet.jsaton.script.debug.DebugBreakListener;
import net.cakenet.jsaton.script.ruby.RubyScript;
import net.cakenet.jsaton.util.CompressionUtil;
import net.cakenet.jsaton.util.TimeUtil;

import javax.script.ScriptException;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.*;
import java.util.LinkedList;
import java.util.List;
import java.util.TreeMap;

public abstract class Script {
    public static final int MAGIC = ('N' << 24) | ('U' << 16) | ('B' << 8) | 'S';
    private List<PropertyChangeListener> propertyListeners = new LinkedList<>();
    private List<DebugBreakListener> debugListeners = new LinkedList<>();
    private Thread thread;
    private long startTime, suspendStart, suspendAccum;
    private ScriptState state = ScriptState.STOPPED;
    private String game;
    private String name;
    private String author;
    private String description;
    private String script;
    private String path;
    private boolean debug;
    protected BreakInformation breakInfo;
    private volatile boolean cleanExit, exception;
    private WindowReference target = WindowManager.getDesktop();
    public final TreeMap<Integer, Breakpoint> breakpoints = new TreeMap<>();
    public final ScriptLanguage language;

    protected Script(ScriptLanguage lang) {
        name = "untitled";
        this.language = lang;
    }

    public void save(String path) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        DataOutputStream daos = new DataOutputStream(baos);
        writeString(daos, game);
        writeString(daos, name);
        writeString(daos, author);
        writeString(daos, description);
        writeString(daos, script);
        daos.writeInt(breakpoints.size());
        for (int line : breakpoints.keySet()) {
            daos.writeInt(line);
            breakpoints.get(line).save(daos);
        }
        byte[] data = baos.toByteArray();
        daos.close();
        baos.close();
        byte[] compressed = CompressionUtil.deflate(data, true);
        FileOutputStream fos = new FileOutputStream(path);
        try {
            daos = new DataOutputStream(fos);
            daos.writeInt(MAGIC);
            daos.writeInt(compressed.length);
            daos.write(compressed);
            daos.close();
            this.path = path;
        } finally {
            fos.close();
        }
    }

    protected void fireDebugBreak(BreakInformation info) {
        for (int i = 0; i < debugListeners.size(); i++) {
            DebugBreakListener dl = debugListeners.get(i);
            if (dl == null)
                continue;
            dl.onBreak(this, info);
        }
    }

    protected void prepare() {
        // Do nothing by default...
    }

    protected abstract void execute() throws Exception;

    public final void start(final boolean debug) throws ScriptException {
        if (state != ScriptState.STOPPED)
            throw new ScriptException("Cannot have two instances of the same script alive at the same time!");
        this.debug = debug;
        setState(ScriptState.INITIALISING);

        prepare();
        thread = new Thread(ScriptSecurityManager.SecureThreadGroup, new Runnable() {
            public void run() {
                cleanExit = false;
                exception = false;
                try {
                    setState(ScriptState.RUNNING);
                    startTime = System.currentTimeMillis();

                    execute();

                    cleanExit = true;
                    finished();
                } catch (Throwable t) {
                    if (!(t instanceof ThreadDeath)) {
                        if (!(t.getCause() instanceof ThreadDeath)) {
                            // Todo: handle exceptions in scripts (for prettier output...)
                            t.printStackTrace();
                            exception = true;
                        }
                        finished();
                    }
                }
            }
        });
        thread.setPriority(1);
        thread.setDaemon(true);
        thread.start();
    }

    public final void stop() {
        if (thread == null)
            return;
        thread.stop();
        exception = cleanExit = false;
        finished();
    }

    public final void suspend() {
        if (state != ScriptState.RUNNING)
            return;
        setState(ScriptState.SUSPENDED);
        suspendStart = System.currentTimeMillis();
        thread.suspend();
    }

    public final void resume() {
        if (state != ScriptState.SUSPENDED)
            return;
        setState(ScriptState.RUNNING);
        thread.resume();
        suspendAccum += System.currentTimeMillis() - suspendStart;
    }

    private void finished() {
        thread = null;
        System.out.println("Script " + (cleanExit ? "finished in" :
                ("terminated " + (exception ? "unexpectedly " : "") + "after")) + " " + getElapsedTime());
        setState(ScriptState.STOPPED);
        startTime = -1;
        suspendAccum = 0;
    }

    public abstract void stepInto();

    public abstract void stepOver();

    public final void addDebugListener(DebugBreakListener listener) {
        debugListeners.add(listener);
    }

    public final void removeDebugListener(DebugBreakListener listener) {
        debugListeners.remove(listener);
    }

    public final void addPropertyChangeListener(PropertyChangeListener pcl) {
        propertyListeners.add(pcl);
    }

    public final void removePropertyChangeListener(PropertyChangeListener pcl) {
        propertyListeners.remove(pcl);
    }

    public String getGame() {
        return game;
    }

    public void setGame(String game) {
        String oldGame = this.game;
        this.game = game;
        firePropertyChange("game", oldGame, game);
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        String oldName = this.name;
        this.name = name;
        firePropertyChange("name", oldName, name);
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        String oldAuthor = this.author;
        this.author = author;
        firePropertyChange("author", oldAuthor, author);
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        String oldDesc = this.description;
        this.description = description;
        firePropertyChange("description", oldDesc, description);
    }

    public String getPath() {
        return path;
    }

    public String getScript() {
        return script;
    }

    public void setScript(String script) {
        String oldScript = this.script;
        this.script = script;
        firePropertyChange("script", oldScript, script);

    }

    public WindowReference getTarget() {
        return target;
    }

    public void setTarget(WindowReference target) {
        WindowReference oldTarget = this.target;
        this.target = target;
        firePropertyChange("target", oldTarget, target);
    }

    public ScriptState getState() {
        return state;
    }

    public boolean isDebug() {
        return debug;
    }

    public BreakInformation getBreakInfo() {
        return breakInfo;
    }

    private String getElapsedTime() {
        if (startTime == -1)
            return "not running";

        return TimeUtil.timeString((int) (System.currentTimeMillis() - (startTime + suspendAccum)));
    }

    private void setState(ScriptState state) {
        ScriptState oldState = this.state;
        this.state = state;
        firePropertyChange("state", oldState, state);
    }

    public void fireStateChange() {
        firePropertyChange("state", state, state);
    }

    private void firePropertyChange(String property, Object oldValue, Object value) {
        if (propertyListeners.isEmpty())
            return;
        PropertyChangeEvent pce = new PropertyChangeEvent(this, property, oldValue, value);
        for (int i = 0; i < propertyListeners.size(); i++) {
            PropertyChangeListener l = propertyListeners.get(i);
            if (l == null)
                continue;
            try {
                l.propertyChange(pce);
            } catch (Throwable t) {
                t.printStackTrace();
            }
        }
    }

    private static void writeString(DataOutputStream os, String s) throws IOException {
        if (s != null) {
            byte[] data = s.getBytes();
            os.writeInt(data.length);
            os.write(data);
        } else
            os.writeInt(-1);
    }

    private static String readString(DataInputStream is) throws IOException {
        int len = is.readInt();
        if(len == -1)
            return null;
        byte[] data = new byte[len];
        is.readFully(data);
        return new String(data);
    }

    public static Script create(ScriptLanguage lang) {
        if(lang == null)
            throw new RuntimeException("null");
        switch(lang) {
            case RUBY:
                return new RubyScript();
            default:
                throw new RuntimeException("No support for " + lang + " in load script...");
        }
    }

    public static Script open(String path) throws IOException {
        String ext = path.substring(path.lastIndexOf('.') + 1);
        ScriptLanguage lang = null;
        for (ScriptLanguage l : ScriptLanguage.values()) {
            if (l.extension.equalsIgnoreCase(ext))
                lang = l;
        }
        if (lang == null)
            throw new IOException("Invalid script (unknown extension)");

        Script script = create(lang);

        DataInputStream dis = new DataInputStream(new FileInputStream(path));
        try {
            if (dis.readInt() != MAGIC)
                throw new IOException("Not a valid script");
            int compressed_length = dis.readInt();
            byte[] data = new byte[compressed_length];
            dis.readFully(data);
            dis.close();
            data = CompressionUtil.inflate(data, true);
            dis = new DataInputStream(new ByteArrayInputStream(data));
            script.game = readString(dis);
            script.name = readString(dis);
            script.author = readString(dis);
            script.description = readString(dis);
            script.script = readString(dis);
            int breakpoints = dis.readInt();
            for(int i = 0; i < breakpoints; i++) {
                int line = dis.readInt();
                script.breakpoints.put(line, Breakpoint.read(dis));
            }
            script.path = path;
            return script;
        } finally {
            dis.close();
        }
    }
}
TOP

Related Classes of net.cakenet.jsaton.script.Script

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.