View Javadoc

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 }