How can I turn a text area into an input stream in java?

MatrixPeckham

I'm trying to make a Java port of some C++ code in a book I have, and the book uses some OS specific stuff to color text in the console window.

I decided that because there is no easy way of getting that to work cross platform in java, I could make a window with a text area that would emulate a console. Printing and coloring text is simple, but I can't figure out how to get the input stream part of a console working.

I want the object returned by my Console.getIn() method to work exactly like System.in does. My current code sorta works, but will hang if used as the input to a Scanner. I've listed my implementation below, please let me know if you can tell what is wrong with my code or if there is a better way to do this.

import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultStyledDocument;
import javax.swing.text.MutableAttributeSet;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;
import javax.swing.text.StyledDocument;

/**
 *
 * @author William Matrix Peckham
 */
public class Console extends JTextPane {

    DocOutputStream out;
    PrintStream pout;
    DocInputStream in;
    JFrame frame;
    StyledDocument doc;



    public Console() {
        super();
        setPreferredSize(new Dimension(500, 500));
        doc = this.getStyledDocument();
        out = new DocOutputStream(doc,this);
        pout=new PrintStream(out);
        in = new DocInputStream();
        this.addKeyListener(in);
        setFGColor(Color.black);
        setBGColor(Color.white);
        frame = new JFrame("Console");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(new JScrollPane(this));
        frame.pack();
        frame.setVisible(true);
    }

    public InputStream getIn(){
        return in;
    }
    public PrintStream getOut(){
        return pout;
    }

    public void setFGColor(Color c){
        StyleConstants.setForeground(out.cur, c);
    }
    public void setBGColor(Color c){
        StyleConstants.setBackground(out.cur, c);
    }

    private static class DocOutputStream extends OutputStream {

        StyledDocument doc;
        MutableAttributeSet cur;
        JTextPane pane;

        public DocOutputStream(StyledDocument doc, JTextPane pane) {
            this.doc = doc;
            this.pane=pane;
            cur=new SimpleAttributeSet();
        }

        @Override
        public void write(int b) throws IOException {
            try {
                doc.insertString(doc.getLength(), (char)b+"", cur);
                pane.setCaretPosition(doc.getLength());
            } catch (BadLocationException ex) {
                Logger.getLogger(Console.class.getName()).
                        log(Level.SEVERE, null, ex);
            }

        }
    }

    private static class DocInputStream extends InputStream implements KeyListener {

        ArrayBlockingQueue<Integer> queue;

        public DocInputStream(){
            queue=new ArrayBlockingQueue<Integer>(1024);
        }

        @Override
        public int read() throws IOException {
            Integer i=null;
            try {
                i = queue.take();
            } catch (InterruptedException ex) {
                Logger.getLogger(Console.class.getName()).
                        log(Level.SEVERE, null, ex);
            }
            if(i!=null)
                return i;
            return -1;
        }

        @Override
        public void keyTyped(KeyEvent e) {
        }

        @Override
        public void keyPressed(KeyEvent e) {

        }

        @Override
        public void keyReleased(KeyEvent e) {
            int c = e.getKeyCode();
            try {
                queue.put(c);
            } catch (InterruptedException ex) {
                Logger.getLogger(Console.class.getName()).
                        log(Level.SEVERE, null, ex);
            }
        }

    }
}

Edit: note that in read() I tried poll() instead of take(), which stopped it from blocking at all, but I thought it may stop Scanner from blocking forever which was true, but it also stopped it from getting any real input at all.

MatrixPeckham

I figured it out. The problem was that Scanner was calling InputStream.read(char[],int,int), which is implemented to read the whole stream or the entire sized buffer. The scanner was attempting to fill a buffer of 8000+ bytes, and the default read(...) implementation stops calls read() only after the buffer is full, or -1 is read (EOF).

This lead the scanner to block forever, because most console input would never be that long. The solution was to override the buffered read. The version I need will block for the first byte, and return if there are no more characters, I thought the BufferedInputStream already implemented this by calling available() on the stream, so I tried wrapping my class in one after overriding available and that did not work. My new implementation uses available() as well as EOF as a stopping case.

This is what the implementation now looks like:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultStyledDocument;
import javax.swing.text.MutableAttributeSet;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;
import javax.swing.text.StyledDocument;

/**
 *
 * @author William Matrix Peckham
 */
public class Console extends JTextPane {

    DocOutputStream out;
    PrintStream pout;
    DocInputStream in;
    JFrame frame;
    StyledDocument doc;



