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 from with * to. * * @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 from with * to. * * @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 froms * with tos. *

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

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

* If {@link BufferedOutputStream#bypassFlush} is false, all writes to the * under-laying stream are done in this method. *

* 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). *

* But be careful! 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(); } } }