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}