1 | /* Copyright (C) 1999, 2000, 2001 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 BytesToUnicode extends IOConverter
|
---|
12 | {
|
---|
13 | /** Buffer to read bytes from.
|
---|
14 | * The characters inbuffer[inpos] ... inbuffer[inlength-1] are available. */
|
---|
15 | public byte[] inbuffer;
|
---|
16 | /** Starting index in buffer to read bytes from. */
|
---|
17 | public int inpos;
|
---|
18 | /** End of valid bytes in buffer. */
|
---|
19 | public int inlength;
|
---|
20 |
|
---|
21 | // The name of the default encoding.
|
---|
22 | static String defaultEncoding;
|
---|
23 |
|
---|
24 | /* These keep a small cache of decoders for reuse. The array holds
|
---|
25 | the actual decoders. The currCachePos is the next value we are
|
---|
26 | going to replace in the cache. We don't just throw the data away
|
---|
27 | if the cache is full, because if the cache filled up with stuff
|
---|
28 | we don't need then the cache would be worthless. We instead
|
---|
29 | circulate through the cache the implement kind of an LRU
|
---|
30 | algorithm. */
|
---|
31 | private static final int CACHE_SIZE = 4; // A power of 2 for speed
|
---|
32 | private static BytesToUnicode[] decoderCache
|
---|
33 | = new BytesToUnicode[CACHE_SIZE];
|
---|
34 | private static int currCachePos = 0;
|
---|
35 |
|
---|
36 | public abstract String getName();
|
---|
37 |
|
---|
38 | public static BytesToUnicode getDefaultDecoder()
|
---|
39 | {
|
---|
40 | try
|
---|
41 | {
|
---|
42 | synchronized (BytesToUnicode.class)
|
---|
43 | {
|
---|
44 | if (defaultEncoding == null)
|
---|
45 | {
|
---|
46 | String encoding
|
---|
47 | = canonicalize (System.getProperty("file.encoding",
|
---|
48 | "8859_1"));
|
---|
49 | String className = "gnu.gcj.convert.Input_" + encoding;
|
---|
50 | try
|
---|
51 | {
|
---|
52 | Class defaultDecodingClass = Class.forName(className);
|
---|
53 | defaultEncoding = encoding;
|
---|
54 | }
|
---|
55 | catch (ClassNotFoundException ex)
|
---|
56 | {
|
---|
57 | throw new NoClassDefFoundError("missing default encoding "
|
---|
58 | + encoding + " (class "
|
---|
59 | + className
|
---|
60 | + " not found)");
|
---|
61 | }
|
---|
62 | }
|
---|
63 | }
|
---|
64 | return getDecoder (defaultEncoding);
|
---|
65 | }
|
---|
66 | catch (Throwable ex)
|
---|
67 | {
|
---|
68 | return new Input_8859_1();
|
---|
69 | }
|
---|
70 | }
|
---|
71 |
|
---|
72 | /** Get a byte-stream->char-stream converter given an encoding name. */
|
---|
73 | public static BytesToUnicode getDecoder (String encoding)
|
---|
74 | throws java.io.UnsupportedEncodingException
|
---|
75 | {
|
---|
76 | /* First hunt in our cache to see if we have a decoder that is
|
---|
77 | already allocated. */
|
---|
78 | synchronized (BytesToUnicode.class)
|
---|
79 | {
|
---|
80 | int i;
|
---|
81 | for (i = 0; i < decoderCache.length; ++i)
|
---|
82 | {
|
---|
83 | if (decoderCache[i] != null
|
---|
84 | && encoding.equals(decoderCache[i].getName ()))
|
---|
85 | {
|
---|
86 | BytesToUnicode rv = decoderCache[i];
|
---|
87 | decoderCache[i] = null;
|
---|
88 | return rv;
|
---|
89 | }
|
---|
90 | }
|
---|
91 | }
|
---|
92 |
|
---|
93 | // It's not in the cache, so now we have to do real work.
|
---|
94 | String className = "gnu.gcj.convert.Input_" + canonicalize (encoding);
|
---|
95 | Class decodingClass;
|
---|
96 | try
|
---|
97 | {
|
---|
98 | decodingClass = Class.forName(className);
|
---|
99 | return (BytesToUnicode) decodingClass.newInstance();
|
---|
100 | }
|
---|
101 | catch (Throwable ex)
|
---|
102 | {
|
---|
103 | try
|
---|
104 | {
|
---|
105 | // We pass the original name to iconv and let it handle
|
---|
106 | // its own aliasing.
|
---|
107 | return new Input_iconv (encoding);
|
---|
108 | }
|
---|
109 | catch (Throwable _)
|
---|
110 | {
|
---|
111 | throw new java.io.UnsupportedEncodingException(encoding
|
---|
112 | + " (" + ex + ')');
|
---|
113 | }
|
---|
114 | }
|
---|
115 | }
|
---|
116 |
|
---|
117 | /** Make input bytes available to the conversion.
|
---|
118 | * @param buffer source of input bytes
|
---|
119 | * @param pos index of first available byte
|
---|
120 | * @param length one more than index of last available byte
|
---|
121 | */
|
---|
122 | public final void setInput(byte[] buffer, int pos, int length)
|
---|
123 | {
|
---|
124 | inbuffer = buffer;
|
---|
125 | inpos = pos;
|
---|
126 | inlength = length;
|
---|
127 | }
|
---|
128 |
|
---|
129 | /** Convert bytes to chars.
|
---|
130 | * Input bytes are taken from this.inbuffer. The available input
|
---|
131 | * bytes start at inbuffer[inpos], and end at inbuffer[inlength-1].
|
---|
132 | * @param outbuffer buffer for the converted character
|
---|
133 | * @param outpos position in buffer to start putting converted characters
|
---|
134 | * @param count the maximum number of characters to convert
|
---|
135 | * @return number of chars placed in outbuffer.
|
---|
136 | * Also, this.inpos is incremented by the number of bytes consumed.
|
---|
137 | *
|
---|
138 | * (Note the asymmetry in that the input upper bound is inbuffer[inlength-1],
|
---|
139 | * while the output upper bound is outbuffer[outpos+count-1]. The
|
---|
140 | * justification is that inlength is like the count field of a
|
---|
141 | * BufferedInputStream, while the count parameter is like the
|
---|
142 | * length parameter of a read request.) The count parameter is
|
---|
143 | * also defined to be <= outbuffer.length - outpos (per the specification
|
---|
144 | * of the length parameter for a read request).
|
---|
145 | */
|
---|
146 | public abstract int read (char[] outbuffer, int outpos, int count);
|
---|
147 |
|
---|
148 | /** Indicate that the converter is resuable.
|
---|
149 | * This class keeps track of converters on a per-encoding basis.
|
---|
150 | * When done with an encoder you may call this method to indicate
|
---|
151 | * that it can be reused later.
|
---|
152 | */
|
---|
153 | public void done ()
|
---|
154 | {
|
---|
155 | synchronized (BytesToUnicode.class)
|
---|
156 | {
|
---|
157 | this.inbuffer = null;
|
---|
158 | this.inpos = 0;
|
---|
159 | this.inlength = 0;
|
---|
160 |
|
---|
161 | decoderCache[currCachePos] = this;
|
---|
162 | currCachePos = (currCachePos + 1) % CACHE_SIZE;
|
---|
163 | }
|
---|
164 | }
|
---|
165 | }
|
---|