001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.flatfile.util;
018
019import java.io.IOException;
020import java.io.InputStream;
021
022import org.apache.commons.lang3.Validate;
023
024/**
025 * Provides factory methods to return InputStreams that return a repeating
026 * ordered sequence of bytes, optionally limiting output to some maximum total
027 * size.
028 *
029 * @version $Revision: 1301242 $ $Date: 2009-03-24 16:09:19 -0500 (Tue, 24 Mar
030 *          2009) $
031 */
032public abstract class RepeatingInputStream {
033    /**
034     * Construct a new {@link RepeatingInputStream} instance.
035     */
036    private RepeatingInputStream() {
037    }
038
039    /**
040     * An InputStream that repeats a single byte forever.
041     */
042    private static class RepeatOneByte extends InputStream {
043        private final byte b;
044
045        /**
046         * Create a new RepeatOneByte instance.
047         *
048         * @param b byte to repeat
049         */
050        private RepeatOneByte(byte b) {
051            this.b = b;
052        }
053
054        /**
055         * {@inheritDoc}
056         */
057        public int read() throws IOException {
058            return b;
059        }
060    }
061
062    /**
063     * An InputStream that repeats a byte[] forever.
064     */
065    private static class RepeatArray extends InputStream {
066        private final byte[] b;
067        private int pos;
068
069        /**
070         * Create a new RepeatArray instance.
071         *
072         * @param b byte[] to repeat
073         */
074        private RepeatArray(byte[] b) {
075            this.b = b;
076        }
077
078        /**
079         * {@inheritDoc}
080         */
081        public synchronized int read() throws IOException {
082            try {
083                return b[pos++];
084            } finally {
085                if (pos == b.length) {
086                    pos = 0;
087                }
088            }
089        }
090    }
091
092    /**
093     * InputStream that limits the content of another InputStream.
094     */
095    private static class LimitedOutput extends InputStream {
096        private static final int EOF = -1;
097
098        private final InputStream source;
099        private final int bytesToReturn;
100        private int bytesReturned;
101
102        /**
103         * Create a new LimitedOutput instance.
104         *
105         * @param source wrapped InputStream
106         * @param bytesToReturn int max
107         */
108        private LimitedOutput(InputStream source, int bytesToReturn) {
109            this.source = source;
110            this.bytesToReturn = bytesToReturn;
111        }
112
113        /**
114         * {@inheritDoc}
115         */
116        public int read() throws IOException {
117            if (bytesReturned == bytesToReturn) {
118                return EOF;
119            }
120            try {
121                return source.read();
122            } finally {
123                bytesReturned++;
124            }
125        }
126    }
127
128    /**
129     * Holds cached instances of single-byte RepeatingInputStreams, with enough
130     * space for every unique byte value.
131     */
132    private static final InputStream[] REPEAT_BYTE = new InputStream[(int) Math.pow(2, Byte.SIZE)];
133
134    /**
135     * Get an InputStream that will return the specified byte[] forever.
136     *
137     * @param b byte[] to repeat
138     * @return {@link InputStream}
139     * @throws NullPointerException if {@code b} is {@code null}
140     */
141    public static InputStream of(byte... b) {
142        Validate.notNull(b);
143        if (b.length == 1) {
144            final int index = 0 | b[0];
145            InputStream result = REPEAT_BYTE[index];
146            if (result == null) {
147                result = new RepeatOneByte(b[0]);
148                REPEAT_BYTE[index] = result;
149            }
150            return result;
151        }
152        return new RepeatArray(b);
153    }
154
155    /**
156     * Get an InputStream that will return the specified byte sequence until a
157     * total of {@code limit} bytes have been returned.
158     *
159     * @param limit int
160     * @param b byte[] to repeat
161     * @return {@link InputStream}
162     * @throws NullPointerException if {@code b} is {@code null}
163     */
164    public static synchronized InputStream withLimit(int limit, byte... b) {
165        return new LimitedOutput(of(b), limit);
166    }
167
168}