mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			370 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Java
		
	
	
	
			
		
		
	
	
			370 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Java
		
	
	
	
/* CharsetEncoder.java --
 | 
						|
   Copyright (C) 2002 Free Software Foundation, Inc.
 | 
						|
 | 
						|
This file is part of GNU Classpath.
 | 
						|
 | 
						|
GNU Classpath is free software; you can redistribute it and/or modify
 | 
						|
it under the terms of the GNU General Public License as published by
 | 
						|
the Free Software Foundation; either version 2, or (at your option)
 | 
						|
any later version.
 | 
						|
 | 
						|
GNU Classpath is distributed in the hope that it will be useful, but
 | 
						|
WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | 
						|
General Public License for more details.
 | 
						|
 | 
						|
You should have received a copy of the GNU General Public License
 | 
						|
along with GNU Classpath; see the file COPYING.  If not, write to the
 | 
						|
Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 | 
						|
02110-1301 USA.
 | 
						|
 | 
						|
Linking this library statically or dynamically with other modules is
 | 
						|
making a combined work based on this library.  Thus, the terms and
 | 
						|
conditions of the GNU General Public License cover the whole
 | 
						|
combination.
 | 
						|
 | 
						|
As a special exception, the copyright holders of this library give you
 | 
						|
permission to link this library with independent modules to produce an
 | 
						|
executable, regardless of the license terms of these independent
 | 
						|
modules, and to copy and distribute the resulting executable under
 | 
						|
terms of your choice, provided that you also meet, for each linked
 | 
						|
independent module, the terms and conditions of the license of that
 | 
						|
module.  An independent module is a module which is not derived from
 | 
						|
or based on this library.  If you modify this library, you may extend
 | 
						|
this exception to your version of the library, but you are not
 | 
						|
obligated to do so.  If you do not wish to do so, delete this
 | 
						|
exception statement from your version. */
 | 
						|
 | 
						|
package java.nio.charset;
 | 
						|
 | 
						|
import java.nio.ByteBuffer;
 | 
						|
import java.nio.CharBuffer;
 | 
						|
 | 
						|
/**
 | 
						|
 * @author Jesse Rosenstock
 | 
						|
 * @since 1.4
 | 
						|
 */
 | 
						|