    public Console() {
        super();
        setPreferredSize(new Dimension(500, 500));
        doc = this.getStyledDocument();
        out = new DocOutputStream(doc,this);
        pout=new PrintStream(out);
        in = new DocInputStream();
        this.addKeyListener(in);
        setFGColor(Color.black);
        setBGColor(Color.white);
        frame = new JFrame("Console");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(new JScrollPane(this));
        frame.pack();
        frame.setVisible(true);
    }

    public InputStream getIn(){
        return in;
    }
    public PrintStream getOut(){
        return pout;
    }

    public void setFGColor(Color c){
        StyleConstants.setForeground(out.cur, c);
    }
    public void setBGColor(Color c){
        StyleConstants.setBackground(out.cur, c);
    }

    private static class DocOutputStream extends OutputStream {

        StyledDocument doc;
        MutableAttributeSet cur;
        JTextPane pane;

        public DocOutputStream(StyledDocument doc, JTextPane pane) {
            this.doc = doc;
            this.pane=pane;
            cur=new SimpleAttributeSet();
        }

        @Override
        public void write(int b) throws IOException {
            try {
                doc.insertString(doc.getLength(), (char)b+"", cur);
                pane.setCaretPosition(doc.getLength());
            } catch (BadLocationException ex) {
                Logger.getLogger(Console.class.getName()).
                        log(Level.SEVERE, null, ex);
            }

        }
    }

    private static class DocInputStream extends InputStream implements KeyListener {

        ArrayBlockingQueue<Integer> queue;

        public DocInputStream(){
            queue=new ArrayBlockingQueue<Integer>(1024);
        }

        @Override
        public int read() throws IOException {
            Integer i=null;
            try {
                i = queue.take();
            } catch (InterruptedException ex) {
                Logger.getLogger(Console.class.getName()).
                        log(Level.SEVERE, null, ex);
            }
            if(i!=null)
                return i;
            return -1;
        }

        @Override
        public int read(byte[] b, int off, int len) throws IOException {
            if (b == null) {
                throw new NullPointerException();
            } else if (off < 0 || len < 0 || len > b.length - off) {
                throw new IndexOutOfBoundsException();
            } else if (len == 0) {
                return 0;
            }
                    int c = read();
            if (c == -1) {
                return -1;
            }
            b[off] = (byte)c;

            int i = 1;
            try {
                for (; i < len && available() > 0 ; i++) {
                    c = read();
                    if (c == -1) {
                        break;
                    }
                    b[off + i] = (byte)c;
                }
            } catch (IOException ee) {
            }   
            return i;

        }



        @Override
        public int available(){
            return queue.size();
        }

        @Override
        public void keyTyped(KeyEvent e) {
            int c = e.getKeyChar();
            try {
                queue.put(c);
            } catch (InterruptedException ex) {
                Logger.getLogger(Console.class.getName()).
                        log(Level.SEVERE, null, ex);
            }
        }

        @Override
        public void keyPressed(KeyEvent e) {

        }

        @Override
        public void keyReleased(KeyEvent e) {
        }

    }
}

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

How can I turn a Stream into an Iterable?

How do I turn a Java Enumeration into a Stream?

How can I remove punctuation from input text in Java?

How do I turn a JSON file into a Java 8 Object Stream?

How can I use a custom font in the input area of an input method?

Javascript - How can I send the value of the text area to my textbox

How do I idiomatically turn a text component into an input component on click?

how can I align a button with text area?

How can I access the same text area variable?

How can i get value of text area?

How can I add a value from a combo box into a text area?

How can I turn input from a series of toggles into speed and direction?

How can I use a Java Text Field as input for a class name?

How I can create a text area that will always resize?

Racket : How can I turn a stream into a list?

How can i get the text of a paragraph and then turn it into an integer in HTML

turn a text area input string to type object

How can I deserialise a String in a Stream in java?

How can I disable autocomplete for this text area using js?

How can I limit the clickable area only for text in menu?

How can i turn text to link version with javascript?

How can I take a list input from a file and turn it into a dictionary?

How can I add a scroll bar to a text area?

How can i convert it to java stream

How i can bring my <text area> in front of CSS animation?

Swift How can I adjust the background area of a Text

How can I turn the following text file into a nice RMarkDown table?

How can I detect text area in an image?

How can i render a div near a text area?

TOP Ranking

HotTag

Archive