mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			318 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			Java
		
	
	
	
			
		
		
	
	
			318 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			Java
		
	
	
	
| /* CharsetDecoder.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 CharsetDecoder
 | |
| {
 | |
|   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 String DEFAULT_REPLACEMENT = "\uFFFD";
 | |
| 
 | |
|   private final Charset charset;
 | |
|   private final float averageCharsPerByte;
 | |
|   private final float maxCharsPerByte;
 | |
|   private String replacement;
 | |
| 
 | |
|   private int state = STATE_RESET;
 | |
| 
 | |
|   private CodingErrorAction malformedInputAction
 | |
|     = CodingErrorAction.REPORT;
 | |
|   private CodingErrorAction unmappableCharacterAction
 | |
|     = CodingErrorAction.REPORT;
 | |
| 
 | |
|   private CharsetDecoder (Charset cs, float averageCharsPerByte,
 | |
|                           float maxCharsPerByte, String replacement)
 | |
|   {
 | |
|     if (averageCharsPerByte <= 0.0f)
 | |
|       throw new IllegalArgumentException ("Non-positive averageCharsPerByte");
 | |
|     if (maxCharsPerByte <= 0.0f)
 | |
|       throw new IllegalArgumentException ("Non-positive maxCharsPerByte");
 | |
| 
 | |
|     this.charset = cs;
 | |
|     this.averageCharsPerByte
 | |
|       = averageCharsPerByte;
 | |
|     this.maxCharsPerByte
 | |
|       = maxCharsPerByte;
 | |
|     this.replacement = replacement;
 | |
|     implReplaceWith (replacement);
 | |
|   }
 | |
| 
 | |
|   protected CharsetDecoder (Charset cs, float averageCharsPerByte,
 | |
|                             float maxCharsPerByte)
 | |
|   {
 | |
|     this (cs, averageCharsPerByte, maxCharsPerByte, DEFAULT_REPLACEMENT);
 | |
|   }
 | |
| 
 | |
|   public final float averageCharsPerByte ()
 | |
|   {
 | |
|     return averageCharsPerByte;
 | |
|   }
 | |
| 
 | |
|   public final Charset charset ()
 | |
|   {
 | |
|     return charset;
 | |
|   }
 | |
| 
 | |
|   public final CharBuffer decode (ByteBuffer 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 Decoder".
 | |
|     // 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 * maxCharsPerByte ());
 | |
|     CharBuffer out = CharBuffer.allocate (n);
 | |
| 
 | |
|     if (remaining == 0)
 | |
|       {
 | |
|         state = STATE_FLUSHED;
 | |
|         return out;
 | |
|       }
 | |
| 
 | |
|     CoderResult cr = decode (in, out, true);
 | |
|     if (cr.isError ())
 | |
|       cr.throwException ();
 | |
| 
 | |
|     cr = flush (out);
 | |
|     if (cr.isError ())
 | |
|       cr.throwException ();
 | |
| 
 | |
|     reset();
 | |
|     out.flip ();
 | |
| 
 | |
|     // Unfortunately, resizing the actual charbuffer array is required.
 | |
|     char[] resized = new char[out.remaining()];
 | |
|     out.get(resized);
 | |
|     return CharBuffer.wrap(resized);
 | |
|   }
 | |
| 
 | |
|   public final CoderResult decode (ByteBuffer in, CharBuffer 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 = decodeLoop (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 decodeLoop (ByteBuffer in, CharBuffer out);
 | |
| 
 | |
|   public Charset detectedCharset ()
 | |
|   {
 | |
|     throw new UnsupportedOperationException ();
 | |
|   }
 | |
| 
 | |
|   public final CoderResult flush (CharBuffer 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 decode 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
 | |
|     // decode with true as the last argument.  It does not require
 | |
|     // that the call succeeded.  decode() 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 (CharBuffer out)
 | |
|   {
 | |
|     return CoderResult.UNDERFLOW;
 | |
|   }
 | |
| 
 | |
|   public final CharsetDecoder onMalformedInput (CodingErrorAction newAction)
 | |
|   {
 | |
|     if (newAction == null)
 | |
|       throw new IllegalArgumentException ("Null action");
 | |
| 
 | |
|     malformedInputAction = newAction;
 | |
|     implOnMalformedInput (newAction);
 | |
|     return this;
 | |
|   }
 | |
| 
 | |
|   protected void implOnMalformedInput (CodingErrorAction newAction)
 | |
|   {
 | |
|     // default implementation does nothing
 | |
|   }
 | |
| 
 | |
|   protected void implOnUnmappableCharacter (CodingErrorAction newAction)
 | |
|   {
 | |
|     // default implementation does nothing
 | |
|   }
 | |
| 
 | |
|   protected void implReplaceWith (String newReplacement)
 | |
|   {
 | |
|     // default implementation does nothing
 | |
|   }
 | |
| 
 | |
|   protected void implReset ()
 | |
|   {
 | |
|     // default implementation does nothing
 | |
|   }
 | |
| 
 | |
|   public boolean isAutoDetecting ()
 | |
|   {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   public boolean isCharsetDetected ()
 | |
|   {
 | |
|     throw new UnsupportedOperationException ();
 | |
|   }
 | |
| 
 | |
|   public CodingErrorAction malformedInputAction ()
 | |
|   {
 | |
|     return malformedInputAction;
 | |
|   }
 | |
| 
 | |
|   public final float maxCharsPerByte ()
 | |
|   {
 | |
|     return maxCharsPerByte;
 | |
|   }
 | |
| 
 | |
|   public final CharsetDecoder onUnmappableCharacter
 | |
|     (CodingErrorAction newAction)
 | |
|   {
 | |
|     if (newAction == null)
 | |
|       throw new IllegalArgumentException ("Null action");
 | |
| 
 | |
|     unmappableCharacterAction = newAction;
 | |
|     implOnUnmappableCharacter (newAction);
 | |
|     return this;
 | |
|   }
 | |
| 
 | |
|   public final String replacement ()
 | |
|   {
 | |
|     return replacement;
 | |
|   }
 | |
| 
 | |
|   public final CharsetDecoder replaceWith (String newReplacement)
 | |
|   {
 | |
|     if (newReplacement == null)
 | |
|       throw new IllegalArgumentException ("Null replacement");
 | |
|     if (newReplacement.length () == 0)
 | |
|       throw new IllegalArgumentException ("Empty replacement");
 | |
|     // XXX: what about maxCharsPerByte?
 | |
| 
 | |
|     this.replacement = newReplacement;
 | |
|     implReplaceWith (newReplacement);
 | |
|     return this;
 | |
|   }
 | |
| 
 | |
|   public final CharsetDecoder reset ()
 | |
|   {
 | |
|     state = STATE_RESET;
 | |
|     implReset ();
 | |
|     return this;
 | |
|   }
 | |
| 
 | |
|   public CodingErrorAction unmappableCharacterAction ()
 | |
|   {
 | |
|     return unmappableCharacterAction;
 | |
|   }
 | |
| }
 |