ReplaceStreams: allow multiple replacements strings
authorNiki Roo <niki@nikiroo.be>
Sat, 27 Apr 2019 17:32:15 +0000 (19:32 +0200)
committerNiki Roo <niki@nikiroo.be>
Sat, 27 Apr 2019 17:32:15 +0000 (19:32 +0200)
src/be/nikiroo/utils/streams/ReplaceInputStream.java
src/be/nikiroo/utils/streams/ReplaceOutputStream.java
src/be/nikiroo/utils/streams/StreamUtils.java

index f5138eefeafc327dbd865e3b89713843c027aa0a..eea3a1ab9b74b42158d5b40e565b59c839cb690a 100644 (file)
@@ -10,8 +10,9 @@ import java.io.InputStream;
  * @author niki
  */
 public class ReplaceInputStream extends BufferedInputStream {
-       private byte[] from;
-       private byte[] to;
+       private byte[][] froms;
+       private byte[][] tos;
+       private int maxToSize;
 
        private byte[] source;
        private int spos;
@@ -44,9 +45,55 @@ 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 <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.bytes(froms), StreamUtils.bytes(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);
-               this.from = from;
-               this.to = to;
+
+               if (froms.length != tos.length) {
+                       throw new IllegalArgumentException(
+                                       "For replacing, each FROM must have a corresponding TO");
+               }
+
+               this.froms = froms;
+               this.tos = tos;
+
+               for (int i = 0; i < tos.length; i++) {
+                       maxToSize = Math.max(maxToSize, tos[i].length);
+               }
 
                source = new byte[4096];
                spos = 0;
@@ -55,9 +102,9 @@ public class ReplaceInputStream extends BufferedInputStream {
 
        @Override
        protected int read(InputStream in, byte[] buffer) throws IOException {
-               if (buffer.length < to.length || source.length < to.length * 2) {
+               if (buffer.length < maxToSize || source.length < maxToSize * 2) {
                        throw new IOException(
-                                       "An underlaying buffer is too small for this replace value");
+                                       "An underlaying buffer is too small for these replace values");
                }
 
                if (spos >= slen) {
@@ -67,13 +114,23 @@ public class ReplaceInputStream extends BufferedInputStream {
 
                // Note: very simple, not efficient implementation, sorry.
                int count = 0;
-               while (spos < slen && count < buffer.length - to.length) {
-                       if (from.length > 0
-                                       && StreamUtils.startsWith(from, source, spos, slen)) {
-                               System.arraycopy(to, 0, buffer, spos, to.length);
-                               count += to.length;
-                               spos += from.length;
-                       } else {
+               while (spos < slen && count < buffer.length - 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, spos, tos[i].length);
+                                               count += tos[i].length;
+                                       }
+
+                                       spos += froms[i].length;
+                                       replaced = true;
+                                       break;
+                               }
+                       }
+
+                       if (!replaced) {
                                buffer[count++] = source[spos++];
                        }
                }
index e889b76ef6b756c20ca1e2e9ecec561c45ea9a55..dcb020770e003379cd88953d27d4d8bd4bc625bb 100644 (file)
@@ -10,8 +10,8 @@ import java.io.OutputStream;
  * @author niki
  */
 public class ReplaceOutputStream extends BufferedOutputStream {
-       private byte[] from;
-       private byte[] to;
+       private byte[][] froms;
+       private byte[][] tos;
 
        /**
         * Create a {@link ReplaceOutputStream} that will replace <tt>from</tt> with
@@ -40,23 +40,76 @@ public class ReplaceOutputStream extends BufferedOutputStream {
         *            the value to replace with
         */
        public ReplaceOutputStream(OutputStream out, byte[] from, byte[] to) {
+               this(out, new byte[][] { from }, new byte[][] { to });
+       }
+
+       /**
+        * Create a {@link ReplaceOutputStream} 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 out
+        *            the under-laying {@link OutputStream}
+        * @param froms
+        *            the values to replace
+        * @param tos
+        *            the values to replace with
+        */
+       public ReplaceOutputStream(OutputStream out, String[] froms, String[] tos) {
+               this(out, StreamUtils.bytes(froms), StreamUtils.bytes(tos));
+       }
+
+       /**
+        * Create a {@link ReplaceOutputStream} 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 out
+        *            the under-laying {@link OutputStream}
+        * @param froms
+        *            the values to replace
+        * @param tos
+        *            the values to replace with
+        */
+       public ReplaceOutputStream(OutputStream out, byte[][] froms, byte[][] tos) {
                super(out);
                bypassFlush = false;
 
-               this.from = from;
-               this.to = to;
+               if (froms.length != tos.length) {
+                       throw new IllegalArgumentException(
+                                       "For replacing, each FROM must have a corresponding TO");
+               }
+
+               this.froms = froms;
+               this.tos = tos;
        }
 
        @Override
        protected void flush(boolean includingSubStream) throws IOException {
                // Note: very simple, not efficient implementation, sorry.
                while (start < stop) {
-                       if (from.length > 0
-                                       && StreamUtils.startsWith(from, buffer, start, stop)) {
-                               out.write(to);
-                               bytesWritten += to.length;
-                               start += from.length;
-                       } else {
+                       boolean replaced = false;
+                       for (int i = 0; i < froms.length; i++) {
+                               if (froms[i] != null
+                                               && froms[i].length > 0
+                                               && StreamUtils
+                                                               .startsWith(froms[i], buffer, start, stop)) {
+                                       if (tos[i] != null && tos[i].length > 0) {
+                                               out.write(tos[i]);
+                                               bytesWritten += tos[i].length;
+                                       }
+
+                                       start += froms[i].length;
+                                       replaced = true;
+                                       break;
+                               }
+                       }
+
+                       if (!replaced) {
                                out.write(buffer[start++]);
                                bytesWritten++;
                        }
index 6b8251a6e692f498452d8dcacab6d0d1958318f7..9b9ffe653c8d7823dbfdc9f279b950711a51e066 100644 (file)
@@ -55,7 +55,7 @@ class StreamUtils {
         * UTF-8.
         * 
         * @param str
-        *            the string to transform into bytes
+        *            the {@link String} to transform into bytes
         * @return the content in bytes
         */
        static public byte[] bytes(String str) {
@@ -67,4 +67,26 @@ class StreamUtils {
                        return null;
                }
        }
+
+       /**
+        * Return the bytes array representation of the given {@link String} in
+        * UTF-8.
+        * 
+        * @param strs
+        *            the {@link String}s to transform into bytes
+        * @return the content in bytes
+        */
+       static public byte[][] bytes(String[] strs) {
+               try {
+                       byte[][] bytes = new byte[strs.length][];
+                       for (int i = 0; i < strs.length; i++) {
+                               bytes[i] = strs[i].getBytes("UTF-8");
+                       }
+                       return bytes;
+               } catch (UnsupportedEncodingException e) {
+                       // All conforming JVM must support UTF-8
+                       e.printStackTrace();
+                       return null;
+               }
+       }
 }