Save This Page
Home » openjdk-7 » java » nio » charset » [javadoc | source]
    1   /* CharsetDecoder.java -- 
    2      Copyright (C) 2002 Free Software Foundation, Inc.
    3   
    4   This file is part of GNU Classpath.
    5   
    6   GNU Classpath is free software; you can redistribute it and/or modify
    7   it under the terms of the GNU General Public License as published by
    8   the Free Software Foundation; either version 2, or (at your option)
    9   any later version.
   10   
   11   GNU Classpath is distributed in the hope that it will be useful, but
   12   WITHOUT ANY WARRANTY; without even the implied warranty of
   13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   14   General Public License for more details.
   15   
   16   You should have received a copy of the GNU General Public License
   17   along with GNU Classpath; see the file COPYING.  If not, write to the
   18   Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
   19   02110-1301 USA.
   20   
   21   Linking this library statically or dynamically with other modules is
   22   making a combined work based on this library.  Thus, the terms and
   23   conditions of the GNU General Public License cover the whole
   24   combination.
   25   
   26   As a special exception, the copyright holders of this library give you
   27   permission to link this library with independent modules to produce an
   28   executable, regardless of the license terms of these independent
   29   modules, and to copy and distribute the resulting executable under
   30   terms of your choice, provided that you also meet, for each linked
   31   independent module, the terms and conditions of the license of that
   32   module.  An independent module is a module which is not derived from
   33   or based on this library.  If you modify this library, you may extend
   34   this exception to your version of the library, but you are not
   35   obligated to do so.  If you do not wish to do so, delete this
   36   exception statement from your version. */
   37   
   38   package java.nio.charset;
   39   
   40   import java.nio.ByteBuffer;
   41   import java.nio.CharBuffer;
   42   
   43   /**
   44    * @author Jesse Rosenstock
   45    * @since 1.4
   46    */
   47   public abstract class CharsetDecoder
   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 String DEFAULT_REPLACEMENT = "\uFFFD";
   55   
   56     private final Charset charset;
   57     private final float averageCharsPerByte;
   58     private final float maxCharsPerByte;
   59     private String 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     private CharsetDecoder (Charset cs, float averageCharsPerByte,
   69                             float maxCharsPerByte, String replacement)
   70     {
   71       if (averageCharsPerByte <= 0.0f)
   72         throw new IllegalArgumentException ("Non-positive averageCharsPerByte");
   73       if (maxCharsPerByte <= 0.0f)
   74         throw new IllegalArgumentException ("Non-positive maxCharsPerByte");
   75   
   76       this.charset = cs;
   77       this.averageCharsPerByte
   78         = averageCharsPerByte;
   79       this.maxCharsPerByte
   80         = maxCharsPerByte;
   81       this.replacement = replacement;
   82       implReplaceWith (replacement);
   83     }
   84   
   85     protected CharsetDecoder (Charset cs, float averageCharsPerByte,
   86                               float maxCharsPerByte)
   87     {
   88       this (cs, averageCharsPerByte, maxCharsPerByte, DEFAULT_REPLACEMENT);
   89     }
   90   
   91     public final float averageCharsPerByte ()
   92     {
   93       return averageCharsPerByte;
   94     }
   95   
   96     public final Charset charset ()
   97     {
   98       return charset;
   99     }
  100   
  101     public final CharBuffer decode (ByteBuffer in)
  102       throws CharacterCodingException
  103     {
  104       // XXX: Sun's Javadoc seems to contradict itself saying an
  105       // IllegalStateException is thrown "if a decoding operation is already
  106       // in progress" and also that "it resets this Decoder".
  107       // Should we check to see that the state is reset, or should we
  108       // call reset()?
  109       if (state != STATE_RESET)
  110         throw new IllegalStateException ();
  111   
  112       // REVIEW: Using max instead of average may allocate a very large
  113       // buffer.  Maybe we should do something more efficient?
  114       int remaining = in.remaining ();
  115       int n = (int) (remaining * maxCharsPerByte ());
  116       CharBuffer out = CharBuffer.allocate (n);
  117   
  118       if (remaining == 0)
  119         {
  120           state = STATE_FLUSHED;
  121           return out;
  122         }
  123   
  124       CoderResult cr = decode (in, out, true);
  125       if (cr.isError ())
  126         cr.throwException ();
  127   
  128       cr = flush (out);
  129       if (cr.isError ())
  130         cr.throwException ();
  131   
  132       reset();
  133       out.flip ();
  134   
  135       // Unfortunately, resizing the actual charbuffer array is required.
  136       char[] resized = new char[out.remaining()];
  137       out.get(resized);
  138       return CharBuffer.wrap(resized);
  139     }
  140   
  141     public final CoderResult decode (ByteBuffer in, CharBuffer out,
  142                                      boolean endOfInput)
  143     {
  144       int newState = endOfInput ? STATE_END : STATE_CODING;
  145       // XXX: Need to check for "previous step was an invocation [not] of
  146       // this method with a value of true for the endOfInput parameter but
  147       // a return value indicating an incomplete decoding operation"
  148       // XXX: We will not check the previous return value, just
  149       // that the previous call passed true for endOfInput
  150       if (state != STATE_RESET && state != STATE_CODING
  151           && !(endOfInput && state == STATE_END))
  152         throw new IllegalStateException ();
  153       state = newState;
  154   
  155       for (;;)
  156         {
  157           CoderResult cr;
  158           try
  159             {
  160               cr = decodeLoop (in, out);
  161             }
  162           catch (RuntimeException e)
  163   	  {
  164               throw new CoderMalfunctionError (e);
  165             }
  166   
  167           if (cr.isOverflow ())
  168             return cr;
  169   
  170           if (cr.isUnderflow ())
  171             {
  172               if (endOfInput && in.hasRemaining ())
  173                 cr = CoderResult.malformedForLength (in.remaining ());
  174               else
  175                 return cr;
  176             }
  177   
  178           CodingErrorAction action = cr.isMalformed ()
  179                                        ? malformedInputAction
  180                                        : unmappableCharacterAction;
  181   
  182           if (action == CodingErrorAction.REPORT)
  183             return cr;
  184   
  185           if (action == CodingErrorAction.REPLACE)
  186             {
  187               if (out.remaining () < replacement.length ())
  188                 return CoderResult.OVERFLOW;
  189               out.put (replacement);
  190             }
  191   
  192           in.position (in.position () + cr.length ());
  193         }
  194     }
  195   
  196     protected abstract CoderResult decodeLoop (ByteBuffer in, CharBuffer out);
  197   
  198     public Charset detectedCharset ()
  199     {
  200       throw new UnsupportedOperationException ();
  201     }
  202       
  203     public final CoderResult flush (CharBuffer out)
  204     {
  205       // It seems weird that you can flush after reset, but Sun's javadoc
  206       // says an IllegalStateException is thrown "If the previous step of the
  207       // current decoding operation was an invocation neither of the reset
  208       // method nor ... of the three-argument decode method with a value of
  209       // true for the endOfInput parameter."
  210       // Further note that flush() only requires that there not be
  211       // an IllegalStateException if the previous step was a call to
  212       // decode with true as the last argument.  It does not require
  213       // that the call succeeded.  decode() does require that it succeeded.
  214       // XXX: test this to see if reality matches javadoc
  215       if (state != STATE_RESET && state != STATE_END)
  216         throw new IllegalStateException ();
  217   
  218       state = STATE_FLUSHED;
  219       return implFlush (out);
  220     }
  221   
  222     protected CoderResult implFlush (CharBuffer out)
  223     {
  224       return CoderResult.UNDERFLOW;
  225     }
  226   
  227     public final CharsetDecoder onMalformedInput (CodingErrorAction newAction)
  228     {
  229       if (newAction == null)
  230         throw new IllegalArgumentException ("Null action");
  231   
  232       malformedInputAction = newAction;
  233       implOnMalformedInput (newAction);
  234       return this;
  235     }
  236   
  237     protected void implOnMalformedInput (CodingErrorAction newAction)
  238     {
  239       // default implementation does nothing
  240     }
  241   
  242     protected void implOnUnmappableCharacter (CodingErrorAction newAction)
  243     {
  244       // default implementation does nothing
  245     }
  246   
  247     protected void implReplaceWith (String newReplacement)
  248     {
  249       // default implementation does nothing
  250     }
  251   
  252     protected void implReset ()
  253     {
  254       // default implementation does nothing
  255     }
  256   
  257     public boolean isAutoDetecting ()
  258     {
  259       return false;
  260     }
  261   
  262     public boolean isCharsetDetected ()
  263     {
  264       throw new UnsupportedOperationException ();
  265     }
  266   
  267     public CodingErrorAction malformedInputAction ()
  268     {
  269       return malformedInputAction;
  270     }
  271   
  272     public final float maxCharsPerByte ()
  273     {
  274       return maxCharsPerByte;
  275     }
  276   
  277     public final CharsetDecoder onUnmappableCharacter
  278       (CodingErrorAction newAction)
  279     {
  280       if (newAction == null)
  281         throw new IllegalArgumentException ("Null action");
  282   
  283       unmappableCharacterAction = newAction;
  284       implOnUnmappableCharacter (newAction);
  285       return this;
  286     }
  287   
  288     public final String replacement ()
  289     {
  290       return replacement;
  291     }
  292   
  293     public final CharsetDecoder replaceWith (String newReplacement)
  294     {
  295       if (newReplacement == null)
  296         throw new IllegalArgumentException ("Null replacement");
  297       if (newReplacement.length () == 0)
  298         throw new IllegalArgumentException ("Empty replacement");
  299       // XXX: what about maxCharsPerByte?
  300   
  301       this.replacement = newReplacement;
  302       implReplaceWith (newReplacement);
  303       return this;
  304     }
  305   
  306     public final CharsetDecoder reset ()
  307     {
  308       state = STATE_RESET;
  309       implReset ();
  310       return this;
  311     }
  312   
  313     public CodingErrorAction unmappableCharacterAction ()
  314     {
  315       return unmappableCharacterAction;
  316     }
  317   }

Save This Page
Home » openjdk-7 » java » nio » charset » [javadoc | source]