com.tinspx.util.io.ChannelSink.java Source code

Java tutorial

Introduction

Here is the source code for com.tinspx.util.io.ChannelSink.java

Source

/* Copyright (C) 2013-2014 Ian Teune <ian.teune@gmail.com>
 * 
 * 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.tinspx.util.io;

import com.google.common.base.Function;
import static com.google.common.base.Preconditions.*;
import com.google.common.io.ByteSink;
import com.google.common.io.Closer;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;
import javax.annotation.WillNotClose;
import lombok.NonNull;
import lombok.experimental.Delegate;

/**
 * {@link ByteSink} extension that adds {@link #openChannel()}. Only once of
 * {@link #openStream()} or {@link #openChannel()} needs to be implemented.
 * 
 * @see BAOutputStream#asByteSink()
 * @author Ian
 */
public abstract class ChannelSink extends ByteSink {

    @Override
    public OutputStream openStream() throws IOException {
        return ByteUtils.asOutputStream(openChannel());
    }

    public WritableByteChannel openChannel() throws IOException {
        return ByteUtils.asWritableByteChannel(openStream());
    }

    @Override
    public void write(byte[] bytes) throws IOException {
        write(bytes, 0, bytes.length);
    }

    public void write(byte[] bytes, int off, int len) throws IOException {
        ByteUtils.copy(bytes, off, len, this);
    }

    @Override
    public long writeFrom(InputStream input) throws IOException {
        return ByteUtils.copy(input, this, Long.MAX_VALUE);
    }

    public long writeFrom(ReadableByteChannel input) throws IOException {
        return ByteUtils.copy(input, this, Long.MAX_VALUE);
    }

    /**
     * Indicates if {@link #openChannel() openChannel()} is preferred over
     * {@link #openStream() openStream()}. If this method returns {@code true},
     * consumers of this {@code ChannelSink} are <i>encouraged</i> to use
     * {@code openChannel()} over {@code openStream()}, but are not required to
     * do so. This method provides a hint that {@code openChannel()} will likely
     * be more efficient than {@code openStream()}.
     * <p>
     * Defaults to {@code false},
     */
    public boolean preferChannel() {
        return false;
    }

    /**
     * Wraps {@code file} as a {@code ChannelSink} that uses
     * {@link ChannelSource#fromFileOutputStream()} to acquire a
     * {@link FileChannel} from the {@code file}. The {@code file} is <i>not</i>
     * opened in append mode.
     */
    public static ChannelSink of(File file) {
        return new FileChannelSink(file, false, ChannelSource.fromFileOutputStream());
    }

    /**
     * Wraps {@code file} as a {@code ChannelSink} that uses
     * {@link ChannelSource#fromFileOutputStream()} to acquire a
     * {@link FileChannel} from the {@code file}. If {@code append} is true,
     * the {@code file} is opened in append mode.
     */
    public static ChannelSink of(File file, boolean append) {
        return new FileChannelSink(file, append, ChannelSource.fromFileOutputStream());
    }

    /**
     * Wraps {@code file} as a {@code ChannelSink} that uses
     * {@code fileChannelFunction} to acquire a {@link FileChannel} from the
     * {@code file}. The {@code file} is <i>not</i>
     * opened in append mode. {@code fileChannelFunction} must not create a
     * {@code FileChannel} in append mode; if it does, use
     * {@link #of(File, boolean, Function)} with {@code append} {@code true}
     * instead.
     */
    public static ChannelSink of(File file, Function<? super File, ? extends FileChannel> fileChannelFunction) {
        return new FileChannelSink(file, false, fileChannelFunction);
    }

    /**
     * Wraps {@code file} as a {@code ChannelSink} that uses
     * {@code fileChannelFunction} to acquire a {@link FileChannel} from the
     * {@code file}. If {@code append} is true, the {@code file} is opened in
     * append mode. If {@code fileChannelFunction} creates a {@code FileChannel}
     * in append mode, {@code append} must be {@code true}.
     */
    public static ChannelSink of(File file, boolean append,
            Function<? super File, ? extends FileChannel> fileChannelFunction) {
        return new FileChannelSink(file, append, fileChannelFunction);
    }

