| /* Copyright (C) 1999, 2000, 2001, 2003 Free Software Foundation |
| |
| This file is part of libgcj. |
| |
| This software is copyrighted work licensed under the terms of the |
| Libgcj License. Please consult the file "LIBGCJ_LICENSE" for |
| details. */ |
| |
| package gnu.gcj.convert; |
| |
| public abstract class UnicodeToBytes extends IOConverter |
| { |
| /** Buffer to emit bytes to. |
| * The locations buf[count] ... buf[buf.length-1] are available. */ |
| public byte[] buf; |
| public int count; |
| |
| // The name of the default encoding. |
| static String defaultEncoding; |
| |
| /* These keep a small cache of encoders for reuse. The array holds |
| the actual encoders. The currCachePos is the next value we are |
| going to replace in the cache. We don't just throw the data away |
| if the cache is full, because if the cache filled up with stuff we |
| don't need then the cache would be worthless. We instead |
| circulate through the cache the implement kind of an LRU |
| algorithm. */ |
| private static final int CACHE_SIZE = 4; // A power of 2 for speed |
| private static UnicodeToBytes[] encoderCache |
| = new UnicodeToBytes[CACHE_SIZE]; |
| private static int currCachePos = 0; |
| |
| public abstract String getName(); |
| |
| public static UnicodeToBytes getDefaultEncoder() |
| { |
| try |
| { |
| synchronized (UnicodeToBytes.class) |
| { |
| if (defaultEncoding == null) |
| { |
| String encoding |
| = canonicalize (System.getProperty("file.encoding", |
| "8859_1")); |
| String className = "gnu.gcj.convert.Output_" + encoding; |
| try |
| { |
| Class defaultEncodingClass = Class.forName(className); |
| defaultEncoding = encoding; |
| } |
| catch (ClassNotFoundException ex) |
| { |
| throw new NoClassDefFoundError("missing default encoding " |
| + encoding + " (class " |
| + className |
| + " not found)"); |
| } |
| } |
| } |
| |
| return getEncoder (defaultEncoding); |
| } |
| catch (Throwable ex) |
| { |
| return new Output_8859_1(); |
| } |
| } |
| |
| /** Get a char-stream->byte-stream converter given an encoding name. */ |
| public static UnicodeToBytes getEncoder (String encoding) |
| throws java.io.UnsupportedEncodingException |
| { |
| /* First hunt in our cache to see if we have a encoder that is |
| already allocated. */ |
| String canonicalEncoding = canonicalize(encoding); |
| synchronized (UnicodeToBytes.class) |
| { |
| int i; |
| for (i = 0; i < encoderCache.length; ++i) |
| { |
| if (encoderCache[i] != null |
| && canonicalEncoding.equals(encoderCache[i].getName ())) |
| { |
| UnicodeToBytes rv = encoderCache[i]; |
| encoderCache[i] = null; |
| return rv; |
| } |
| } |
| } |
| |
| String className = "gnu.gcj.convert.Output_" + canonicalEncoding; |
| Class encodingClass; |
| try |
| { |
| encodingClass = Class.forName(className); |
| return (UnicodeToBytes) encodingClass.newInstance(); |
| } |
| catch (Throwable ex) |
| { |
| try |
| { |
| // We pass the original name to iconv and let it handle |
| // its own aliasing. |
| return new Output_iconv (encoding); |
| } |
| catch (Throwable _) |
| { |
| // Put the original exception in the throwable. |
| throw new java.io.UnsupportedEncodingException(encoding + " (" |
| + ex + ')'); |
| } |
| } |
| } |
| |
| public final void setOutput(byte[] buffer, int count) |
| { |
| this.buf = buffer; |
| this.count = count; |
| } |
| |
| /** Convert chars to bytes. |
| * Converted bytes are written to buf, starting at count. |
| * @param inbuffer source of characters to convert |
| * @param inpos index of initial character in inbuffer to convert |
| * @param inlength number of characters to convert |
| * @return number of chars converted |
| * Also, this.count is increment by the number of bytes converted. |
| */ |
| public abstract int write (char[] inbuffer, int inpos, int inlength); |
| |
| /** Convert chars to bytes. |
| * Converted bytes are written to buf, starting at count. |
| * @param str source of characters to convert |
| * @param inpos index of initial character in str to convert |
| * @param inlength number of characters to convert |
| * @param work if non-null, a buffer than can be used |
| * @return number of chars converted |
| * Also, this.count is increment by the number of bytes converted. |
| */ |
| public int write (String str, int inpos, int inlength, char[] work) |
| { |
| if (work == null) |
| work = new char[inlength]; |
| int srcEnd = inpos + (inlength > work.length ? work.length : inlength); |
| str.getChars(inpos, srcEnd, work, 0); |
| return write(work, 0, srcEnd - inpos); |
| } |
| |
| /** |
| * Returns true when the converter has consumed some bytes that are |
| * not yet converted to characters because further continuation |
| * bytes are needed. Defaults to false, should be overridden by |
| * decoders that internally store some bytes. |
| */ |
| public boolean havePendingBytes() |
| { |
| return false; |
| } |
| |
| /** Indicate that the converter is resuable. |
| * This class keeps track of converters on a per-encoding basis. |
| * When done with an encoder you may call this method to indicate |
| * that it can be reused later. |
| */ |
| public void done () |
| { |
| synchronized (UnicodeToBytes.class) |
| { |
| this.buf = null; |
| this.count = 0; |
| |
| encoderCache[currCachePos] = this; |
| currCachePos = (currCachePos + 1) % CACHE_SIZE; |
| } |
| } |
| } |