1:
37:
38: package ;
39:
40: import ;
41: import ;
42:
43:
47: public abstract class CharsetEncoder
48: {
49: private static final int STATE_RESET = 0;
50: private static final int STATE_CODING = 1;
51: private static final int STATE_END = 2;
52: private static final int STATE_FLUSHED = 3;
53:
54: private static final byte[] DEFAULT_REPLACEMENT = {(byte)'?'};
55:
56: private final Charset charset;
57: private final float averageBytesPerChar;
58: private final float maxBytesPerChar;
59: private byte[] replacement;
60:
61: private int state = STATE_RESET;
62:
63: private CodingErrorAction malformedInputAction
64: = CodingErrorAction.REPORT;
65: private CodingErrorAction unmappableCharacterAction
66: = CodingErrorAction.REPORT;
67:
68: protected CharsetEncoder (Charset cs, float averageBytesPerChar,
69: float maxBytesPerChar)
70: {
71: this (cs, averageBytesPerChar, maxBytesPerChar, DEFAULT_REPLACEMENT);
72: }
73:
74: protected CharsetEncoder (Charset cs, float averageBytesPerChar,
75: float maxBytesPerChar, byte[] replacement)
76: {
77: if (averageBytesPerChar <= 0.0f)
78: throw new IllegalArgumentException ("Non-positive averageBytesPerChar");
79: if (maxBytesPerChar <= 0.0f)
80: throw new IllegalArgumentException ("Non-positive maxBytesPerChar");
81:
82: this.charset = cs;
83: this.averageBytesPerChar
84: = averageBytesPerChar;
85: this.maxBytesPerChar
86: = maxBytesPerChar;
87: this.replacement = replacement;
88: implReplaceWith (replacement);
89: }
90:
91: public final float averageBytesPerChar ()
92: {
93: return averageBytesPerChar;
94: }
95:
96: public boolean canEncode (char c)
97: {
98: CharBuffer cb = CharBuffer.allocate (1).put (c);
99: cb.flip ();
100: return canEncode (cb);
101: }
102:
103: public boolean canEncode (CharSequence cs)
104: {
105: CharBuffer cb;
106: if (cs instanceof CharBuffer)
107: cb = ((CharBuffer) cs).duplicate ();
108: else
109: cb = CharBuffer.wrap (cs);
110: return canEncode (cb);
111: }
112:
113: private boolean canEncode (CharBuffer cb)
114: {
115:
116:
117:
118: if (state == STATE_FLUSHED)
119: reset ();
120: else if (state != STATE_RESET)
121: throw new IllegalStateException ();
122:
123: CodingErrorAction oldMalformedInputAction = malformedInputAction;
124: CodingErrorAction oldUnmappableCharacterAction
125: = unmappableCharacterAction;
126:
127: try
128: {
129: if (oldMalformedInputAction != CodingErrorAction.REPORT)
130: onMalformedInput (CodingErrorAction.REPORT);
131: if (oldUnmappableCharacterAction != CodingErrorAction.REPORT)
132: onUnmappableCharacter (CodingErrorAction.REPORT);
133: }
134: catch (Exception e)
135: {
136: return false;
137: }
138: finally
139: {
140: if (oldMalformedInputAction != CodingErrorAction.REPORT)
141: onMalformedInput (oldMalformedInputAction);
142: if (oldUnmappableCharacterAction != CodingErrorAction.REPORT)
143: onUnmappableCharacter (oldUnmappableCharacterAction);
144: }
145:
146: return true;
147: }
148:
149: public final Charset charset ()
150: {
151: return charset;
152: }
153:
154: public final ByteBuffer encode (CharBuffer in)
155: throws CharacterCodingException
156: {
157:
158:
159:
160:
161:
162: if (state != STATE_RESET)
163: throw new IllegalStateException ();
164:
165:
166:
167: int remaining = in.remaining ();
168: int n = (int) (remaining * maxBytesPerChar ());
169: ByteBuffer out = ByteBuffer.allocate (n);
170:
171: if (remaining == 0)
172: {
173: state = STATE_FLUSHED;
174: return out;
175: }
176:
177: CoderResult cr = encode (in, out, true);
178: if (cr.isError ())
179: cr.throwException ();
180:
181: cr = flush (out);
182: if (cr.isError ())
183: cr.throwException ();
184:
185: out.flip ();
186:
187:
188: byte[] resized = new byte[out.remaining()];
189: out.get(resized);
190: return ByteBuffer.wrap(resized);
191: }
192:
193: public final CoderResult encode (CharBuffer in, ByteBuffer out,
194: boolean endOfInput)
195: {
196: int newState = endOfInput ? STATE_END : STATE_CODING;
197:
198:
199:
200:
201:
202: if (state != STATE_RESET && state != STATE_CODING
203: && !(endOfInput && state == STATE_END))
204: throw new IllegalStateException ();
205: state = newState;
206:
207: for (;;)
208: {
209: CoderResult cr;
210: try
211: {
212: cr = encodeLoop (in, out);
213: }
214: catch (RuntimeException e)
215: {
216: throw new CoderMalfunctionError (e);
217: }
218:
219: if (cr.isOverflow ())
220: return cr;
221:
222: if (cr.isUnderflow ())
223: {
224: if (endOfInput && in.hasRemaining ())
225: cr = CoderResult.malformedForLength (in.remaining ());
226: else
227: return cr;
228: }
229:
230: CodingErrorAction action = cr.isMalformed ()
231: ? malformedInputAction
232: : unmappableCharacterAction;
233:
234: if (action == CodingErrorAction.REPORT)
235: return cr;
236:
237: if (action == CodingErrorAction.REPLACE)
238: {
239: if (out.remaining () < replacement.length)
240: return CoderResult.OVERFLOW;
241: out.put (replacement);
242: }
243:
244: in.position (in.position () + cr.length ());
245: }
246: }
247:
248: protected abstract CoderResult encodeLoop (CharBuffer in, ByteBuffer out);
249:
250: public final CoderResult flush (ByteBuffer out)
251: {
252:
253:
254:
255:
256:
257:
258:
259:
260:
261:
262: if (state != STATE_RESET && state != STATE_END)
263: throw new IllegalStateException ();
264:
265: state = STATE_FLUSHED;
266: return implFlush (out);
267: }
268:
269: protected CoderResult implFlush (ByteBuffer out)
270: {
271: return CoderResult.UNDERFLOW;
272: }
273:
274: protected void implOnMalformedInput (CodingErrorAction newAction)
275: {
276:
277: }
278:
279: protected void implOnUnmappableCharacter (CodingErrorAction newAction)
280: {
281:
282: }
283:
284: protected void implReplaceWith (byte[] newReplacement)
285: {
286:
287: }
288:
289: protected void implReset ()
290: {
291:
292: }
293:
294: public boolean isLegalReplacement (byte[] replacement)
295: {
296:
297:
298: CharsetDecoder decoder = charset.newDecoder ();
299: ByteBuffer bb = ByteBuffer.wrap (replacement);
300: CharBuffer cb
301: = CharBuffer.allocate ((int) (replacement.length
302: * decoder.maxCharsPerByte ()));
303: return !decoder.decode (bb, cb, true).isError ();
304: }
305:
306: public CodingErrorAction malformedInputAction ()
307: {
308: return malformedInputAction;
309: }
310:
311: public final float maxBytesPerChar ()
312: {
313: return maxBytesPerChar;
314: }
315:
316: public final CharsetEncoder onMalformedInput (CodingErrorAction newAction)
317: {
318: if (newAction == null)
319: throw new IllegalArgumentException ("Null action");
320:
321: malformedInputAction = newAction;
322: implOnMalformedInput (newAction);
323: return this;
324: }
325:
326: public CodingErrorAction unmappableCharacterAction ()
327: {
328: return unmappableCharacterAction;
329: }
330:
331: public final CharsetEncoder onUnmappableCharacter
332: (CodingErrorAction newAction)
333: {
334: if (newAction == null)
335: throw new IllegalArgumentException ("Null action");
336:
337: unmappableCharacterAction = newAction;
338: implOnUnmappableCharacter (newAction);
339: return this;
340: }
341:
342: public final byte[] replacement ()
343: {
344: return replacement;
345: }
346:
347: public final CharsetEncoder replaceWith (byte[] newReplacement)
348: {
349: if (newReplacement == null)
350: throw new IllegalArgumentException ("Null replacement");
351: if (newReplacement.length == 0)
352: throw new IllegalArgumentException ("Empty replacement");
353:
354:
355: if (!isLegalReplacement (newReplacement))
356: throw new IllegalArgumentException ("Illegal replacement");
357:
358: this.replacement = newReplacement;
359: implReplaceWith (newReplacement);
360: return this;
361: }
362:
363: public final CharsetEncoder reset ()
364: {
365: state = STATE_RESET;
366: implReset ();
367: return this;
368: }
369: }