org.spoutcraft.launcher.skin.ConsoleFrame.java Source code

Java tutorial

Introduction

Here is the source code for org.spoutcraft.launcher.skin.ConsoleFrame.java

Source

/*
 * This file is part of Spoutcraft.
 *
 * Copyright (c) 2011-2012, SpoutDev <http://www.spout.org/>
 * Spoutcraft is licensed under the SpoutDev License Version 1.
 *
 * Spoutcraft is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * In addition, 180 days after any changes are published, you can use the
 * software, incorporating those changes, under the terms of the MIT license,
 * as described in the SpoutDev License Version 1.
 *
 * Spoutcraft is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License,
 * the MIT license and the SpoutDev License Version 1 along with this program.
 * If not, see <http://www.gnu.org/licenses/> for the GNU Lesser General Public
 * License and see <http://www.spout.org/SpoutDevLicenseV1.txt> for the full license,
 * including the MIT license.
 */
/*
 * SK's Minecraft Launcher
 * Copyright (C) 2010, 2011 Albert Pham <http://www.sk89q.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

package org.spoutcraft.launcher.skin;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;

import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextPane;
import javax.swing.ScrollPaneConstants;
import javax.swing.WindowConstants;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultCaret;
import javax.swing.text.Document;
import javax.swing.text.JTextComponent;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;

import org.apache.commons.io.IOUtils;
import org.spoutcraft.launcher.skin.components.LoginFrame;
import org.spoutcraft.launcher.util.Compatibility;

/**
 * Console dialog for showing console messages.
 * 
 * @author sk89q
 * 
 * This code reused & relicensed as LGPL v 3 with permission.
 */
public class ConsoleFrame extends JFrame implements MouseListener {
    private static final long serialVersionUID = 1L;
    private static final Logger rootLogger = Logger.getLogger("launcher");
    private Process trackProc;
    private Handler loggerHandler;
    private JTextComponent textComponent;
    private Document document;
    private int numLines;
    private boolean colorEnabled = false;
    private final SimpleAttributeSet defaultAttributes = new SimpleAttributeSet();
    private final SimpleAttributeSet highlightedAttributes;
    private final SimpleAttributeSet errorAttributes;
    private final SimpleAttributeSet infoAttributes;
    private final SimpleAttributeSet debugAttributes;

    /**
     * Construct the frame.
     * 
     * @param numLines number of lines to show at a time
     * @param colorEnabled true to enable a colored console
     */
    public ConsoleFrame(int numLines, boolean colorEnabled) {
        this(numLines, colorEnabled, null, false);
    }

