ReplaceStreams: allow multiple replacements strings
[nikiroo-utils.git] / src / be / nikiroo / utils / streams / ReplaceInputStream.java
1 package be.nikiroo.utils.streams;
2
3 import java.io.IOException;
4 import java.io.InputStream;
5
6 /**
7 * This {@link InputStream} will change some of its content by replacing it with
8 * something else.
9 *
10 * @author niki
11 */
12 public class ReplaceInputStream extends BufferedInputStream {
13 private byte[][] froms;
14 private byte[][] tos;
15 private int maxToSize;
16
17 private byte[] source;
18 private int spos;
19 private int slen;
20
21 /**
22 * Create a {@link ReplaceInputStream} that will replace <tt>from</tt> with
23 * <tt>to</tt>.
24 *
25 * @param in
26 * the under-laying {@link InputStream}
27 * @param from
28 * the {@link String} to replace
29 * @param to
30 * the {@link String} to replace with
31 */
32 public ReplaceInputStream(InputStream in, String from, String to) {
33 this(in, StreamUtils.bytes(from), StreamUtils.bytes(to));
34 }
35
36 /**
37 * Create a {@link ReplaceInputStream} that will replace <tt>from</tt> with
38 * <tt>to</tt>.
39 *
40 * @param in
41 * the under-laying {@link InputStream}
42 * @param from
43 * the value to replace
44 * @param to
45 * the value to replace with
46 */
47 public ReplaceInputStream(InputStream in, byte[] from, byte[] to) {
48 this(in, new byte[][] { from }, new byte[][] { to });
49 }
50
51 /**
52 * Create a {@link ReplaceInputStream} that will replace all <tt>froms</tt>
53 * with <tt>tos</tt>.
54 * <p>
55 * Note that they will be replaced in order, and that for each <tt>from</tt>
56 * a <tt>to</tt> must correspond.
57 *
58 * @param in
59 * the under-laying {@link InputStream}
60 * @param froms
61 * the values to replace
62 * @param tos
63 * the values to replace with
64 */
65 public ReplaceInputStream(InputStream in, String[] froms, String[] tos) {
66 this(in, StreamUtils.bytes(froms), StreamUtils.bytes(tos));
67 }
68
69 /**
70 * Create a {@link ReplaceInputStream} that will replace all <tt>froms</tt>
71 * with <tt>tos</tt>.
72 * <p>
73 * Note that they will be replaced in order, and that for each <tt>from</tt>
74 * a <tt>to</tt> must correspond.
75 *
76 * @param in
77 * the under-laying {@link InputStream}
78 * @param froms
79 * the values to replace
80 * @param tos
81 * the values to replace with
82 */
83 public ReplaceInputStream(InputStream in, byte[][] froms, byte[][] tos) {
84 super(in);
85
86 if (froms.length != tos.length) {
87 throw new IllegalArgumentException(
88 "For replacing, each FROM must have a corresponding TO");
89 }
90
91 this.froms = froms;
92 this.tos = tos;
93
94 for (int i = 0; i < tos.length; i++) {
95 maxToSize = Math.max(maxToSize, tos[i].length);
96 }
97
98 source = new byte[4096];
99 spos = 0;
100 slen = 0;
101 }
102
103 @Override
104 protected int read(InputStream in, byte[] buffer) throws IOException {
105 if (buffer.length < maxToSize || source.length < maxToSize * 2) {
106 throw new IOException(
107 "An underlaying buffer is too small for these replace values");
108 }
109
110 if (spos >= slen) {
111 spos = 0;
112 slen = in.read(source);
113 }
114
115 // Note: very simple, not efficient implementation, sorry.
116 int count = 0;
117 while (spos < slen && count < buffer.length - maxToSize) {
118 boolean replaced = false;
119 for (int i = 0; i < froms.length; i++) {
120 if (froms[i] != null && froms[i].length > 0
121 && StreamUtils.startsWith(froms[i], source, spos, slen)) {
122 if (tos[i] != null && tos[i].length > 0) {
123 System.arraycopy(tos[i], 0, buffer, spos, tos[i].length);
124 count += tos[i].length;
125 }
126
127 spos += froms[i].length;
128 replaced = true;
129 break;
130 }
131 }
132
133 if (!replaced) {
134 buffer[count++] = source[spos++];
135 }
136 }
137
138 return count;
139 }
140 }