Commit | Line | Data |
---|---|---|
8e76f6ab | 1 | package be.nikiroo.utils.streams; |
6ef54b36 NR |
2 | |
3 | import java.io.IOException; | |
4 | import java.io.InputStream; | |
6ef54b36 | 5 | |
876dbf8b | 6 | /** |
c8ce09c4 NR |
7 | * This {@link InputStream} will change some of its content by replacing it with |
8 | * something else. | |
876dbf8b NR |
9 | * |
10 | * @author niki | |
11 | */ | |
6ef54b36 | 12 | public class ReplaceInputStream extends BufferedInputStream { |
627f866e NR |
13 | private byte[][] froms; |
14 | private byte[][] tos; | |
15 | private int maxToSize; | |
6ef54b36 NR |
16 | |
17 | private byte[] source; | |
18 | private int spos; | |
19 | private int slen; | |
20 | ||
876dbf8b NR |
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 | */ | |
6ef54b36 | 32 | public ReplaceInputStream(InputStream in, String from, String to) { |
c8ce09c4 | 33 | this(in, StreamUtils.bytes(from), StreamUtils.bytes(to)); |
6ef54b36 NR |
34 | } |
35 | ||
876dbf8b NR |
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 | */ | |
6ef54b36 | 47 | public ReplaceInputStream(InputStream in, byte[] from, byte[] to) { |
627f866e NR |
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) { | |
6ef54b36 | 84 | super(in); |
627f866e NR |
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 | } | |
6ef54b36 NR |
97 | |
98 | source = new byte[4096]; | |
99 | spos = 0; | |
100 | slen = 0; | |
101 | } | |
102 | ||
103 | @Override | |
028ff7c2 NR |
104 | protected int read(InputStream in, byte[] buffer, int off, int len) |
105 | throws IOException { | |
106 | if (len < maxToSize || source.length < maxToSize * 2) { | |
6ef54b36 | 107 | throw new IOException( |
627f866e | 108 | "An underlaying buffer is too small for these replace values"); |
6ef54b36 NR |
109 | } |
110 | ||
111 | if (spos >= slen) { | |
112 | spos = 0; | |
113 | slen = in.read(source); | |
114 | } | |
115 | ||
116 | // Note: very simple, not efficient implementation, sorry. | |
117 | int count = 0; | |
028ff7c2 | 118 | while (spos < slen && count < len - maxToSize) { |
627f866e NR |
119 | boolean replaced = false; |
120 | for (int i = 0; i < froms.length; i++) { | |
121 | if (froms[i] != null && froms[i].length > 0 | |
122 | && StreamUtils.startsWith(froms[i], source, spos, slen)) { | |
123 | if (tos[i] != null && tos[i].length > 0) { | |
028ff7c2 NR |
124 | System.arraycopy(tos[i], 0, buffer, off + spos, |
125 | tos[i].length); | |
627f866e NR |
126 | count += tos[i].length; |
127 | } | |
128 | ||
129 | spos += froms[i].length; | |
130 | replaced = true; | |
131 | break; | |
132 | } | |
133 | } | |
134 | ||
135 | if (!replaced) { | |
028ff7c2 | 136 | buffer[off + count++] = source[spos++]; |
6ef54b36 NR |
137 | } |
138 | } | |
139 | ||
140 | return count; | |
141 | } | |
6ef54b36 | 142 | } |