Add 'src/be/nikiroo/utils/' from commit '46add0670fdee4bd936a13fe2448c5e20a7ffd0a'
[fanfix.git] / src / be / nikiroo / utils / streams / ReplaceOutputStream.java
diff --git a/src/be/nikiroo/utils/streams/ReplaceOutputStream.java b/src/be/nikiroo/utils/streams/ReplaceOutputStream.java
new file mode 100644 (file)
index 0000000..c6679cc
--- /dev/null
@@ -0,0 +1,148 @@
+package be.nikiroo.utils.streams;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import be.nikiroo.utils.StringUtils;
+
+/**
+ * This {@link OutputStream} will change some of its content by replacing it
+ * with something else.
+ * 
+ * @author niki
+ */
+public class ReplaceOutputStream extends BufferedOutputStream {
+       private byte[][] froms;
+       private byte[][] tos;
+
+       /**
+        * Create a {@link ReplaceOutputStream} that will replace <tt>from</tt> with
+        * <tt>to</tt>.
+        * 
+        * @param out
+        *            the under-laying {@link OutputStream}
+        * @param from
+        *            the {@link String} to replace
+        * @param to
+        *            the {@link String} to replace with
+        */
+       public ReplaceOutputStream(OutputStream out, String from, String to) {
+               this(out, StringUtils.getBytes(from), StringUtils.getBytes(to));
+       }
+
+       /**
+        * Create a {@link ReplaceOutputStream} that will replace <tt>from</tt> with
+        * <tt>to</tt>.
+        * 
+        * @param out
+        *            the under-laying {@link OutputStream}
+        * @param from
+        *            the value to replace
+        * @param to
+        *            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.getBytes(froms), StreamUtils.getBytes(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;
+
+               if (froms.length != tos.length) {
+                       throw new IllegalArgumentException(
+                                       "For replacing, each FROM must have a corresponding TO");
+               }
+
+               this.froms = froms;
+               this.tos = tos;
+       }
+
+       /**
+        * Flush the {@link BufferedOutputStream}, write the current buffered data
+        * to (and optionally also flush) the under-laying stream.
+        * <p>
+        * If {@link BufferedOutputStream#bypassFlush} is false, all writes to the
+        * under-laying stream are done in this method.
+        * <p>
+        * This can be used if you want to write some data in the under-laying
+        * stream yourself (in that case, flush this {@link BufferedOutputStream}
+        * with or without flushing the under-laying stream, then you can write to
+        * the under-laying stream).
+        * <p>
+        * <b>But be careful!</b> If a replacement could be done with the end o the
+        * currently buffered data and the start of the data to come, we obviously
+        * will not be able to do it.
+        * 
+        * @param includingSubStream
+        *            also flush the under-laying stream
+        * @throws IOException
+        *             in case of I/O error
+        */
+       @Override
+       public void flush(boolean includingSubStream) throws IOException {
+               // Note: very simple, not efficient implementation; sorry.
+               while (start < stop) {
+                       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++;
+                       }
+               }
+
+               start = 0;
+               stop = 0;
+
+               if (includingSubStream) {
+                       out.flush();
+               }
+       }
+}