--- /dev/null
+package be.nikiroo.utils.streams;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import be.nikiroo.utils.StringUtils;
+
+/**
+ * This {@link InputStream} will change some of its content by replacing it with
+ * something else.
+ *
+ * @author niki
+ */
+public class ReplaceInputStream extends BufferedInputStream {
+ /**
+ * The minimum size of the internal buffer (could be more if at least one of
+ * the 'FROM' bytes arrays is > 2048 bytes — in that case the
+ * buffer will be twice the largest size of the 'FROM' bytes arrays).
+ * <p>
+ * This is a different buffer than the one from the inherited class.
+ */
+ static private final int MIN_BUFFER_SIZE = 4096;
+
+ private byte[][] froms;
+ private byte[][] tos;
+ private int maxFromSize;
+ private int maxToSize;
+
+ private byte[] source;
+ private int spos;
+ private int slen;
+
+ /**
+ * Create a {@link ReplaceInputStream} that will replace <tt>from</tt> with
+ * <tt>to</tt>.
+ *
+ * @param in
+ * the under-laying {@link InputStream}
+ * @param from
+ * the {@link String} to replace
+ * @param to
+ * the {@link String} to replace with
+ */
+ public ReplaceInputStream(InputStream in, String from, String to) {
+ this(in, StringUtils.getBytes(from), StringUtils.getBytes(to));
+ }
+
+ /**
+ * Create a {@link ReplaceInputStream} that will replace <tt>from</tt> with
+ * <tt>to</tt>.
+ *
+ * @param in
+ * the under-laying {@link InputStream}
+ * @param from
+ * the value to replace
+ * @param to
+ * the value to replace with
+ */
+ public ReplaceInputStream(InputStream in, byte[] from, byte[] to) {
+ this(in, new byte[][] { from }, new byte[][] { to });
+ }
+
+ /**
+ * Create a {@link ReplaceInputStream} that will replace all <tt>froms</tt>
+ * with <tt>tos</tt>.
+ * <p>
+ * Note that they will be replaced in order, and that for each <tt>from</tt>
+ * a <tt>to</tt> must correspond.
+ *
+ * @param in
+ * the under-laying {@link InputStream}
+ * @param froms
+ * the values to replace
+ * @param tos
+ * the values to replace with
+ */
+ public ReplaceInputStream(InputStream in, String[] froms, String[] tos) {
+ this(in, StreamUtils.getBytes(froms), StreamUtils.getBytes(tos));
+ }
+
+ /**
+ * Create a {@link ReplaceInputStream} that will replace all <tt>froms</tt>
+ * with <tt>tos</tt>.
+ * <p>
+ * Note that they will be replaced in order, and that for each <tt>from</tt>
+ * a <tt>to</tt> must correspond.
+ *
+ * @param in
+ * the under-laying {@link InputStream}
+ * @param froms
+ * the values to replace
+ * @param tos
+ * the values to replace with
+ */
+ public ReplaceInputStream(InputStream in, byte[][] froms, byte[][] tos) {
+ super(in);
+
+ if (froms.length != tos.length) {
+ throw new IllegalArgumentException(
+ "For replacing, each FROM must have a corresponding TO");
+ }
+
+ this.froms = froms;
+ this.tos = tos;
+
+ maxFromSize = 0;
+ for (int i = 0; i < froms.length; i++) {
+ maxFromSize = Math.max(maxFromSize, froms[i].length);
+ }
+
+ maxToSize = 0;
+ for (int i = 0; i < tos.length; i++) {
+ maxToSize = Math.max(maxToSize, tos[i].length);
+ }
+
+ // We need at least maxFromSize so we can iterate and replace
+ source = new byte[Math.max(2 * maxFromSize, MIN_BUFFER_SIZE)];
+ spos = 0;
+ slen = 0;
+ }
+
+ @Override
+ protected int read(InputStream in, byte[] buffer, int off, int len)
+ throws IOException {
+ if (len < maxToSize || source.length < maxToSize * 2) {
+ throw new IOException(
+ "An underlaying buffer is too small for these replace values");
+ }
+
+ // We need at least one byte of data to process
+ if (available() < Math.max(maxFromSize, 1) && !eof) {
+ spos = 0;
+ slen = in.read(source);
+ }
+
+ // Note: very simple, not efficient implementation; sorry.
+ int count = 0;
+ while (spos < slen && count < len - maxToSize) {
+ boolean replaced = false;
+ for (int i = 0; i < froms.length; i++) {
+ if (froms[i] != null && froms[i].length > 0
+ && StreamUtils.startsWith(froms[i], source, spos, slen)) {
+ if (tos[i] != null && tos[i].length > 0) {
+ System.arraycopy(tos[i], 0, buffer, off + spos,
+ tos[i].length);
+ count += tos[i].length;
+ }
+
+ spos += froms[i].length;
+ replaced = true;
+ break;
+ }
+ }
+
+ if (!replaced) {
+ buffer[off + count++] = source[spos++];
+ }
+ }
+
+ return count;
+ }
+}