001/*
002 *  Licensed to the Apache Software Foundation (ASF) under one
003 *  or more contributor license agreements.  See the NOTICE file
004 *  distributed with this work for additional information
005 *  regarding copyright ownership.  The ASF licenses this file
006 *  to you under the Apache License, Version 2.0 (the
007 *  "License"); you may not use this file except in compliance
008 *  with the License.  You may obtain a copy of the License at
009 *
010 *        http://www.apache.org/licenses/LICENSE-2.0
011 *
012 *  Unless required by applicable law or agreed to in writing,
013 *  software distributed under the License is distributed on an
014 *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 *  KIND, either express or implied.  See the License for the
016 *  specific language governing permissions and limitations
017 *  under the License.
018 */
019
020package org.apache.isis.core.commons.io;
021
022import static org.hamcrest.CoreMatchers.is;
023import static org.hamcrest.CoreMatchers.not;
024import static org.hamcrest.CoreMatchers.nullValue;
025
026import java.io.IOException;
027import java.io.InputStream;
028
029import org.apache.isis.core.commons.ensure.Ensure;
030
031/**
032 * An input stream that reads from an underlying {@link InputStream}, deferring
033 * the interactions until needed.
034 * 
035 * <p>
036 * This other stream is provided as needed by an {@link InputStreamProvider} so
037 * that the underlying stream is not eagerly loaded.
038 */
039public class LazyInputStream extends InputStream {
040
041    /**
042     * An interface to be implemented by clients that wish to utilize
043     * {@link LazyInputStream}s. The implementation of this interface should
044     * defer obtaining the desired input stream until absolutely necessary.
045     */
046    public static interface InputStreamProvider {
047        InputStream getInputStream() throws IOException;
048    }
049
050    private final InputStreamProvider provider;
051
052    private InputStream underlying = null;
053
054    // ///////////////////////////////////////////////////////
055    // Constructor
056    // ///////////////////////////////////////////////////////
057
058    /**
059     * Construct a new lazy stream based off the given provider.
060     * 
061     * @param provider
062     *            the input stream provider. Must not be <code>null</code>.
063     */
064    public LazyInputStream(final InputStreamProvider provider) {
065        Ensure.ensureThatArg(provider, is(not(nullValue())));
066        this.provider = provider;
067    }
068
069    // ///////////////////////////////////////////////////////
070    // InputStream API
071    // ///////////////////////////////////////////////////////
072
073    @Override
074    public void close() throws IOException {
075        obtainUnderlyingIfRequired();
076        underlying.close();
077    }
078
079    @Override
080    public int available() throws IOException {
081        obtainUnderlyingIfRequired();
082        return underlying.available();
083    }
084
085    @Override
086    public void mark(final int readlimit) {
087        try {
088            obtainUnderlyingIfRequired();
089            underlying.mark(readlimit);
090        } catch (final IOException e) {
091            throw new RuntimeException(e);
092        }
093    }
094
095    @Override
096    public boolean markSupported() {
097        try {
098            obtainUnderlyingIfRequired();
099            return underlying.markSupported();
100        } catch (final IOException e) {
101            throw new RuntimeException(e);
102        }
103    }
104
105    @Override
106    public int read() throws IOException {
107        obtainUnderlyingIfRequired();
108        return underlying.read();
109    }
110
111    @Override
112    public int read(final byte[] b, final int off, final int len) throws IOException {
113        obtainUnderlyingIfRequired();
114        return underlying.read(b, off, len);
115    }
116
117    @Override
118    public int read(final byte[] b) throws IOException {
119        obtainUnderlyingIfRequired();
120        return underlying.read(b);
121    }
122
123    @Override
124    public long skip(final long n) throws IOException {
125        obtainUnderlyingIfRequired();
126        return underlying.skip(n);
127    }
128
129    @Override
130    public void reset() throws IOException {
131        obtainUnderlyingIfRequired();
132        underlying.reset();
133    }
134
135    // ///////////////////////////////////////////////////////
136    // helpers
137    // ///////////////////////////////////////////////////////
138
139    private void obtainUnderlyingIfRequired() throws IOException {
140        if (underlying == null) {
141            underlying = provider.getInputStream();
142        }
143    }
144
145    // ///////////////////////////////////////////////////////
146    // equals, hashCode
147    // ///////////////////////////////////////////////////////
148
149    @Override
150    public boolean equals(final Object obj) {
151        try {
152            obtainUnderlyingIfRequired();
153            return underlying.equals(obj);
154        } catch (final IOException e) {
155            throw new RuntimeException(e);
156        }
157    }
158
159    @Override
160    public int hashCode() {
161        try {
162            obtainUnderlyingIfRequired();
163            return underlying.hashCode();
164        } catch (final IOException e) {
165            throw new RuntimeException(e);
166        }
167    }
168
169    // ///////////////////////////////////////////////////////
170    // toString
171    // ///////////////////////////////////////////////////////
172
173    @Override
174    public String toString() {
175        try {
176            obtainUnderlyingIfRequired();
177            return underlying.toString();
178        } catch (final IOException e) {
179            throw new RuntimeException(e);
180        }
181    }
182
183}