Java tutorial
/** * 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 } } }