1 package com.aragost.javahg.internals;
2
3 import java.io.ByteArrayOutputStream;
4 import java.io.FilterOutputStream;
5 import java.io.IOException;
6 import java.io.OutputStream;
7 import java.util.Map;
8
9 public class PatternReplacingOutputStream extends FilterOutputStream {
10
11 private enum State {
12
13
14
15
16 NORMAL,
17
18
19
20
21 MAYBE_NAME_START,
22
23
24
25
26 READING_NAME,
27
28
29
30 WRITING_REPLACEMENT;
31 };
32
33 private final ByteArrayOutputStream nameBuffer = new ByteArrayOutputStream(20);
34
35 private final Map<String, byte[]> replacements;
36
37 private State state = State.NORMAL;
38
39 public PatternReplacingOutputStream(OutputStream out, Map<String, byte[]> replacements) {
40 super(out);
41 this.replacements = replacements;
42 }
43
44 @Override
45 public synchronized void write(int b) throws IOException {
46 switch (this.state) {
47 case NORMAL:
48 if (b == '%') {
49 this.state = State.MAYBE_NAME_START;
50 } else {
51 super.write(b);
52 }
53 break;
54 case MAYBE_NAME_START:
55 if (b == '{') {
56 this.state = State.READING_NAME;
57 } else {
58 super.write('%');
59 if (b == '%') {
60 this.state = State.NORMAL;
61 } else {
62 super.write(b);
63 }
64 }
65 break;
66 case READING_NAME:
67 if (b == '}') {
68 this.state = State.WRITING_REPLACEMENT;
69 String name = new String(this.nameBuffer.toByteArray());
70 this.nameBuffer.reset();
71 byte[] replacement = this.replacements.get(name);
72 if (replacement == null) {
73 replacement = ("%{" + name + "=null}").getBytes();
74 }
75 this.out.write(replacement);
76 this.state = State.NORMAL;
77 } else {
78 this.nameBuffer.write(b);
79 }
80 break;
81 case WRITING_REPLACEMENT:
82 super.write(b);
83 break;
84 }
85 }
86 }