net.derquinse.common.io.MemoryByteSourceLoader.java Source code

Java tutorial

Introduction

Here is the source code for net.derquinse.common.io.MemoryByteSourceLoader.java

Source

/*
 * Copyright (C) the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package net.derquinse.common.io;

import static com.google.common.base.Preconditions.checkNotNull;
import static net.derquinse.common.io.InternalPreconditions.checkChunkSize;
import static net.derquinse.common.io.InternalPreconditions.checkMaxSize;
import static net.derquinse.common.io.InternalPreconditions.checkSize;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;

import javax.annotation.concurrent.ThreadSafe;

import com.google.common.base.MoreObjects;
import com.google.common.base.Objects;
import com.google.common.io.ByteSource;
import com.google.common.io.ByteStreams;
import com.google.common.io.Closer;
import com.google.common.io.Files;
import com.google.common.primitives.Ints;

/**
 * Memory byte source loader.
 * @author Andres Rodriguez
 */
@ThreadSafe
public final class MemoryByteSourceLoader {
    /** Default loader. */
    private static final MemoryByteSourceLoader DEFAULT = new MemoryByteSourceLoader(false, Integer.MAX_VALUE, 8192,
            false, null);
    /** Whether to use direct memory. */
    private final boolean direct;
    /** Maximum size. */
    private final int maxSize;
    /** Chunk size. */
    private final int chunkSize;
    /** Whether to merge after loading. */
    private final boolean merge;
    /** Transformer to use. */
    private final BytesTransformer transformer;

    /** Gets the default loader. */
    public static MemoryByteSourceLoader get() {
        return DEFAULT;
    }

    /** Constructor. */
    private MemoryByteSourceLoader(boolean direct, int maxSize, int chunkSize, boolean merge,
            BytesTransformer transformer) {
        this.direct = direct;
        this.maxSize = maxSize;
        this.chunkSize = chunkSize;
        this.merge = merge;
        this.transformer = transformer;
    }

    /** Returns whether the loader uses direct memory. */
    public boolean isDirect() {
        return direct;
    }

    /** Returns the maximum size. */
    public int getMaxSize() {
        return maxSize;
    }

    /** Returns the chunk size. */
    public int getChunkSize() {
        return chunkSize;
    }

    /** Returns whether the loader merges after loading. */
    public boolean isMerge() {
        return merge;
    }

    /** Returns the transformer used. */
    public BytesTransformer getTransformer() {
        return transformer;
    }

    /**
     * Returns a loader with the same configuration and the use of direct memory specified by the
     * argument.
     * @param direct True to use direct memory, false to use heap.
     */
    public MemoryByteSourceLoader direct(boolean direct) {
        if (direct == this.direct) {
            return this;
        }
        return new MemoryByteSourceLoader(direct, maxSize, chunkSize, merge, transformer);
    }

    /**
     * Returns a loader with the same configuration and the maximum allowed size specified by the
     * argument.
     */
    public MemoryByteSourceLoader maxSize(int maxSize) {
        checkMaxSize(maxSize);
        if (maxSize == this.maxSize) {
            return this;
        }
        return new MemoryByteSourceLoader(direct, maxSize, chunkSize, merge, transformer);
    }

    /**
     * Returns a loader with the same configuration and the chunk size specified by the argument.
     */
    public MemoryByteSourceLoader chunkSize(int chunkSize) {
        checkChunkSize(chunkSize);
        if (chunkSize == this.chunkSize) {
            return this;
        }
        return new MemoryByteSourceLoader(direct, maxSize, chunkSize, merge, transformer);
    }

    /**
     * Returns a loader with the same configuration and that merges the source after loading according
     * to the provided argument.
     * @param merge Whether to merge after loading.
     */
    public MemoryByteSourceLoader merge(boolean merge) {
        if (merge == this.merge) {
            return this;
        }
        return new MemoryByteSourceLoader(direct, maxSize, chunkSize, merge, transformer);
    }

    /**
     * Returns a loader with the same configuration and the transformer specified by the argument.
     */
    public MemoryByteSourceLoader transformer(ByteStreamTransformer transformer) {
        BytesTransformer v = transformer == null ? null : BytesTransformer.of(transformer);
        if (Objects.equal(this.transformer, v)) {
            return this;
        }
        return new MemoryByteSourceLoader(direct, maxSize, chunkSize, merge, v);
    }

    private MemoryByteSource merged(MemoryByteSource source) {
        if (merge) {
            return source.merge();
        }
        return source;
    }

    /** Opens a new memory output stream. */
    public MemoryOutputStream openStream() {
        if (direct) {
            return DirectByteSource.openStream(this);
        }
        return HeapByteSource.openStream(this);
    }

    /**
     * Loads the contents of the input stream into a memory byte source.
     * @param is Input stream. It is not closed.
     * @return The loaded data in a byte source.
     */
    public MemoryByteSource load(InputStream is) throws IOException {
        checkNotNull(is, "The input stream to load must be provided");
        final MemoryOutputStream os = openStream();
        if (transformer != null) {
            transformer.transform(is, os);
        } else {
            ByteStreams.copy(is, os);
        }
        return os.toByteSource();
    }

    /** Performs a copy of the provided source. */
    MemoryByteSource copy(MemoryByteSource source) {
        try {
            return load(source.openStream());
        } catch (IOException e) {
            throw new IllegalStateException(e); // should not happen
        }
    }

    /**
     * Transform an existing memory byte source.
     */
    private MemoryByteSource transform(MemoryByteSource source) {
        checkSize(Ints.saturatedCast(source.size()), maxSize);
        final MemoryByteSource transformed;
        if (direct) {
            transformed = source.toDirect(chunkSize);
        } else {
            transformed = source.toHeap(chunkSize);
        }
        return merged(transformed);
    }

    /**
     * Loads the contents of an existing source into a memory byte source.
     * @return The loaded data in a byte source.
     */
    public MemoryByteSource load(ByteSource source) throws IOException {
        checkNotNull(source, "The byte source to load must be provided");
        if (transformer == null && source instanceof MemoryByteSource) {
            return transform((MemoryByteSource) source);
        }
        Closer closer = Closer.create();
        try {
            InputStream is = closer.register(source.openStream());
            return load(is);
        } catch (Throwable t) {
            throw closer.rethrow(t);
        } finally {
            closer.close();
        }
    }

    /**
     * Loads the contents of an existing file into a memory byte source.
     * @return The loaded data in a byte source.
     */
    public MemoryByteSource load(File file) throws IOException {
        checkNotNull(file, "The file to load must be provided");
        return load(Files.asByteSource(file));
    }

    @Override
    public int hashCode() {
        return Objects.hashCode(direct, maxSize, chunkSize, merge, transformer);
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof MemoryByteSourceLoader) {
            MemoryByteSourceLoader s = (MemoryByteSourceLoader) obj;
            return direct == s.direct && merge == s.merge && maxSize == s.maxSize && chunkSize == s.chunkSize
                    && transformer.equals(s.transformer);
        }
        return false;
    }

    /** Creates a new sink based on this loader. */
    public MemoryByteSink newSink() {
        return new MemoryByteSink(this);
    }

    @Override
    public String toString() {
        return MoreObjects.toStringHelper(this).omitNullValues().add("direct", direct).add("maxSize", maxSize)
                .add("chunkSize", chunkSize).add("merge", merge).add("transformer", transformer).toString();
    }

}