/*
* Copyright (c) 1996, 2004, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.io;
import java.io.*;
/**
* An abstract base class for subclasses which convert Unicode
* characters into an external encoding.
*
* @author Asmus Freytag
* @author Lloyd Honomichl, Novell, Inc.
*
* @deprecated Replaced by {@link java.nio.charset}. THIS API WILL BE
* REMOVED IN J2SE 1.6.
*/
@Deprecated
public abstract class CharToByteConverter {
/**
* Substitution mode flag.
*/
protected boolean subMode = true;
/**
* Bytes to substitute for unmappable input.
*/
protected byte[] subBytes = { (byte)'?' };
/**
* Offset of next character to be converted.
*/
protected int charOff;
/**
* Offset of next byte to be output.
*/
protected int byteOff;
/**
* Length of bad input that caused conversion to stop.
*/
protected int badInputLength;
/**
* Create an instance of the default CharToByteConverter subclass.
*/
public static CharToByteConverter getDefault() {
Object cvt;
cvt = Converters.newDefaultConverter(Converters.CHAR_TO_BYTE);
return (CharToByteConverter)cvt;
}
/**
* Returns appropriate CharToByteConverter subclass instance.
* @param string represets encoding
*/
public static CharToByteConverter getConverter(String encoding)
throws UnsupportedEncodingException
{
Object cvt;
cvt = Converters.newConverter(Converters.CHAR_TO_BYTE, encoding);
return (CharToByteConverter)cvt;
}
/**
* Returns the character set id for the conversion.
*/
public abstract String getCharacterEncoding();
/**
* Converts an array of Unicode characters into an array of bytes
* in the target character encoding. This method allows a buffer by
* buffer conversion of a data stream. The state of the conversion is
* saved between calls to convert. If a call to convert results in
* an exception, the conversion may be continued by calling convert again
* with suitably modified parameters. All conversions should be finished
* with a call to the flush method.
*
* @return the number of bytes written to output.
* @param input array containing Unicode characters to be converted.
* @param inStart begin conversion at this offset in input array.
* @param inEnd stop conversion at this offset in input array (exclusive).
* @param output byte array to receive conversion result.
* @param outStart start writing to output array at this offset.
* @param outEnd stop writing to output array at this offset (exclusive).
* @exception MalformedInputException if the input buffer contains any
* sequence of chars that is illegal in Unicode (principally unpaired
* surrogates and \uFFFF or \uFFFE). After this exception is thrown,
* the method nextCharIndex can be called to obtain the index of the
* first invalid input character. The MalformedInputException can
* be queried for the length of the invalid input.
* @exception UnknownCharacterException for any character that
* that cannot be converted to the external character encoding. Thrown
* only when converter is not in substitution mode.
* @exception ConversionBufferFullException if output array is filled prior
* to converting all the input.
*/
public abstract int convert(char[] input, int inStart, int inEnd,
byte[] output, int outStart, int outEnd)
throws MalformedInputException,
UnknownCharacterException,
ConversionBufferFullException;
/*
* Converts any array of characters, including malformed surrogate
* pairs, into an array of bytes in the target character encoding.
* A precondition is that substitution mode is turned on. This method
* allows a buffer by buffer conversion of a data stream.
* The state of the conversion is saved between calls to convert.
* All conversions should be finished with a call to the flushAny method.
*
* @return the number of bytes written to output.
* @param input array containing Unicode characters to be converted.
* @param inStart begin conversion at this offset in input array.
* @param inEnd stop conversion at this offset in input array (exclusive).
* @param output byte array to receive conversion result.
* @param outStart start writing to output array at this offset.
* @param outEnd stop writing to output array at this offset (exclusive).
* @exception ConversionBufferFullException if output array is filled prior
* to converting all the input.
*/
public int convertAny(char[] input, int inStart, int inEnd,
byte[] output, int outStart, int outEnd)
throws ConversionBufferFullException
{
if (!subMode) { /* Precondition: subMode == true */
throw new IllegalStateException("Substitution mode is not on");
}
/* Rely on the untested precondition that the indices are meaningful */
/* For safety, use the public interface to charOff and byteOff, but
badInputLength is directly modified.*/
int localInOff = inStart;
int localOutOff = outStart;
while(localInOff < inEnd) {
try {
int discard = convert(input, localInOff, inEnd,
output, localOutOff, outEnd);
return (nextByteIndex() - outStart);
} catch (MalformedInputException e) {
byte[] s = subBytes;
int subSize = s.length;
localOutOff = nextByteIndex();
if ((localOutOff + subSize) > outEnd)
throw new ConversionBufferFullException();
for (int i = 0; i < subSize; i++)
output[localOutOff++] = s[i];
localInOff = nextCharIndex();
localInOff += badInputLength;
badInputLength = 0;
if (localInOff >= inEnd){
byteOff = localOutOff;
return (byteOff - outStart);
}
continue;
}catch (UnknownCharacterException e) {
/* Should never occur, since subMode == true */
throw new Error("UnknownCharacterException thrown "
+ "in substititution mode",
e);
}
}
return (nextByteIndex() - outStart);
}
/**
* Converts an array of Unicode characters into an array of bytes
* in the target character encoding. Unlike convert, this method
* does not do incremental conversion. It assumes that the given
* input array contains all the characters to be converted. The
* state of the converter is reset at the beginning of this method
* and is left in the reset state on successful termination.
* The converter is not reset if an exception is thrown.
* This allows the caller to determine where the bad input
* was encountered by calling nextCharIndex.
* <p>
* This method uses substitution mode when performing the conversion.
* The method setSubstitutionBytes may be used to determine what
* bytes are substituted. Even though substitution mode is used,
* the state of the converter's substitution mode is not changed
* at the end of this method.
*
* @return an array of bytes containing the converted characters.
* @param input array containing Unicode characters to be converted.
* @exception MalformedInputException if the input buffer contains any
* sequence of chars that is illegal in Unicode (principally unpaired
* surrogates and \uFFFF or \uFFFE). After this exception is thrown,
* the method nextCharIndex can be called to obtain the index of the
* first invalid input character and getBadInputLength can be called
* to determine the length of the invalid input.
*
* @see #nextCharIndex
* @see #setSubstitutionMode
* @see #setSubstitutionBytes
* @see #getBadInputLength
*/
public byte[] convertAll( char input[] ) throws MalformedInputException {
reset();
boolean savedSubMode = subMode;
subMode = true;
byte[] output = new byte[ getMaxBytesPerChar() * input.length ];
try {
int outputLength = convert( input, 0, input.length,
output, 0, output.length );
outputLength += flush( output, nextByteIndex(), output.length );
byte [] returnedOutput = new byte[ outputLength ];
System.arraycopy( output, 0, returnedOutput, 0, outputLength );
return returnedOutput;
}
catch( ConversionBufferFullException e ) {
//Not supposed to happen. If it does, getMaxBytesPerChar() lied.
throw new
InternalError("this.getMaxBytesPerChar returned bad value");
}
catch( UnknownCharacterException e ) {
// Not supposed to happen since we're in substitution mode.
throw new InternalError();
}
finally {
subMode = savedSubMode;
}
}
/**
* Writes any remaining output to the output buffer and resets the
* converter to its initial state.
*
* @param output byte array to receive flushed output.
* @param outStart start writing to output array at this offset.
* @param outEnd stop writing to output array at this offset (exclusive).
* @exception MalformedInputException if the output to be flushed contained
* a partial or invalid multibyte character sequence. Will occur if the
* input buffer on the last call to convert ended with the first character
* of a surrogate pair. flush will write what it can to the output buffer
* and reset the converter before throwing this exception. An additional
* call to flush is not required.
* @exception ConversionBufferFullException if output array is filled
* before all the output can be flushed. flush will write what it can
* to the output buffer and remember its state. An additional call to
* flush with a new output buffer will conclude the operation.
*/
public abstract int flush( byte[] output, int outStart, int outEnd )
throws MalformedInputException, ConversionBufferFullException;
/**
* Writes any remaining output to the output buffer and resets the
* converter to its initial state. May only be called when substitution
* mode is turned on, and never complains about malformed input (always
* substitutes).
*
* @param output byte array to receive flushed output.
* @param outStart start writing to output array at this offset.
* @param outEnd stop writing to output array at this offset (exclusive).
* @return number of bytes writter into output.
* @exception ConversionBufferFullException if output array is filled
* before all the output can be flushed. flush will write what it can
* to the output buffer and remember its state. An additional call to
* flush with a new output buffer will conclude the operation.
*/
public int flushAny( byte[] output, int outStart, int outEnd )
throws ConversionBufferFullException
{
if (!subMode) { /* Precondition: subMode == true */
throw new IllegalStateException("Substitution mode is not on");
}
try {
return flush(output, outStart, outEnd);
} catch (MalformedInputException e) {
/* Assume that if a malformed input exception has occurred,
no useful data has been placed in the output buffer.
i.e. there is no mixture of left over good + some bad data.
Usually occurs with a trailing high surrogate pair element.
Special cases occur in Cp970, 949c and 933 that seem
to be covered, but may require further investigation */
int subSize = subBytes.length;
byte[] s = subBytes;
int outIndex = outStart;
if ((outStart + subSize) > outEnd)
throw new ConversionBufferFullException();
for (int i = 0; i < subSize; i++)
output[outIndex++] = s[i];
byteOff = charOff = 0; // Reset the internal state.
badInputLength = 0;
return subSize;
}
}
/**
* Resets converter to its initial state.
*/
public abstract void reset();
/**
* Returns true if the given character can be converted to the
* target character encoding.
* @return true if given character is translatable, false otherwise.
* @param c character to test
*/
public boolean canConvert(char c) {
try {
//FIXME output buffer size should use getMaxBytesPerChar value.
char[] input = new char[1];
byte[] output = new byte[3];
input[0] = c;
convert(input, 0, 1, output, 0, 3);
return true;
} catch(CharConversionException e){
return false;
}
}
/**
* Returns the maximum number of bytes needed to convert a char. Useful
* for calculating the maximum output buffer size needed for a particular
* input buffer.
*/
public abstract int getMaxBytesPerChar();
/**
* Returns the length, in chars, of the input which caused a
* MalformedInputException. Always refers to the last
* MalformedInputException thrown by the converter. If none have
* ever been thrown, returns 0.
*/
public int getBadInputLength() {
return badInputLength;
}
/**
* Returns the index of the character just past
* the last character successfully converted by the previous call
* to convert.
*/
public int nextCharIndex() {
return charOff;
}
/**
* Returns the index of the byte just past the last byte written by
* the previous call to convert.
*/
public int nextByteIndex() {
return byteOff;
}
/**
* Sets converter into substitution mode. In substitution mode,
* the converter will replace untranslatable characters in the source
* encoding with the substitution character set by setSubstitutionBytes.
* When not in substitution mode, the converter will throw an
* UnknownCharacterException when it encounters untranslatable input.
*
* @param doSub if true, enable substitution mode.
* @see #setSubstitutionBytes
*/
public void setSubstitutionMode(boolean doSub) {
subMode = doSub;
}
/**
* Sets the substitution bytes to use when the converter is in
* substitution mode. The given bytes should represent a valid
* character in the target character encoding and must not be
* longer than the value returned by getMaxBytesPerChar for this
* converter.
*
* @param newSubBytes the substitution bytes
* @exception IllegalArgumentException if given byte array is longer than
* the value returned by the method getMaxBytesPerChar.
* @see #setSubstitutionMode
* @see #getMaxBytesPerChar
*/
public void setSubstitutionBytes( byte[] newSubBytes )
throws IllegalArgumentException
{
if( newSubBytes.length > getMaxBytesPerChar() ) {
throw new IllegalArgumentException();
}
subBytes = new byte[ newSubBytes.length ];
System.arraycopy( newSubBytes, 0, subBytes, 0, newSubBytes.length );
}
/**
* Returns a string representation of the class.
*/
public String toString() {
return "CharToByteConverter: " + getCharacterEncoding();
}
}