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.io.input;
18  
19  import java.io.IOException;
20  import java.io.InputStream;
21  import java.util.Objects;
22  
23  import org.apache.commons.io.IOUtils;
24  
25  /**
26   * An {@link InputStream} that repeats provided bytes for given target byte count.
27   * <p>
28   * Closing this input stream has no effect. The methods in this class can be called after the stream has been closed
29   * without generating an {@link IOException}.
30   * </p>
31   *
32   * @see InfiniteCircularInputStream
33   * @since 2.8.0
34   */
35  public class CircularInputStream extends InputStream {
36  
37      /**
38       * Throws an {@link IllegalArgumentException} if the input contains -1.
39       *
40       * @param repeatContent input to validate.
41       * @return the input.
42       */
43      private static byte[] validate(final byte[] repeatContent) {
44          Objects.requireNonNull(repeatContent, "repeatContent");
45          for (final byte b : repeatContent) {
46              if (b == IOUtils.EOF) {
47                  throw new IllegalArgumentException("repeatContent contains the end-of-stream marker " + IOUtils.EOF);
48              }
49          }
50          return repeatContent;
51      }
52  
53      private long byteCount;
54      private int position = IOUtils.EOF;
55      private final byte[] repeatedContent;
56      private final long targetByteCount;
57  
58      /**
59       * Constructs an instance from the specified array of bytes.
60       *
61       * @param repeatContent Input buffer to be repeated this buffer is not copied.
62       * @param targetByteCount How many bytes the read. A negative number means an infinite target count.
63       */
64      public CircularInputStream(final byte[] repeatContent, final long targetByteCount) {
65          this.repeatedContent = validate(repeatContent);
66          if (repeatContent.length == 0) {
67              throw new IllegalArgumentException("repeatContent is empty.");
68          }
69          this.targetByteCount = targetByteCount;
70      }
71  
72      @Override
73      public int read() {
74          if (targetByteCount >= 0) {
75              if (byteCount == targetByteCount) {
76                  return IOUtils.EOF;
77              }
78              byteCount++;
79          }
80          position = (position + 1) % repeatedContent.length;
81          return repeatedContent[position] & 0xff;
82      }
83  
84  }