X-Git-Url: http://git.nikiroo.be/?p=nikiroo-utils.git;a=blobdiff_plain;f=src%2Fbe%2Fnikiroo%2Futils%2Fstreams%2FReplaceInputStream.java;h=0860f78c35e09b08ffe1f3326a379e1d504e5959;hp=5eac0b448fef0262b9f2ded53a4c199a2c41d0b7;hb=28bee1c360ac061db48395356f36ef62c2923bf1;hpb=8e76f6ab13a8a4a651f2518b6c91d5e6424c7922 diff --git a/src/be/nikiroo/utils/streams/ReplaceInputStream.java b/src/be/nikiroo/utils/streams/ReplaceInputStream.java index 5eac0b4..0860f78 100644 --- a/src/be/nikiroo/utils/streams/ReplaceInputStream.java +++ b/src/be/nikiroo/utils/streams/ReplaceInputStream.java @@ -2,21 +2,31 @@ package be.nikiroo.utils.streams; import java.io.IOException; import java.io.InputStream; -import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.List; + +import be.nikiroo.utils.StringUtils; /** - * This {@link InputStream} will replace some of its content by replacing it - * with something else. + * This {@link InputStream} will change some of its content by replacing it with + * something else. * * @author niki */ public class ReplaceInputStream extends BufferedInputStream { - private byte[] from; - private byte[] to; + /** + * 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). + *

+ * This is a different buffer than the one from the inherited class. + */ + static private final int MIN_BUFFER_SIZE = 4096; - private byte[] source; - private int spos; - private int slen; + private byte[][] froms; + private byte[][] tos; + private int bufferSize; + private int maxFromSize; /** * Create a {@link ReplaceInputStream} that will replace from with @@ -30,7 +40,7 @@ public class ReplaceInputStream extends BufferedInputStream { * the {@link String} to replace with */ public ReplaceInputStream(InputStream in, String from, String to) { - this(in, bytes(from), bytes(to)); + this(in, StringUtils.getBytes(from), StringUtils.getBytes(to)); } /** @@ -45,57 +55,163 @@ public class ReplaceInputStream extends BufferedInputStream { * 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 froms + * with tos. + *

+ * Note that they will be replaced in order, and that for each from + * a to 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 froms + * with tos. + *

+ * Note that they will be replaced in order, and that for each from + * a to 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); - this.from = from; - this.to = to; - source = new byte[4096]; - spos = 0; - slen = 0; + 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); + } + + int 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 + bufferSize = Math.max(4 * Math.max(maxToSize, maxFromSize), + MIN_BUFFER_SIZE); + } + + @Override + protected boolean preRead() throws IOException { + boolean rep = super.preRead(); + start = stop; + return rep; } @Override protected int read(InputStream in, byte[] buffer) throws IOException { - if (buffer.length < to.length || source.length < to.length * 2) { - throw new IOException( - "An underlaying buffer is too small for this replace value"); - } + buffer = null; // do not use the buffer. - if (spos >= slen) { - spos = 0; - slen = in.read(source); + byte[] newBuffer = new byte[bufferSize]; + int read = 0; + while (read < bufferSize / 2) { + int thisTime = in.read(newBuffer, read, bufferSize / 2 - read); + if (thisTime <= 0) { + break; + } + read += thisTime; } - // Note: very simple, not efficient implementation, sorry. - int count = 0; - while (spos < slen && count < buffer.length - to.length) { - if (from.length > 0 && startsWith(from, source, spos, slen)) { - System.arraycopy(to, 0, buffer, spos, to.length); - count += to.length; - spos += from.length; - } else { - buffer[count++] = source[spos++]; + List bbBuffers = new ArrayList(); + List bbOffsets = new ArrayList(); + List bbLengths = new ArrayList(); + + int offset = 0; + for (int i = 0; i < read; i++) { + for (int fromIndex = 0; fromIndex < froms.length; fromIndex++) { + byte[] from = froms[fromIndex]; + byte[] to = tos[fromIndex]; + + if (from.length > 0 + && StreamUtils.startsWith(from, newBuffer, i, read)) { + if (i - offset > 0) { + bbBuffers.add(newBuffer); + bbOffsets.add(offset); + bbLengths.add(i - offset); + } + + if (to.length > 0) { + bbBuffers.add(to); + bbOffsets.add(0); + bbLengths.add(to.length); + } + + i += from.length; + offset = i; + } } } - return count; + if (offset < read) { + bbBuffers.add(newBuffer); + bbOffsets.add(offset); + bbLengths.add(read - offset); + } + + for (int i = bbBuffers.size() - 1; i >= 0; i--) { + // DEBUG("pushback", bbBuffers.get(i), bbOffsets.get(i), + // bbLengths.get(i)); + pushback(bbBuffers.get(i), bbOffsets.get(i), bbLengths.get(i)); + } + + return read; } - /** - * Return the bytes array representation of the given {@link String} in - * UTF-8. - * - * @param str - * the string to transform into bytes - * @return the content in bytes - */ - static private byte[] bytes(String str) { - try { - return str.getBytes("UTF-8"); - } catch (UnsupportedEncodingException e) { - // All conforming JVM must support UTF-8 - e.printStackTrace(); - return null; + // static public void DEBUG(String title, byte[] b, int off, int len) { + // String str = new String(b,off,len); + // if(str.length()>20) { + // str=str.substring(0,10)+" ... + // "+str.substring(str.length()-10,str.length()); + // } + // } + + @Override + public String toString() { + StringBuilder rep = new StringBuilder(); + rep.append(getClass().getSimpleName()).append("\n"); + + for (int i = 0; i < froms.length; i++) { + byte[] from = froms[i]; + byte[] to = tos[i]; + + rep.append("\t"); + rep.append("bytes[").append(from.length).append("]"); + if (from.length <= 20) { + rep.append(" (").append(new String(from)).append(")"); + } + rep.append(" -> "); + rep.append("bytes[").append(to.length).append("]"); + if (to.length <= 20) { + rep.append(" (").append(new String(to)).append(")"); + } + rep.append("\n"); } + + return "[" + rep + "]"; } }