    static class FileChannelSink extends ChannelSink implements Function<File, FileChannel> {
        final File file;
        final boolean append;
        final Function<? super File, ? extends FileChannel> function;

        public FileChannelSink(File file, boolean append, Function<? super File, ? extends FileChannel> function) {
            this.file = checkNotNull(file);
            this.append = append;
            this.function = checkNotNull(function);
        }

        @Override
        public FileOutputStream openStream() throws IOException {
            return new FileOutputStream(file, append);
        }

        @Override
        public FileChannel openChannel() throws IOException {
            return ChannelSource.apply(file, this);
        }

        @Override
        public boolean preferChannel() {
            return true;
        }

        @Override
        @SuppressWarnings("null")
        public FileChannel apply(File input) {
            boolean error = false;
            Closer closer = Closer.create();
            try {
                FileChannel fc = closer.register(function.apply(file));
                if (append) {
                    final long size = fc.size();
                    if (fc.position() < size) {
                        fc.position(size);
                    }
                }
                return fc;
            } catch (Throwable t) {
                error = true;
                try {
                    throw closer.rethrow(t);
                } catch (IOException ex) {
                    throw new RuntimeException(ex);
                }
            } finally {
                if (error) {
                    try {
                        closer.close();
                    } catch (IOException ex) {
                        throw new RuntimeException(ex);
                    }
                }
            }
        }

        @Override
        public long writeFrom(InputStream input) throws IOException {
            if (input instanceof FileInputStream) {
                return writeFrom(((FileInputStream) input).getChannel());
            } else if (input instanceof ReadableByteChannel) {
                return writeFrom((ReadableByteChannel) input);
            } else {
                return super.writeFrom(input);
            }
        }

        @Override
        public long writeFrom(@WillNotClose ReadableByteChannel from) throws IOException {
            Closer closer = Closer.create();
            try {
                return ChannelSource.copy(from, closer.register(openChannel()));
            } catch (Throwable t) {
                throw closer.rethrow(t);
            } finally {
                closer.close();
            }
        }

        @Override
        public String toString() {
            return String.format("ChannelSink.of(%s, append=%b, %s)", file, append, function);
        }
    }

    /**
     * Returns a {@code ChannelSource} that discards all input.
     */
    public static ChannelSink nullSink() {
        return NullChannelSink.INSTANCE;
    }

    static class NullChannelSink extends ChannelSink {
        static final ChannelSink INSTANCE = new NullChannelSink();

        @Override
        public void write(@NonNull byte[] bytes) throws IOException {
        }

        @Override
        public void write(@NonNull byte[] bytes, int off, int len) throws IOException {
            checkPositionIndexes(off, off + len, bytes.length);
        }

        @Override
        public WritableByteChannel openChannel() throws IOException {
            return ByteUtils.nullWritableByteChannel();
        }

        @Override
        public OutputStream openStream() throws IOException {
            return ByteUtils.nullOutputStream();
        }

        @Override
        public OutputStream openBufferedStream() throws IOException {
            return ByteUtils.nullOutputStream();
        }

        @Override
        public String toString() {
            return " ChannelSink.nullSink()";
        }
    }

    public static ChannelSink of(ByteSink sink) {
        return sink instanceof ChannelSink ? (ChannelSink) sink : new ByteSinkChannelSink(sink);
    }

    static class ByteSinkChannelSink extends ChannelSink {
        @Delegate
        final ByteSink sink;

        public ByteSinkChannelSink(ByteSink sink) {
            this.sink = checkNotNull(sink);
        }

        @Override
        public String toString() {
            return sink.toString();
        }
    }
}