1 | |
|
2 | |
|
3 | |
|
4 | |
|
5 | |
|
6 | |
|
7 | |
|
8 | |
|
9 | |
|
10 | |
|
11 | |
|
12 | |
|
13 | |
|
14 | |
|
15 | |
|
16 | |
|
17 | |
|
18 | |
|
19 | |
|
20 | |
|
21 | |
|
22 | |
|
23 | |
|
24 | |
|
25 | |
|
26 | |
|
27 | |
|
28 | |
|
29 | |
|
30 | |
|
31 | |
|
32 | |
|
33 | |
package net.sf.jmatchparser.util.charset; |
34 | |
|
35 | |
import java.nio.ByteBuffer; |
36 | |
import java.nio.CharBuffer; |
37 | |
import java.nio.charset.Charset; |
38 | |
import java.nio.charset.CharsetDecoder; |
39 | |
import java.nio.charset.CharsetEncoder; |
40 | |
import java.nio.charset.CoderResult; |
41 | |
import java.nio.charset.CodingErrorAction; |
42 | |
import java.util.Set; |
43 | |
|
44 | |
class AddBOMCharset extends Charset { |
45 | |
|
46 | |
static final char BOM = '\uFEFF'; |
47 | |
public static final String SUFFIX = "-BOM"; |
48 | |
private final Charset base; |
49 | |
|
50 | |
protected AddBOMCharset(Charset base) { |
51 | 6 | super(base.name() + SUFFIX, buildAliases(base.aliases())); |
52 | 6 | this.base = base; |
53 | 6 | } |
54 | |
|
55 | |
private static String[] buildAliases(Set<String> aliases) { |
56 | 6 | String[] result = new String[aliases.size()]; |
57 | 6 | int i = 0; |
58 | 6 | for (String alias : aliases) { |
59 | 18 | result[i] = alias + SUFFIX; |
60 | 18 | i++; |
61 | |
} |
62 | 6 | return result; |
63 | |
} |
64 | |
|
65 | |
@Override |
66 | |
public boolean contains(Charset cs) { |
67 | 0 | return cs instanceof AddBOMCharset || base.contains(cs); |
68 | |
} |
69 | |
|
70 | |
@Override |
71 | |
public CharsetDecoder newDecoder() { |
72 | 12 | return new Decoder(base.newDecoder()); |
73 | |
} |
74 | |
|
75 | |
@Override |
76 | |
public CharsetEncoder newEncoder() { |
77 | 6 | return new Encoder(base.newEncoder()); |
78 | |
} |
79 | |
|
80 | |
private class Decoder extends CharsetDecoder { |
81 | |
|
82 | |
private final CharsetDecoder baseDecoder; |
83 | 12 | private boolean bomRead = false, flushed = false; |
84 | |
|
85 | 12 | protected Decoder(CharsetDecoder baseDecoder) { |
86 | 12 | super(AddBOMCharset.this, baseDecoder.averageCharsPerByte(), baseDecoder.maxCharsPerByte()); |
87 | 12 | this.baseDecoder = baseDecoder; |
88 | 12 | baseDecoder.onMalformedInput(CodingErrorAction.REPORT); |
89 | 12 | baseDecoder.onUnmappableCharacter(CodingErrorAction.REPORT); |
90 | 12 | } |
91 | |
|
92 | |
@Override |
93 | |
protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) { |
94 | 36 | if (out.remaining() == 0) |
95 | 0 | return CoderResult.OVERFLOW; |
96 | 36 | if (!bomRead) { |
97 | 36 | CharBuffer bom = CharBuffer.allocate(1); |
98 | 36 | CoderResult bomResult = baseDecoder.decode(in, bom, false); |
99 | 36 | bom.flip(); |
100 | 36 | if (bom.remaining() == 1) { |
101 | 36 | char c = bom.get(); |
102 | 36 | if (c != BOM) |
103 | 12 | out.put(c); |
104 | 36 | bomRead = true; |
105 | |
} |
106 | 36 | if (!bomResult.isOverflow()) { |
107 | 12 | return bomResult; |
108 | |
} |
109 | 24 | bomRead = true; |
110 | |
} |
111 | 24 | return baseDecoder.decode(in, out, false); |
112 | |
} |
113 | |
|
114 | |
@Override |
115 | |
protected CoderResult implFlush(CharBuffer out) { |
116 | 30 | if (!flushed) { |
117 | 30 | ByteBuffer empty = ByteBuffer.allocate(1); |
118 | 30 | empty.flip(); |
119 | 30 | CoderResult result = baseDecoder.decode(empty, out, true); |
120 | 30 | if (!result.isUnderflow()) |
121 | 0 | return result; |
122 | 30 | result = baseDecoder.flush(out); |
123 | 30 | if (!result.isUnderflow()) |
124 | 0 | return result; |
125 | |
} |
126 | 30 | return super.implFlush(out); |
127 | |
} |
128 | |
|
129 | |
@Override |
130 | |
protected void implReset() { |
131 | 30 | baseDecoder.reset(); |
132 | 30 | bomRead = false; |
133 | 30 | flushed = false; |
134 | 30 | } |
135 | |
} |
136 | |
|
137 | |
private class Encoder extends CharsetEncoder { |
138 | |
|
139 | |
private CharsetEncoder baseEncoder; |
140 | 6 | boolean bomWritten = false, flushed = false; |
141 | |
|
142 | 6 | protected Encoder(CharsetEncoder baseEncoder) { |
143 | 6 | super(AddBOMCharset.this, baseEncoder.averageBytesPerChar(), baseEncoder.maxBytesPerChar() * 2, baseEncoder.replacement()); |
144 | 6 | this.baseEncoder = baseEncoder; |
145 | 6 | baseEncoder.onMalformedInput(CodingErrorAction.REPORT); |
146 | 6 | baseEncoder.onUnmappableCharacter(CodingErrorAction.REPORT); |
147 | 6 | } |
148 | |
|
149 | |
@Override |
150 | |
protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) { |
151 | 24 | if (!bomWritten) { |
152 | 24 | CharBuffer bom = CharBuffer.allocate(1); |
153 | 24 | bom.put(BOM); |
154 | 24 | bom.flip(); |
155 | 24 | CoderResult result = baseEncoder.encode(bom, out, false); |
156 | 24 | if (!result.isUnderflow()) |
157 | 0 | return result; |
158 | 24 | bomWritten = true; |
159 | |
} |
160 | 24 | return baseEncoder.encode(in, out, false); |
161 | |
} |
162 | |
|
163 | |
@Override |
164 | |
protected void implReset() { |
165 | 24 | baseEncoder.reset(); |
166 | 24 | bomWritten = false; |
167 | 24 | flushed = false; |
168 | 24 | } |
169 | |
|
170 | |
@Override |
171 | |
protected CoderResult implFlush(ByteBuffer out) { |
172 | 24 | if (!flushed) { |
173 | 24 | CharBuffer maybeBom = CharBuffer.allocate(1); |
174 | 24 | if (!bomWritten) { |
175 | 0 | maybeBom.put(BOM); |
176 | |
} |
177 | 24 | maybeBom.flip(); |
178 | 24 | CoderResult result = baseEncoder.encode(maybeBom, out, true); |
179 | 24 | if (!result.isUnderflow()) |
180 | 0 | return result; |
181 | 24 | bomWritten = true; |
182 | 24 | result = baseEncoder.flush(out); |
183 | 24 | if (!result.isUnderflow()) |
184 | 0 | return result; |
185 | 24 | flushed = true; |
186 | |
} |
187 | 24 | return super.implFlush(out); |
188 | |
} |
189 | |
} |
190 | |
} |