1 | /* Copyright (C) 1999, 2000, 2001, 2003 Free Software Foundation
|
---|
2 |
|
---|
3 | This file is part of libgcj.
|
---|
4 |
|
---|
5 | This software is copyrighted work licensed under the terms of the
|
---|
6 | Libgcj License. Please consult the file "LIBGCJ_LICENSE" for
|
---|
7 | details. */
|
---|
8 |
|
---|
9 | package gnu.gcj.convert;
|
---|
10 |
|
---|
11 | public abstract class UnicodeToBytes extends IOConverter
|
---|
12 | {
|
---|
13 | /** Buffer to emit bytes to.
|
---|
14 | * The locations buf[count] ... buf[buf.length-1] are available. */
|
---|
15 | public byte[] buf;
|
---|
16 | public int count;
|
---|
17 |
|
---|
18 | // The name of the default encoding.
|
---|
19 | static String defaultEncoding;
|
---|
20 |
|
---|
21 | /* These keep a small cache of encoders for reuse. The array holds
|
---|
22 | the actual encoders. The currCachePos is the next value we are
|
---|
23 | going to replace in the cache. We don't just throw the data away
|
---|
24 | if the cache is full, because if the cache filled up with stuff we
|
---|
25 | don't need then the cache would be worthless. We instead
|
---|
26 | circulate through the cache the implement kind of an LRU
|
---|
27 | algorithm. */
|
---|
28 | private static final int CACHE_SIZE = 4; // A power of 2 for speed
|
---|
29 | private static UnicodeToBytes[] encoderCache
|
---|
30 | = new UnicodeToBytes[CACHE_SIZE];
|
---|
31 | private static int currCachePos = 0;
|
---|
32 |
|
---|
33 | public abstract String getName();
|
---|
34 |
|
---|
35 | public static UnicodeToBytes getDefaultEncoder()
|
---|
36 | {
|
---|
37 | try
|
---|
38 | {
|
---|
39 | synchronized (UnicodeToBytes.class)
|
---|
40 | {
|
---|
41 | if (defaultEncoding == null)
|
---|
42 | {
|
---|
43 | String encoding
|
---|
44 | = canonicalize (System.getProperty("file.encoding",
|
---|
45 | "8859_1"));
|
---|
46 | String className = "gnu.gcj.convert.Output_" + encoding;
|
---|
47 | try
|
---|
48 | {
|
---|
49 | Class defaultEncodingClass = Class.forName(className);
|
---|
50 | defaultEncoding = encoding;
|
---|
51 | }
|
---|
52 | catch (ClassNotFoundException ex)
|
---|
53 | {
|
---|
54 | throw new NoClassDefFoundError("missing default encoding "
|
---|
55 | + encoding + " (class "
|
---|
56 | + className
|
---|
57 | + " not found)");
|
---|
58 | }
|
---|
59 | }
|
---|
60 | }
|
---|
61 |
|
---|
62 | return getEncoder (defaultEncoding);
|
---|
63 | }
|
---|
64 | catch (Throwable ex)
|
---|
65 | {
|
---|
66 | return new Output_8859_1();
|
---|
67 | }
|
---|
68 | }
|
---|
69 |
|
---|
70 | /** Get a char-stream->byte-stream converter given an encoding name. */
|
---|
71 | public static UnicodeToBytes getEncoder (String encoding)
|
---|
72 | throws java.io.UnsupportedEncodingException
|
---|
73 | {
|
---|
74 | /* First hunt in our cache to see if we have a encoder that is
|
---|
75 | already allocated. */
|
---|
76 | synchronized (UnicodeToBytes.class)
|
---|
77 | {
|
---|
78 | int i;
|
---|
79 | for (i = 0; i < encoderCache.length; ++i)
|
---|
80 | {
|
---|
81 | if (encoderCache[i] != null
|
---|
82 | && encoding.equals(encoderCache[i].getName ()))
|
---|
83 | {
|
---|
84 | UnicodeToBytes rv = encoderCache[i];
|
---|
85 | encoderCache[i] = null;
|
---|
86 | return rv;
|
---|
87 | }
|
---|
88 | }
|
---|
89 | }
|
---|
90 |
|
---|
91 | String className = "gnu.gcj.convert.Output_" + canonicalize (encoding);
|
---|
92 | Class encodingClass;
|
---|
93 | try
|
---|
94 | {
|
---|
95 | encodingClass = Class.forName(className);
|
---|
96 | return (UnicodeToBytes) encodingClass.newInstance();
|
---|
97 | }
|
---|
98 | catch (Throwable ex)
|
---|
99 | {
|
---|
100 | try
|
---|
101 | {
|
---|
102 | // We pass the original name to iconv and let it handle
|
---|
103 | // its own aliasing.
|
---|
104 | return new Output_iconv (encoding);
|
---|
105 | }
|
---|
106 | catch (Throwable _)
|
---|
107 | {
|
---|
108 | // Put the original exception in the throwable.
|
---|
109 | throw new java.io.UnsupportedEncodingException(encoding + " ("
|
---|
110 | + ex + ')');
|
---|
111 | }
|
---|
112 | }
|
---|
113 | }
|
---|
114 |
|
---|
115 | public final void setOutput(byte[] buffer, int count)
|
---|
116 | {
|
---|
117 | this.buf = buffer;
|
---|
118 | this.count = count;
|
---|
119 | }
|
---|
120 |
|
---|
121 | /** Convert chars to bytes.
|
---|
122 | * Converted bytes are written to buf, starting at count.
|
---|
123 | * @param inbuffer source of characters to convert
|
---|
124 | * @param inpos index of initial character in inbuffer to convert
|
---|
125 | * @param inlength number of characters to convert
|
---|
126 | * @return number of chars converted
|
---|
127 | * Also, this.count is increment by the number of bytes converted.
|
---|
128 | */
|
---|
129 | public abstract int write (char[] inbuffer, int inpos, int inlength);
|
---|
130 |
|
---|
131 | /** Convert chars to bytes.
|
---|
132 | * Converted bytes are written to buf, starting at count.
|
---|
133 | * @param str source of characters to convert
|
---|
134 | * @param inpos index of initial character in str to convert
|
---|
135 | * @param inlength number of characters to convert
|
---|
136 | * @param work if non-null, a buffer than can be used
|
---|
137 | * @return number of chars converted
|
---|
138 | * Also, this.count is increment by the number of bytes converted.
|
---|
139 | */
|
---|
140 | public int write (String str, int inpos, int inlength, char[] work)
|
---|
141 | {
|
---|
142 | if (work == null)
|
---|
143 | work = new char[inlength];
|
---|
144 | int srcEnd = inpos + (inlength > work.length ? work.length : inlength);
|
---|
145 | str.getChars(inpos, srcEnd, work, 0);
|
---|
146 | return write(work, 0, srcEnd - inpos);
|
---|
147 | }
|
---|
148 |
|
---|
149 | /**
|
---|
150 | * Returns true when the converter has consumed some bytes that are
|
---|
151 | * not yet converted to characters because further continuation
|
---|
152 | * bytes are needed. Defaults to false, should be overridden by
|
---|
153 | * decoders that internally store some bytes.
|
---|
154 | */
|
---|
155 | public boolean havePendingBytes()
|
---|
156 | {
|
---|
157 | return false;
|
---|
158 | }
|
---|
159 |
|
---|
160 | /** Indicate that the converter is resuable.
|
---|
161 | * This class keeps track of converters on a per-encoding basis.
|
---|
162 | * When done with an encoder you may call this method to indicate
|
---|
163 | * that it can be reused later.
|
---|
164 | */
|
---|
165 | public void done ()
|
---|
166 | {
|
---|
167 | synchronized (UnicodeToBytes.class)
|
---|
168 | {
|
---|
169 | this.buf = null;
|
---|
170 | this.count = 0;
|
---|
171 |
|
---|
172 | encoderCache[currCachePos] = this;
|
---|
173 | currCachePos = (currCachePos + 1) % CACHE_SIZE;
|
---|
174 | }
|
---|
175 | }
|
---|
176 | }
|
---|