1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17 package org.apache.commons.flatfile.util;
18
19 import java.io.IOException;
20 import java.io.InputStream;
21
22 import org.apache.commons.lang3.Validate;
23
24 /**
25 * Provides factory methods to return InputStreams that return a repeating
26 * ordered sequence of bytes, optionally limiting output to some maximum total
27 * size.
28 *
29 * @version $Revision: 1301242 $ $Date: 2009-03-24 16:09:19 -0500 (Tue, 24 Mar
30 * 2009) $
31 */
32 public abstract class RepeatingInputStream {
33 /**
34 * Construct a new {@link RepeatingInputStream} instance.
35 */
36 private RepeatingInputStream() {
37 }
38
39 /**
40 * An InputStream that repeats a single byte forever.
41 */
42 private static class RepeatOneByte extends InputStream {
43 private final byte b;
44
45 /**
46 * Create a new RepeatOneByte instance.
47 *
48 * @param b byte to repeat
49 */
50 private RepeatOneByte(byte b) {
51 this.b = b;
52 }
53
54 /**
55 * {@inheritDoc}
56 */
57 public int read() throws IOException {
58 return b;
59 }
60 }
61
62 /**
63 * An InputStream that repeats a byte[] forever.
64 */
65 private static class RepeatArray extends InputStream {
66 private final byte[] b;
67 private int pos;
68
69 /**
70 * Create a new RepeatArray instance.
71 *
72 * @param b byte[] to repeat
73 */
74 private RepeatArray(byte[] b) {
75 this.b = b;
76 }
77
78 /**
79 * {@inheritDoc}
80 */
81 public synchronized int read() throws IOException {
82 try {
83 return b[pos++];
84 } finally {
85 if (pos == b.length) {
86 pos = 0;
87 }
88 }
89 }
90 }
91
92 /**
93 * InputStream that limits the content of another InputStream.
94 */
95 private static class LimitedOutput extends InputStream {
96 private static final int EOF = -1;
97
98 private final InputStream source;
99 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 }