public abstract class CharsetEncoder
 | 
						|
{
 | 
						|
  private static final int STATE_RESET   = 0;
 | 
						|
  private static final int STATE_CODING  = 1;
 | 
						|
  private static final int STATE_END     = 2;
 | 
						|
  private static final int STATE_FLUSHED = 3;
 | 
						|
 | 
						|
  private static final byte[] DEFAULT_REPLACEMENT = {(byte)'?'};
 | 
						|
 | 
						|
  private final Charset charset;
 | 
						|
  private final float averageBytesPerChar;
 | 
						|
  private final float maxBytesPerChar;
 | 
						|
  private byte[] replacement;
 | 
						|
 | 
						|
  private int state = STATE_RESET;
 | 
						|
 | 
						|
  private CodingErrorAction malformedInputAction
 | 
						|
    = CodingErrorAction.REPORT;
 | 
						|
  private CodingErrorAction unmappableCharacterAction
 | 
						|
    = CodingErrorAction.REPORT;
 | 
						|
 | 
						|
  protected CharsetEncoder (Charset cs, float averageBytesPerChar,
 | 
						|
                            float maxBytesPerChar)
 | 
						|
  {
 | 
						|
    this (cs, averageBytesPerChar, maxBytesPerChar, DEFAULT_REPLACEMENT);
 | 
						|
  }
 | 
						|
 | 
						|
  protected CharsetEncoder (Charset cs, float averageBytesPerChar,
 | 
						|
                            float maxBytesPerChar, byte[] replacement)
 | 
						|
  {
 | 
						|
    if (averageBytesPerChar <= 0.0f)
 | 
						|
      throw new IllegalArgumentException ("Non-positive averageBytesPerChar");
 | 
						|
    if (maxBytesPerChar <= 0.0f)
 | 
						|
      throw new IllegalArgumentException ("Non-positive maxBytesPerChar");
 | 
						|
 | 
						|
    this.charset = cs;
 | 
						|
    this.averageBytesPerChar
 | 
						|
      = averageBytesPerChar;
 | 
						|
    this.maxBytesPerChar
 | 
						|
      = maxBytesPerChar;
 | 
						|
    this.replacement = replacement;
 | 
						|
    implReplaceWith (replacement);
 | 
						|
  }
 | 
						|
 | 
						|
  public final float averageBytesPerChar ()
 | 
						|
  {
 | 
						|
    return averageBytesPerChar;
 | 
						|
  }
 | 
						|
 | 
						|
  public boolean canEncode (char c)
 | 
						|
  {
 | 
						|
    CharBuffer cb = CharBuffer.allocate (1).put (c);
 | 
						|
    cb.flip ();
 | 
						|
    return canEncode (cb);
 | 
						|
  }
 | 
						|
 | 
						|
  public boolean canEncode (CharSequence cs)
 | 
						|
  {
 | 
						|
    CharBuffer cb;
 | 
						|
    if (cs instanceof CharBuffer)
 | 
						|
      cb = ((CharBuffer) cs).duplicate ();
 | 
						|
    else
 | 
						|
      cb = CharBuffer.wrap (cs);
 | 
						|
    return canEncode (cb);
 | 
						|
  }
 | 
						|
 | 
						|
  private boolean canEncode (CharBuffer cb)
 | 
						|
  {
 | 
						|
    // It is an error if a coding operation is "in progress"
 | 
						|
    // I take that to mean the state is not reset or flushed.
 | 
						|
    // XXX: check "in progress" everywhere
 | 
						|
    if (state == STATE_FLUSHED)
 | 
						|
      reset ();
 | 
						|
    else if (state != STATE_RESET)
 | 
						|
      throw new IllegalStateException ();
 | 
						|
 | 
						|
    CodingErrorAction oldMalformedInputAction = malformedInputAction;
 | 
						|
    CodingErrorAction oldUnmappableCharacterAction
 | 
						|
      = unmappableCharacterAction;
 | 
						|
 | 
						|
    try
 | 
						|
      {
 | 
						|
        if (oldMalformedInputAction != CodingErrorAction.REPORT)
 | 
						|
          onMalformedInput (CodingErrorAction.REPORT);
 | 
						|
        if (oldUnmappableCharacterAction != CodingErrorAction.REPORT)
 | 
						|
          onUnmappableCharacter (CodingErrorAction.REPORT);
 | 
						|
      }
 | 
						|
    catch (Exception e)
 | 
						|
      {
 | 
						|
        return false;
 | 
						|
      }
 | 
						|
    finally
 | 
						|
      {
 | 
						|
        if (oldMalformedInputAction != CodingErrorAction.REPORT)
 | 
						|
          onMalformedInput (oldMalformedInputAction);
 | 
						|
        if (oldUnmappableCharacterAction != CodingErrorAction.REPORT)
 | 
						|
          onUnmappableCharacter (oldUnmappableCharacterAction);
 | 
						|
      }
 | 
						|
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
 | 
						|
  public final Charset charset ()
 | 
						|
  {
 | 
						|
    return charset;
 | 
						|
  }
 | 
						|
 | 
						|
  public final ByteBuffer encode (CharBuffer in)
 | 
						|
    throws CharacterCodingException
 | 
						|
  {
 | 
						|
    // XXX: Sun's Javadoc seems to contradict itself saying an
 | 
						|
    // IllegalStateException is thrown "if a decoding operation is already
 | 
						|
    // in progress" and also that "it resets this Encoder".
 | 
						|
    // Should we check to see that the state is reset, or should we
 | 
						|
    // call reset()?
 | 
						|
    if (state != STATE_RESET)
 | 
						|
      throw new IllegalStateException ();
 | 
						|
 | 
						|
    // REVIEW: Using max instead of average may allocate a very large
 | 
						|
    // buffer.  Maybe we should do something more efficient?
 | 
						|
    int remaining = in.remaining ();
 | 
						|
    int n = (int) (remaining * maxBytesPerChar ());
 | 
						|
    ByteBuffer out = ByteBuffer.allocate (n);
 | 
						|
 | 
						|
    if (remaining == 0)
 | 
						|
      {
 | 
						|
        state = STATE_FLUSHED;
 | 
						|
        return out;
 | 
						|
      }
 | 
						|
 | 
						|
    CoderResult cr = encode (in, out, true);
 | 
						|
    if (cr.isError ())
 | 
						|
      cr.throwException ();
 | 
						|
 | 
						|
    cr = flush (out);
 | 
						|
    if (cr.isError ())
 | 
						|
      cr.throwException ();
 | 
						|
 | 
						|
    out.flip ();
 | 
						|
 | 
						|
    // Unfortunately, resizing the actual bytebuffer array is required.
 | 
						|
    byte[] resized = new byte[out.remaining()];
 | 
						|
    out.get(resized);
 | 
						|
    return ByteBuffer.wrap(resized);
 | 
						|
  }
 | 
						|
 | 
						|
  public final CoderResult encode (CharBuffer in, ByteBuffer out,
 | 
						|
                                   boolean endOfInput)
 | 
						|
  {
 | 
						|
    int newState = endOfInput ? STATE_END : STATE_CODING;
 | 
						|
    // XXX: Need to check for "previous step was an invocation [not] of
 | 
						|
    // this method with a value of true for the endOfInput parameter but
 | 
						|
    // a return value indicating an incomplete decoding operation"
 | 
						|
    // XXX: We will not check the previous return value, just
 | 
						|
    // that the previous call passed true for endOfInput
 | 
						|
    if (state != STATE_RESET && state != STATE_CODING
 | 
						|
        && !(endOfInput && state == STATE_END))
 | 
						|
      throw new IllegalStateException ();
 | 
						|
    state = newState;
 | 
						|
 | 
						|
    for (;;)
 | 
						|
      {
 | 
						|
        CoderResult cr;
 | 
						|
        try
 | 
						|
          {
 | 
						|
            cr = encodeLoop (in, out);
 | 
						|
          }
 | 
						|
        catch (RuntimeException e)
 | 
						|
          {
 | 
						|
            throw new CoderMalfunctionError (e);
 | 
						|
          }
 | 
						|
 | 
						|
        if (cr.isOverflow ())
 | 
						|
          return cr;
 | 
						|
 | 
						|
        if (cr.isUnderflow ())
 | 
						|
          {
 | 
						|
            if (endOfInput && in.hasRemaining ())
 | 
						|
              cr = CoderResult.malformedForLength (in.remaining ());
 | 
						|
            else
 | 
						|
              return cr;
 | 
						|
          }
 | 
						|
 | 
						|
        CodingErrorAction action = cr.isMalformed ()
 | 
						|
                                     ? malformedInputAction
 | 
						|
                                     : unmappableCharacterAction;
 | 
						|
 | 
						|
        if (action == CodingErrorAction.REPORT)
 | 
						|
          return cr;
 | 
						|
 | 
						|
        if (action == CodingErrorAction.REPLACE)
 | 
						|
          {
 | 
						|
            if (out.remaining () < replacement.length)
 | 
						|
              return CoderResult.OVERFLOW;
 | 
						|
            out.put (replacement);
 | 
						|
          }
 | 
						|
 | 
						|
        in.position (in.position () + cr.length ());
 | 
						|
      }
 | 
						|
  }
 | 
						|
 | 
						|
  protected abstract CoderResult encodeLoop (CharBuffer in, ByteBuffer out);
 | 
						|
 | 
						|
  public final CoderResult flush (ByteBuffer out)
 | 
						|
  {
 | 
						|
    // It seems weird that you can flush after reset, but Sun's javadoc
 | 
						|
    // says an IllegalStateException is thrown "If the previous step of the
 | 
						|
    // current decoding operation was an invocation neither of the reset
 | 
						|
    // method nor ... of the three-argument encode method with a value of
 | 
						|
    // true for the endOfInput parameter."
 | 
						|
    // Further note that flush() only requires that there not be
 | 
						|
    // an IllegalStateException if the previous step was a call to
 | 
						|
    // encode with true as the last argument.  It does not require
 | 
						|
    // that the call succeeded.  encode() does require that it succeeded.
 | 
						|
    // XXX: test this to see if reality matches javadoc
 | 
						|
    if (state != STATE_RESET && state != STATE_END)
 | 
						|
      throw new IllegalStateException ();
 | 
						|
 | 
						|
    state = STATE_FLUSHED;
 | 
						|
    return implFlush (out);
 | 
						|
  }
 | 
						|
 | 
						|
  protected CoderResult implFlush (ByteBuffer out)
 | 
						|
  {
 | 
						|
    return CoderResult.UNDERFLOW;
 | 
						|
  }
 | 
						|
 | 
						|
  protected void implOnMalformedInput (CodingErrorAction newAction)
 | 
						|
  {
 | 
						|
    // default implementation does nothing
 | 
						|
  }
 | 
						|
 | 
						|
  protected void implOnUnmappableCharacter (CodingErrorAction newAction)
 | 
						|
  {
 | 
						|
    // default implementation does nothing
 | 
						|
  }
 | 
						|
 | 
						|
  protected void implReplaceWith (byte[] newReplacement)
 | 
						|
  {
 | 
						|
    // default implementation does nothing
 | 
						|
  }
 | 
						|
 | 
						|
  protected void implReset ()
 | 
						|
  {
 | 
						|
    // default implementation does nothing
 | 
						|
  }
 | 
						|
 | 
						|
  public boolean isLegalReplacement (byte[] replacement)
 | 
						|
  {
 | 
						|
    // TODO: cache the decoder
 | 
						|
    // error actions will be REPORT after construction
 | 
						|
    CharsetDecoder decoder = charset.newDecoder ();
 | 
						|
    ByteBuffer bb = ByteBuffer.wrap (replacement);
 | 
						|
    CharBuffer cb
 | 
						|
      = CharBuffer.allocate ((int) (replacement.length
 | 
						|
                                    * decoder.maxCharsPerByte ()));
 | 
						|
    return !decoder.decode (bb, cb, true).isError ();
 | 
						|
  }
 | 
						|
 | 
						|
  public CodingErrorAction malformedInputAction ()
 | 
						|
  {
 | 
						|
    return malformedInputAction;
 | 
						|
  }
 | 
						|
 | 
						|
  public final float maxBytesPerChar ()
 | 
						|
  {
 | 
						|
    return maxBytesPerChar;
 | 
						|
  }
 | 
						|
 | 
						|
  public final CharsetEncoder onMalformedInput (CodingErrorAction newAction)
 | 
						|
  {
 | 
						|
    if (newAction == null)
 | 
						|
      throw new IllegalArgumentException ("Null action");
 | 
						|
 | 
						|
    malformedInputAction = newAction;
 | 
						|
    implOnMalformedInput (newAction);
 | 
						|
    return this;
 | 
						|
  }
 | 
						|
 | 
						|
  public CodingErrorAction unmappableCharacterAction ()
 | 
						|
  {
 | 
						|
    return unmappableCharacterAction;
 | 
						|
  }
 | 
						|
 | 
						|
  public final CharsetEncoder onUnmappableCharacter
 | 
						|
    (CodingErrorAction newAction)
 | 
						|
  {
 | 
						|
    if (newAction == null)
 | 
						|
      throw new IllegalArgumentException ("Null action");
 | 
						|
 | 
						|
    unmappableCharacterAction = newAction;
 | 
						|
    implOnUnmappableCharacter (newAction);
 | 
						|
    return this;
 | 
						|
  }
 | 
						|
 | 
						|
  public final byte[] replacement ()
 | 
						|
  {
 | 
						|
    return replacement;
 | 
						|
  }
 | 
						|
 | 
						|
  public final CharsetEncoder replaceWith (byte[] newReplacement)
 | 
						|
  {
 | 
						|
    if (newReplacement == null)
 | 
						|
      throw new IllegalArgumentException ("Null replacement");
 | 
						|
    if (newReplacement.length == 0)
 | 
						|
      throw new IllegalArgumentException ("Empty replacement");
 | 
						|
    // XXX: what about maxBytesPerChar?
 | 
						|
 | 
						|
      if (!isLegalReplacement (newReplacement))
 | 
						|
        throw new IllegalArgumentException ("Illegal replacement");
 | 
						|
 | 
						|
    this.replacement = newReplacement;
 | 
						|
    implReplaceWith (newReplacement);
 | 
						|
    return this;
 | 
						|
  }
 | 
						|
 | 
						|
  public final CharsetEncoder reset ()
 | 
						|
  {
 | 
						|
    state = STATE_RESET;
 | 
						|
    implReset ();
 | 
						|
    return this;
 | 
						|
  }
 | 
						|
}
 |