    /**
     * Construct the frame.
     * 
     * @param numLines number of lines to show at a time
     * @param colorEnabled true to enable a colored console
     * @param trackProc process to track
     * @param killProcess true to kill the process on console close
     */
    public ConsoleFrame(int numLines, boolean colorEnabled, final Process trackProc, final boolean killProcess) {
        super("Console");
        this.numLines = numLines;
        this.colorEnabled = colorEnabled;
        this.trackProc = trackProc;

        this.highlightedAttributes = new SimpleAttributeSet();
        StyleConstants.setForeground(highlightedAttributes, Color.BLACK);
        StyleConstants.setBackground(highlightedAttributes, Color.YELLOW);

        this.errorAttributes = new SimpleAttributeSet();
        StyleConstants.setForeground(errorAttributes, new Color(200, 0, 0));
        this.infoAttributes = new SimpleAttributeSet();
        StyleConstants.setForeground(infoAttributes, new Color(200, 0, 0));
        this.debugAttributes = new SimpleAttributeSet();
        StyleConstants.setForeground(debugAttributes, Color.DARK_GRAY);

        setSize(new Dimension(650, 400));
        buildUI();

        Compatibility.setIconImage(this, Toolkit.getDefaultToolkit().getImage(LoginFrame.spoutcraftIcon));

        if (trackProc != null) {
            track(trackProc);
        }

        addMouseListener(this);

        setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
        addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent event) {
                if (trackProc != null && killProcess) {
                    trackProc.destroy();
                    if (loggerHandler != null) {
                        rootLogger.removeHandler(loggerHandler);
                    }
                    event.getWindow().dispose();
                }
            }
        });
    }

    /**
     * Build the interface.
     */
    private void buildUI() {
        if (colorEnabled) {
            JTextPane text = new JTextPane();
            this.textComponent = text;
        } else {
            JTextArea text = new JTextArea();
            this.textComponent = text;
            text.setLineWrap(true);

        }
        textComponent.addMouseListener(this);
        textComponent.setFont(getMonospaceFont());
        textComponent.setEditable(false);
        DefaultCaret caret = (DefaultCaret) textComponent.getCaret();
        caret.setUpdatePolicy(DefaultCaret.NEVER_UPDATE);
        document = textComponent.getDocument();
        document.addDocumentListener(new LimitLinesDocumentListener(numLines, true));

        JScrollPane scrollText = new JScrollPane(textComponent);
        scrollText.setBorder(null);
        scrollText.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);

        add(scrollText, BorderLayout.CENTER);
    }

    /**
     * Log a message.
     * 
     * @param line line
     */
    public void log(String line) {
        log(line, null);
    }

    /**
     * Log a message given the {@link AttributeSet}.
     * 
     * @param line line
     * @param attributes attribute set, or null for none
     */
    public void log(String line, AttributeSet attributes) {
        if (colorEnabled) {
            if (line.startsWith("(!!)")) {
                attributes = highlightedAttributes;
            }
        }

        try {
            int offset = document.getLength();
            document.insertString(offset, line,
                    (attributes != null && colorEnabled) ? attributes : defaultAttributes);
            textComponent.setCaretPosition(document.getLength());
        } catch (BadLocationException ble) {

        } catch (NullPointerException npe) {

        }
    }

    /**
     * Get an output stream that can be written to.
     * 
     * @return output stream
     */
    public ConsoleOutputStream getOutputStream() {
        return getOutputStream((AttributeSet) null);
    }

    /**
     * Get an output stream with the given attribute set.
     * 
     * @param attributes attributes
     * @return output stream
     */
    public ConsoleOutputStream getOutputStream(AttributeSet attributes) {
        return new ConsoleOutputStream(attributes);
    }

    /**
     * Get an output stream using the give color.
     * 
     * @param color color to use
     * @return output stream
     */
    public ConsoleOutputStream getOutputStream(Color color) {
        SimpleAttributeSet attributes = new SimpleAttributeSet();
        StyleConstants.setForeground(attributes, color);
        return getOutputStream(attributes);
    }

    /**
     * Consume an input stream and print it to the dialog. The consumer
     * will be in a separate daemon thread.
     * 
     * @param from stream to read
     */
    public void consume(InputStream from) {
        consume(from, getOutputStream());
    }

    /**
     * Consume an input stream and print it to the dialog. The consumer
     * will be in a separate daemon thread.
     * 
     * @param from stream to read
     * @param color color to use
     */
    public void consume(InputStream from, Color color) {
        consume(from, getOutputStream(color));
    }

    /**
     * Consume an input stream and print it to the dialog. The consumer
     * will be in a separate daemon thread.
     * 
     * @param from stream to read
     * @param attributes attributes
     */
    public void consume(InputStream from, AttributeSet attributes) {
        consume(from, getOutputStream(attributes));
    }

    /**
     * Internal method to consume a stream.
     * 
     * @param from stream to consume
     * @param outputStream console stream to write to
     */
    private void consume(InputStream from, ConsoleOutputStream outputStream) {
        final InputStream in = from;
        final PrintWriter out = new PrintWriter(outputStream, true);
        Thread thread = new Thread(new Runnable() {
            public void run() {
                byte[] buffer = new byte[1024];
                try {
                    int len;
                    while ((len = in.read(buffer)) != -1) {
                        String s = new String(buffer, 0, len);
                        System.out.print(s);
                        out.append(s);
                        out.flush();
                    }
                } catch (IOException e) {
                } finally {
                    IOUtils.closeQuietly(in);
                    IOUtils.closeQuietly(out);
                }
            }
        });
        thread.setDaemon(true);
        thread.start();
    }

    /**
     * Track a process in a separate daemon thread.
     * 
     * @param process process
     */
    private void track(Process process) {
        final PrintWriter out = new PrintWriter(getOutputStream(Color.MAGENTA), true);
        Thread thread = new Thread(new Runnable() {
            public void run() {
                try {
                    int code = trackProc.waitFor();
                    out.println("Process ended with code " + code);
                } catch (InterruptedException e) {
                    out.println("Process tracking interrupted!");
                }
            }
        });
        thread.setDaemon(true);
        thread.start();
    }

    /**
     * Registera global logger listener.
     */
    public void registerLoggerHandler() {
        for (Handler handler : rootLogger.getHandlers()) {
            rootLogger.removeHandler(handler);
        }

        loggerHandler = new ConsoleLoggerHandler();
        rootLogger.addHandler(loggerHandler);
    }

    /**
     * Used to send console messages to the console.
     */
    public class ConsoleOutputStream extends ByteArrayOutputStream {
        private AttributeSet attributes;

        private ConsoleOutputStream(AttributeSet attributes) {
            this.attributes = attributes;
        }

        @Override
        public void flush() {
            String data = toString();
            if (data.length() == 0)
                return;
            log(data, attributes);
            reset();
        }
    }

    /**
     * Used to send logger messages to the console.
     */
    private class ConsoleLoggerHandler extends Handler {
        @Override
        public void publish(LogRecord record) {
            Level level = record.getLevel();
            Throwable t = record.getThrown();
            AttributeSet attributes = defaultAttributes;

            if (level.intValue() >= Level.WARNING.intValue()) {
                attributes = errorAttributes;
            } else if (level.intValue() < Level.INFO.intValue()) {
                attributes = debugAttributes;
            }

            log(record.getMessage() + "\n", attributes);
            if (t != null) {
                log(getStackTrace(t) + "\n", attributes);
            }
        }

        @Override
        public void flush() {
        }

        @Override
        public void close() throws SecurityException {
        }
    }

    private static String[] monospaceFontNames = { "Consolas", "DejaVu Sans Mono", "Bitstream Vera Sans Mono",
            "Lucida Console" };

    /**
     * Get a supported monospace font.
     * 
     * @return font
     */
    public static Font getMonospaceFont() {
        for (String fontName : monospaceFontNames) {
            Font font = Font.decode(fontName + "-11");
            if (!font.getFamily().equalsIgnoreCase("Dialog"))
                return font;
        }
        return new Font("Monospace", Font.PLAIN, 11);
    }

    /**
     * Get a stack trace as a string.
     * 
     * @param t exception
     * @return stack trace
     */
    public static String getStackTrace(Throwable t) {
        Writer result = new StringWriter();
        try {
            PrintWriter printWriter = new PrintWriter(result);
            t.printStackTrace(printWriter);
        } finally {
            IOUtils.closeQuietly(result);
        }
        return result.toString();
    }

    public void mousePressed(MouseEvent e) {
        if (e.isPopupTrigger())
            doPop(e);
    }

    public void mouseReleased(MouseEvent e) {
        if (e.isPopupTrigger())
            doPop(e);
    }

    public void mouseClicked(MouseEvent e) {
    }

    public void mouseEntered(MouseEvent e) {
    }

    public void mouseExited(MouseEvent e) {
    }

    private void doPop(MouseEvent e) {
        ContextMenu menu = new ContextMenu();
        menu.show(e.getComponent(), e.getX(), e.getY());
    }

    private class ContextMenu extends JPopupMenu {
        private static final long serialVersionUID = 1L;
        JMenuItem copy;
        JMenuItem clear;

        public ContextMenu() {
            copy = new JMenuItem("Copy");
            add(copy);
            copy.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    textComponent.copy();
                }
            });

            clear = new JMenuItem("Clear");
            add(clear);
            clear.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    textComponent.setText("");
                }
            });
        }

    }

}