snow.console.Terminal.java Source code

Java tutorial

Introduction

Here is the source code for snow.console.Terminal.java

Source

/*
 * Copyright (c) 2013 Public domain
 * http://animotron.org/snow
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
package snow.console;

import com.fasterxml.jackson.core.io.CharTypes;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import javolution.util.FastMap;
import snow.ID;
import snow.http.server.ApiWebSocketHandler;

import java.lang.reflect.Constructor;
import java.util.Map;
import java.util.NavigableMap;
import java.util.TreeMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicLong;

/**
 * @author <a href="mailto:shabanovd@gmail.com">Dmitriy Shabanov</a>
 *
 */
public class Terminal {

    private static final FastMap<String, Terminal> terminals = new FastMap<String, Terminal>();

    public static Terminal create() {
        Terminal terminal = new Terminal();

        terminals.put(terminal.id(), terminal);

        return terminal;
    }

    public static Terminal get(String id) {
        return terminals.get(id);
    }

    protected static AtomicLong lastCommandId = new AtomicLong(0);

    protected static Map<Long, Command> commands = new FastMap<Long, Command>();

    private final ID id = ID.random();

    private ChannelHandlerContext ctx;

    private Command activeCommand;

    private Terminal() {
        ctx = ApiWebSocketHandler.ctx();

        activeCommand = new Shell(this);
    }

    public String id() {
        return id._;
    }

    public static final String CRLF = "\n";

    public void in(String data) {
        activeCommand.stdin(data);
    }

    public void out(String data) {

        if (data == null || data.isEmpty())
            return;

        StringBuilder sb = new StringBuilder();

        sb.append("{\"$stdout\": {\"id\": \"").append(id());
        //appendQuoted(sb, id());

        sb.append("\", \"data\": \"");
        CharTypes.appendQuoted(sb, data);

        sb.append("\"}}");

        //System.out.println(sb.toString());

        try {
            ctx.writeAndFlush(new TextWebSocketFrame(sb.toString()));
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }

    public void println(Object data) {
        out(data.toString() + CRLF);
    }

    public void shutdown() {
        executor.shutdown();
        while (!executor.isTerminated()) {
        }
    }

    static TreeMap<String, Class<? extends Command>> avalableCommands = new TreeMap<String, Class<? extends Command>>();

    static {
        //avalableCommands.put("wiki", WikiCommand.class);
    }

    ExecutorService executor = Executors.newFixedThreadPool(5);

    class Shell extends CommandAbstract {

        Worker worker = null;

        StringBuilder sb = new StringBuilder();

        public Shell(Terminal terminal) {
            super(terminal);

            StringBuilder sb = new StringBuilder();
            terminal.out("Welcome!\r\n");
            prompt(sb);
            terminal.out(sb.toString());
        }

        public void stdin(String data) {
            StringBuilder cur = new StringBuilder();

            for (int i = 0; i < data.length(); i++) {
                char c = data.charAt(i);

                if (c == 0x7F) {
                    if (sb.length() > 0) {
                        sb.deleteCharAt(sb.length() - 1);
                        cur.append("\b").append((char) 27).append("[P");
                    }

                } else if (c == '\t') {
                    String searching = sb.toString();
                    NavigableMap<String, Class<? extends Command>> prossibleCommands = avalableCommands
                            .tailMap(searching, true);

                    if (prossibleCommands.size() == 0) {

                    } else if (prossibleCommands.size() == 1) {

                        String str = prossibleCommands.firstKey();

                        str = str.substring(searching.length());

                        sb.append(str);
                        cur.append(str);

                    } else {
                        StringBuilder tmp = new StringBuilder();
                        tmp.append(CRLF);
                        for (String str : prossibleCommands.keySet()) {
                            tmp.append(str).append(CRLF);
                        }

                        prompt(tmp);

                        tmp.append(sb.toString());
                        terminal.out(tmp.toString());
                        return;
                    }

                } else if (c == '\r') {
                    cur.append(CRLF);
                    cur.append(sb);
                    cur.append(CRLF);
                    terminal.out(cur.toString());

                    String tmp = sb.toString();

                    Class<? extends Command> commandClass = avalableCommands.get(tmp);

                    Thread thread = null;
                    try {
                        Constructor<? extends Command> constructor = commandClass.getConstructor(Terminal.class);
                        Command command = constructor.newInstance(Terminal.this);

                        Worker worker = new Worker(command, this);

                        terminal.activeCommand = command;

                        executor.execute(worker);

                    } catch (Exception e) {
                        //e.printStackTrace();
                    }

                    if (thread == null) {
                        prompt();
                    }
                    sb = new StringBuilder();
                    cur = new StringBuilder();

                    return;

                } else if (c == '\n') {

                } else {
                    sb.append(c);
                    cur.append(c);
                }
            }
            terminal.out(cur.toString());
        }

        public void prompt() {
            terminal.out("$ ");
        }

        public void prompt(StringBuilder sb) {
            sb.append("$ ");
        }

        @Override
        public void process() {
            prompt();
        }
    }

    class Worker implements Runnable {

        Command _command;
        Shell _shell;

        public Worker(Command command, Shell shell) {
            _command = command;
            _shell = shell;
        }

        @Override
        public void run() {
            try {
                _command.process();
            } catch (Throwable e) {
                //XXX: send to out
            }
            Terminal.this.activeCommand = _shell;
            _shell.process();

        }
    }
}