/[classpath]/classpath/java/nio/charset/CharsetEncoder.java
ViewVC logotype

Diff of /classpath/java/nio/charset/CharsetEncoder.java

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 1.3 by mark, Tue Apr 30 21:37:27 2002 UTC revision 1.4 by mkoch, Fri Nov 8 12:15:32 2002 UTC
# Line 37  exception statement from your version. * Line 37  exception statement from your version. *
37    
38  package java.nio.charset;  package java.nio.charset;
39    
40  import java.nio.*;  import java.nio.ByteBuffer;
41    import java.nio.CharBuffer;
42    
43    /**
44     * @author Jesse Rosenstock
45     * @since 1.4
46     */
47  public abstract class CharsetEncoder  public abstract class CharsetEncoder
48  {  {
49      Charset cs;    private static final int STATE_RESET   = 0;
50      float averageBytesPerChar;    private static final int STATE_CODING  = 1;
51      float maxBytesPerChar;    private static final int STATE_END     = 2;
52      byte[] repl;    private static final int STATE_FLUSHED = 3;
53          
54      protected CharsetEncoder(Charset cs,    private static final byte[] DEFAULT_REPLACEMENT = {(byte)'?'};
55                               float averageBytesPerChar,  
56                               float maxBytesPerChar)    private final Charset charset;
57      {    private final float averageBytesPerChar;
58          this(cs, averageBytesPerChar, maxBytesPerChar,  null);    private final float maxBytesPerChar;
59      }    private byte[] replacement;
60    
61      protected CharsetEncoder(Charset cs,    private int state = STATE_RESET;
62                               float averageBytesPerChar,  
63                               float maxBytesPerChar,    private CodingErrorAction malformedInputAction
64                               byte[] replacement)      = CodingErrorAction.REPORT;
65      {    private CodingErrorAction unmappableCharacterAction
66          this.cs = cs;      = CodingErrorAction.REPORT;
67          this.maxBytesPerChar = maxBytesPerChar;  
68          this.averageBytesPerChar = averageBytesPerChar;    protected CharsetEncoder (Charset cs, float averageBytesPerChar,
69          this.repl = replacement;                              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 float averageBytesPerChar()    public final float averageBytesPerChar ()
92      {    {
93          return averageBytesPerChar;      return averageBytesPerChar;
94      }    }
95    
96      public boolean canEncode(char c)    public boolean canEncode (char c)
97      {    {
98          return true;      CharBuffer cb = CharBuffer.allocate (1).put (c);
99      }      cb.flip ();
100            return canEncode (cb);
101      public boolean canEncode(CharSequence cs)    }
102      {  
103          return true;    public boolean canEncode (CharSequence cs)
104      }    {
105            CharBuffer cb;
106      public Charset charset()      if (cs instanceof CharBuffer)
107      {        cb = ((CharBuffer) cs).duplicate ();
108          return cs;      else
109      }        cb = CharBuffer.wrap (cs);
110            return canEncode (cb);
111      public ByteBuffer encode(CharBuffer in)    }
112      {  
113          ByteBuffer x = ByteBuffer.allocate(in.remaining());    private boolean canEncode (CharBuffer cb)
114          encode(in, x, false);    {
115          return x;      // It is an error if a coding operation is "in progress"
116      }      // I take that to mean the state is not reset or flushed.
117            // XXX: check "in progress" everywhere
118      public CoderResult encode(CharBuffer in, ByteBuffer out, boolean endOfInput)      if (state == STATE_FLUSHED)
119      {          reset ();
120          return encodeLoop(in, out);      else if (state != STATE_RESET)
121      }        throw new IllegalStateException ();
122        
123            CodingErrorAction oldMalformedInputAction = malformedInputAction;
124      protected abstract  CoderResult encodeLoop(CharBuffer in, ByteBuffer out);      CodingErrorAction oldUnmappableCharacterAction
125              = unmappableCharacterAction;
126      public CoderResult flush(ByteBuffer out)  
127      {      try
128          return implFlush(out);        {
129      }          if (oldMalformedInputAction != CodingErrorAction.REPORT)
130              onMalformedInput (CodingErrorAction.REPORT);
131      protected  CoderResult implFlush(ByteBuffer out)          if (oldUnmappableCharacterAction != CodingErrorAction.REPORT)
132      {            onUnmappableCharacter (CodingErrorAction.REPORT);
133          return null;        }
134      }      catch (Exception e)
135          {
136      protected  void implOnMalformedInput(CodingErrorAction newAction)          return false;
137      {        }
138      }      finally
139          {
140      protected  void implOnUnmappableCharacter(CodingErrorAction newAction)          if (oldMalformedInputAction != CodingErrorAction.REPORT)
141      {            onMalformedInput (oldMalformedInputAction);
142      }          if (oldUnmappableCharacterAction != CodingErrorAction.REPORT)
143              onUnmappableCharacter (oldUnmappableCharacterAction);
144      protected  void implReplaceWith(byte[] newReplacement)        }
145      {  
146      }      return true;
147          }
148      protected  void implReset()  
149      {    public final Charset charset ()
150      }    {
151        return charset;
152   boolean isLegalReplacement(byte[] repl)    }
153      {  
154          return true;    public final ByteBuffer encode (CharBuffer in)
155      }      throws CharacterCodingException
156      {
157        // XXX: Sun's Javadoc seems to contradict itself saying an
158      public CodingErrorAction malformedInputAction()      // IllegalStateException is thrown "if a decoding operation is already
159      {      // in progress" and also that "it resets this Encoder".
160          return null;      // Should we check to see that the state is reset, or should we
161      }      // call reset()?
162        if (state != STATE_RESET)
163      public float maxBytesPerChar()        throw new IllegalStateException ();
164      {  
165          return  maxBytesPerChar;      // REVIEW: Using max instead of average may allocate a very large
166      }          // buffer.  Maybe we should do something more efficient?
167        int remaining = in.remaining ();
168      public CharsetEncoder onMalformedInput(CodingErrorAction newAction)      int n = (int) (remaining * maxBytesPerChar ());
169      {      ByteBuffer out = ByteBuffer.allocate (n);
170          return null;  
171      }      if (remaining == 0)
172          {
173      public CharsetEncoder onUnmappableCharacter(CodingErrorAction newAction)          state = STATE_FLUSHED;
174      {          return out;
175          return null;        }
176      }  
177            CoderResult cr = encode (in, out, true);
178      public byte[] replacement()      if (cr.isError ())
179      {        cr.throwException ();
180          return repl;  
181      }      cr = flush (out);
182        if (cr.isError ())
183      public CharsetEncoder replaceWith(byte[] newReplacement)        cr.throwException ();
184      {  
185          repl = newReplacement;      out.flip ();
186          return null;      return out;
187      }    }
188    
189      public CharsetEncoder reset()    public final CoderResult encode (CharBuffer in, ByteBuffer out,
190      {                                     boolean endOfInput)
191          return null;    {
192      }      int newState = endOfInput ? STATE_END : STATE_CODING;
193        // XXX: Need to check for "previous step was an invocation [not] of
194      public CodingErrorAction unmappableCharacterAction()      // this method with a value of true for the endOfInput parameter but
195      {      // a return value indicating an incomplete decoding operation"
196          return null;      // XXX: We will not check the previous return value, just
197      }      // that the previous call passed true for endOfInput
198        if (state != STATE_RESET && state != STATE_CODING
199            && !(endOfInput && state == STATE_END))
200          throw new IllegalStateException ();
201        state = newState;
202    
203        for (;;)
204          {
205            CoderResult cr;
206            try
207              {
208                cr = encodeLoop (in, out);
209              }
210            catch (RuntimeException e)
211              {
212                throw new CoderMalfunctionError (e);
213              }
214    
215            if (cr.isOverflow ())
216              return cr;
217    
218            if (cr.isUnderflow ())
219              {
220                if (endOfInput && in.hasRemaining ())
221                  cr = CoderResult.malformedForLength (in.remaining ());
222                else
223                  return cr;
224              }
225    
226            CodingErrorAction action = cr.isMalformed ()
227                                         ? malformedInputAction
228                                         : unmappableCharacterAction;
229    
230            if (action == CodingErrorAction.REPORT)
231              return cr;
232    
233            if (action == CodingErrorAction.REPLACE)
234              {
235                if (out.remaining () < replacement.length)
236                  return CoderResult.OVERFLOW;
237                out.put (replacement);
238              }
239    
240            in.position (in.position () + cr.length ());
241          }
242      }
243    
244      protected abstract CoderResult encodeLoop (CharBuffer in, ByteBuffer out);
245    
246      public final CoderResult flush (ByteBuffer out)
247      {
248        // It seems weird that you can flush after reset, but Sun's javadoc
249        // says an IllegalStateException is thrown "If the previous step of the
250        // current decoding operation was an invocation neither of the reset
251        // method nor ... of the three-argument encode method with a value of
252        // true for the endOfInput parameter."
253        // Further note that flush() only requires that there not be
254        // an IllegalStateException if the previous step was a call to
255        // encode with true as the last argument.  It does not require
256        // that the call succeeded.  encode() does require that it succeeded.
257        // XXX: test this to see if reality matches javadoc
258        if (state != STATE_RESET && state != STATE_END)
259          throw new IllegalStateException ();
260    
261        state = STATE_FLUSHED;
262        return implFlush (out);
263      }
264    
265      protected CoderResult implFlush (ByteBuffer out)
266      {
267        return CoderResult.UNDERFLOW;
268      }
269    
270      protected void implOnMalformedInput (CodingErrorAction newAction)
271      {
272        // default implementation does nothing
273      }
274    
275      protected void implOnUnmappableCharacter (CodingErrorAction newAction)
276      {
277        // default implementation does nothing
278      }
279    
280      protected void implReplaceWith (byte[] newReplacement)
281      {
282        // default implementation does nothing
283      }
284    
285      protected void implReset ()
286      {
287        // default implementation does nothing
288      }
289    
290      public boolean isLegalReplacement (byte[] replacement)
291      {
292        // TODO: cache the decoder
293        // error actions will be REPORT after construction
294        CharsetDecoder decoder = charset.newDecoder ();
295        ByteBuffer bb = ByteBuffer.wrap (replacement);
296        CharBuffer cb
297          = CharBuffer.allocate ((int) (replacement.length
298                                        * decoder.maxCharsPerByte ()));
299        return !decoder.decode (bb, cb, true).isError ();
300      }
301    
302      public CodingErrorAction malformedInputAction ()
303      {
304        return malformedInputAction;
305      }
306    
307      public final float maxBytesPerChar ()
308      {
309        return maxBytesPerChar;
310      }
311    
312      public final CharsetEncoder onMalformedInput (CodingErrorAction newAction)
313      {
314        if (newAction == null)
315          throw new IllegalArgumentException ("Null action");
316    
317        malformedInputAction = newAction;
318        implOnMalformedInput (newAction);
319        return this;
320      }
321    
322      public final CharsetEncoder onUnmappableCharacter
323        (CodingErrorAction newAction)
324      {
325        if (newAction == null)
326          throw new IllegalArgumentException ("Null action");
327    
328        unmappableCharacterAction = newAction;
329        implOnUnmappableCharacter (newAction);
330        return this;
331      }
332    
333      public final byte[] replacement ()
334      {
335        return replacement;
336      }
337    
338      public final CharsetEncoder replaceWith (byte[] newReplacement)
339      {
340        if (newReplacement == null)
341          throw new IllegalArgumentException ("Null replacement");
342        if (newReplacement.length == 0)
343          throw new IllegalArgumentException ("Empty replacement");
344        // XXX: what about maxBytesPerChar?
345    
346          if (!isLegalReplacement (newReplacement))
347            throw new IllegalArgumentException ("Illegal replacement");
348    
349        this.replacement = newReplacement;
350        implReplaceWith (newReplacement);
351        return this;
352      }
353    
354      public final CharsetEncoder reset ()
355      {
356        state = STATE_RESET;
357        implReset ();
358        return this;
359      }
360  }  }

Legend:
Removed from v.1.3  
changed lines
  Added in v.1.4

savannah-hackers-public@gnu.org
ViewVC Help
Powered by ViewVC 1.1.26