aboutsummaryrefslogtreecommitdiff
path: root/libjava/java/nio/charset/CharsetEncoder.java
diff options
context:
space:
mode:
Diffstat (limited to 'libjava/java/nio/charset/CharsetEncoder.java')
-rw-r--r--libjava/java/nio/charset/CharsetEncoder.java360
1 files changed, 360 insertions, 0 deletions
diff --git a/libjava/java/nio/charset/CharsetEncoder.java b/libjava/java/nio/charset/CharsetEncoder.java
new file mode 100644
index 00000000000..dd434a79752
--- /dev/null
+++ b/libjava/java/nio/charset/CharsetEncoder.java
@@ -0,0 +1,360 @@
+/* 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., 59 Temple Place, Suite 330, Boston, MA
+02111-1307 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 ();
+ return out;
+ }
+
+ 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 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;
+ }
+}