com.github.dirkraft.jash.JashIO.java Source code

Java tutorial

Introduction

Here is the source code for com.github.dirkraft.jash.JashIO.java

Source

/**
 * The MIT License (MIT)
 *
 * Copyright (c) 2014 Jason Dunkelberger (dirkraft)
 *
 * 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 com.github.dirkraft.jash;

import com.google.common.base.Charsets;
import com.google.common.base.Objects;
import com.google.common.io.CharStreams;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.StringReader;

/**
 * TODO mash this into Jash.java a la Mockito.*
 */
public class JashIO {
    private static final Logger LOG = LoggerFactory.getLogger(JashIO.class);

    public static final int DEF_TERMINAL_COLUMNS = 120;
    public static final StreamContext DEFAULT_CONTEXT = new StreamContext(System.in, System.out, System.err,
            DEF_TERMINAL_COLUMNS, new File(""));

    static final InheritableThreadLocal<StreamContext> STREAM_CONTEXT = new InheritableThreadLocal<>();

    static StreamContext getContext() {
        StreamContext context = STREAM_CONTEXT.get();
        if (context == null) {
            LOG.warn("No StreamContext set. Falling back to default.");
            context = DEFAULT_CONTEXT;
        }
        return context;
    }

    /**
     * All streams attached to and accessed through the context are guaranteed to not be closed even if passed-in
     * logic (e.g. {@link #outPrint(JashLambda.ExceptingConsumer)}, {@link #err(JashLambda.ExceptingConsumer)}, ...) attempts to close any stream-wrapping
     * abstraction passed back to them.
     *
     * @param stdin   to read standard input
     * @param stdout  to produce to standard output
     * @param stderr  to produce to standard error
     * @param columns terminal column width for formatting
     * @param cwd     current working directory for resolving relative paths
     */
    static void setContext(InputStream stdin, OutputStream stdout, OutputStream stderr, @Nullable Integer columns,
            File cwd) {
        STREAM_CONTEXT.set(
                new StreamContext(stdin, stdout, stderr, Objects.firstNonNull(columns, DEF_TERMINAL_COLUMNS), cwd));
    }

    static void clearContext() {
        STREAM_CONTEXT.remove();
    }

    public static int columns() {
        StreamContext context = getContext();
        return context.columns;
    }

    public static File cwd() {
        StreamContext context = getContext();
        return context.cwd;
    }

    public static Chaining in(JashLambda.ExceptingConsumer<InputStream> consumer) throws JashIOException {
        StreamContext context = getContext();
        try {
            consumer.accept(context.stdin);
        } catch (Exception e) {
            throw new JashIOException(e);
        }
        return CHAINING;
    }

    public static Chaining outln(String line) throws JashIOException {
        return outf(line).outf("%n");
    }

    public static Chaining outf(String format, Object... args) throws JashIOException {
        StreamContext context = getContext();
        _string(String.format(format, args), context.stdout);
        return CHAINING;
    }

    public static Chaining out(JashLambda.ExceptingConsumer<OutputStream> consumer) throws JashIOException {
        StreamContext context = getContext();
        _out(consumer, context.stdout);
        return CHAINING;
    }

    public static Chaining outPrint(JashLambda.ExceptingConsumer<PrintWriter> consumer) throws JashIOException {
        StreamContext context = getContext();
        _print(consumer, context.stdout);
        return CHAINING;
    }

    public static Chaining errln(String line) throws JashIOException {
        return errf(line).errf("%n");
    }

    public static Chaining errf(String format, Object... args) throws JashIOException {
        StreamContext context = getContext();
        _string(String.format(format, args), context.stderr);
        return CHAINING;
    }

    public static Chaining err(JashLambda.ExceptingConsumer<OutputStream> consumer) throws JashIOException {
        StreamContext context = getContext();
        _out(consumer, context.stderr);
        return CHAINING;
    }

    public static Chaining errPrint(JashLambda.ExceptingConsumer<PrintWriter> consumer) throws JashIOException {
        StreamContext context = getContext();
        _print(consumer, context.stderr);
        return CHAINING;
    }

    static void _string(String s, OutputStream stream) throws JashIOException {
        // Do not close
        try {
            OutputStreamWriter stdout = new OutputStreamWriter(stream, Charsets.UTF_8);
            CharStreams.copy(new StringReader(s), stdout);
            stdout.flush();
        } catch (IOException e) {
            throw new JashIOException(e);
        }
    }

    static void _out(JashLambda.ExceptingConsumer<OutputStream> consumer, OutputStream stream)
            throws JashIOException {
        // Do not close
        try {
            consumer.accept(stream);
            stream.flush();
        } catch (Exception e) {
            throw new JashIOException(e);
        }
    }

    static void _print(JashLambda.ExceptingConsumer<PrintWriter> consumer, OutputStream stream)
            throws JashIOException {
        try {
            PrintWriter writer = new PrintWriter(new OutputStreamWriter(stream, Charsets.UTF_8));
            consumer.accept(writer);
            writer.flush();
        } catch (Exception e) {
            throw new JashIOException(e);
        }
    }

    static class StreamContext {
        final NoCloseInputStream stdin;
        final NoCloseOutputStream stdout;
        final NoCloseOutputStream stderr;
        final Integer columns;
        final File cwd;

        public StreamContext(InputStream stdin, OutputStream stdout, OutputStream stderr, Integer columns,
                File cwd) {
            this.stdin = new NoCloseInputStream(stdin);
            this.stdout = new NoCloseOutputStream(stdout);
            this.stderr = new NoCloseOutputStream(stderr);
            this.columns = columns;
            this.cwd = cwd;
        }
    }

    static final Chaining CHAINING = new Chaining();

    // TODO add unit test reflectively making sure all chaining methods are available.
    public static class Chaining {

        public Chaining in(JashLambda.ExceptingConsumer<InputStream> consumer) throws JashIOException {
            return JashIO.in(consumer);
        }

        public Chaining outln(String line) throws JashIOException {
            return JashIO.outln(line);
        }

        public Chaining outf(String format, Object... args) throws JashIOException {
            return JashIO.outf(format, args);
        }

        public Chaining out(JashLambda.ExceptingConsumer<OutputStream> consumer) throws JashIOException {
            return JashIO.out(consumer);
        }

        public Chaining outPrint(JashLambda.ExceptingConsumer<PrintWriter> consumer) throws JashIOException {
            return JashIO.outPrint(consumer);
        }

        public Chaining errln(String line) throws JashIOException {
            return JashIO.errln(line);
        }

        public Chaining errf(String format, Object... args) throws JashIOException {
            return JashIO.errf(format, args);
        }

        public Chaining err(JashLambda.ExceptingConsumer<OutputStream> consumer) throws JashIOException {
            return JashIO.err(consumer);
        }

        public Chaining errPrint(JashLambda.ExceptingConsumer<PrintWriter> consumer) throws JashIOException {
            return JashIO.errPrint(consumer);
        }

    }

    static class NoCloseInputStream extends InputStream {

        final InputStream stream;

        NoCloseInputStream(InputStream stream) {
            this.stream = stream;
        }

        @Override
        public int read() throws IOException {
            return stream.read();
        }

        @Override
        public int read(@Nonnull byte[] b) throws IOException {
            return stream.read(b);
        }

        @Override
        public int read(@Nonnull byte[] b, int off, int len) throws IOException {
            return stream.read(b, off, len);
        }

        @Override
        public long skip(long n) throws IOException {
            return stream.skip(n);
        }

        @Override
        public int available() throws IOException {
            return stream.available();
        }

        @Override
        public void close() throws IOException {
            // do nothing
        }

        @Override
        public void mark(int readlimit) {
            stream.mark(readlimit);
        }

        @Override
        public void reset() throws IOException {
            stream.reset();
        }

        @Override
        public boolean markSupported() {
            return stream.markSupported();
        }
    }

    static class NoCloseOutputStream extends OutputStream {
        final OutputStream stream;

        public NoCloseOutputStream(OutputStream stream) {
            this.stream = stream;
        }

        @Override
        public void write(int b) throws IOException {
            stream.write(b);
        }

        @Override
        public void write(@Nonnull byte[] b) throws IOException {
            stream.write(b);
        }

        @Override
        public void write(@Nonnull byte[] b, int off, int len) throws IOException {
            stream.write(b, off, len);
        }

        @Override
        public void flush() throws IOException {
            stream.flush();
        }

        @Override
        public void close() throws IOException {
            // do nothing
        }
    }
}