KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > go > trove > io > InternedCharToByteBuffer


1 /* ====================================================================
2  * Trove - Copyright (c) 1999-2000 Walt Disney Internet Group
3  * ====================================================================
4  * The Tea Software License, Version 1.1
5  *
6  * Copyright (c) 2000 Walt Disney Internet Group. All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  * 1. Redistributions of source code must retain the above copyright
13  * notice, this list of conditions and the following disclaimer.
14  *
15  * 2. Redistributions in binary form must reproduce the above copyright
16  * notice, this list of conditions and the following disclaimer in
17  * the documentation and/or other materials provided with the
18  * distribution.
19  *
20  * 3. The end-user documentation included with the redistribution,
21  * if any, must include the following acknowledgment:
22  * "This product includes software developed by the
23  * Walt Disney Internet Group (http://opensource.go.com/)."
24  * Alternately, this acknowledgment may appear in the software itself,
25  * if and wherever such third-party acknowledgments normally appear.
26  *
27  * 4. The names "Tea", "TeaServlet", "Kettle", "Trove" and "BeanDoc" must
28  * not be used to endorse or promote products derived from this
29  * software without prior written permission. For written
30  * permission, please contact opensource@dig.com.
31  *
32  * 5. Products derived from this software may not be called "Tea",
33  * "TeaServlet", "Kettle" or "Trove", nor may "Tea", "TeaServlet",
34  * "Kettle", "Trove" or "BeanDoc" appear in their name, without prior
35  * written permission of the Walt Disney Internet Group.
36  *
37  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
38  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
39  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
40  * DISCLAIMED. IN NO EVENT SHALL THE WALT DISNEY INTERNET GROUP OR ITS
41  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
42  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
43  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
44  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
45  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
46  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
47  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
48  * ====================================================================
49  *
50  * For more information about Tea, please see http://opensource.go.com/.
51  */

52
53 package com.go.trove.io;
54
55 import java.io.*;
56 import java.util.*;
57 import com.go.trove.util.IdentityMap;
58
59 /******************************************************************************
60  * A CharToByteBuffer that keeps track of interned strings (mainly string
61  * literals) and statically caches the results of those strings after applying
62  * a byte conversion. This can improve performance if many of the strings being
63  * passed to the append method have been converted before.
64  *
65  * @author Brian S O'Neill
66  * @version
67  * <!--$$Revision:--> 27 <!-- $-->, <!--$$JustDate:--> 7/31/01 <!-- $-->
68  */

69 public class InternedCharToByteBuffer
70     implements CharToByteBuffer, Serializable
71 {
72     private static final int CACHES_PER_ENCODING = 11;
73     private static final int MIN_LENGTH = 4;
74
75     private static final Object JavaDoc MARKER = new Object JavaDoc();
76
77     private static Map cEncodings = new HashMap(7);
78
79     private static Random cLastRandom = new Random();
80
81     /**
82      * Returns several caches for the given encoding. A character in the
83      * string key is used to select the correct cache. By breaking up the
84      * cache in this way, the synchronization required to access the caches
85      * is distributed.
86      */

87     private static Map[] getConvertedCaches(String JavaDoc encoding) {
88         synchronized (cEncodings) {
89             Map[] caches = (Map[])cEncodings.get(encoding);
90             if (caches == null) {
91                 caches = new Map[CACHES_PER_ENCODING];
92                 for (int i=0; i<CACHES_PER_ENCODING; i++) {
93                     caches[i] = Collections.synchronizedMap(new IdentityMap());
94                 }
95                 cEncodings.put(encoding, caches);
96             }
97             return caches;
98         }
99     }
100
101     private static Random getRandom() {
102         synchronized (cLastRandom) {
103             return cLastRandom = new Random(cLastRandom.nextLong());
104         }
105     }
106
107     private CharToByteBuffer mBuffer;
108     private Random mRandom;
109     private transient Map[] mConvertedCaches;
110
111     public InternedCharToByteBuffer(CharToByteBuffer buffer)
112         throws IOException
113     {
114         mBuffer = buffer;
115         mConvertedCaches = getConvertedCaches(buffer.getEncoding());
116     }
117
118     public void setEncoding(String JavaDoc enc) throws IOException {
119         mBuffer.setEncoding(enc);
120         mConvertedCaches = getConvertedCaches(mBuffer.getEncoding());
121     }
122
123     public String JavaDoc getEncoding() throws IOException {
124         return mBuffer.getEncoding();
125     }
126
127     public long getBaseByteCount() throws IOException {
128         return mBuffer.getBaseByteCount();
129     }
130
131     public long getByteCount() throws IOException {
132         return mBuffer.getByteCount();
133     }
134     
135     public void writeTo(OutputStream out) throws IOException {
136         mBuffer.writeTo(out);
137     }
138     
139     public void append(byte b) throws IOException {
140         mBuffer.append(b);
141     }
142     
143     public void append(byte[] bytes) throws IOException {
144         mBuffer.append(bytes);
145     }
146     
147     public void append(byte[] bytes, int offset, int length)
148         throws IOException {
149
150         mBuffer.append(bytes, offset, length);
151     }
152     
153     public void appendSurrogate(ByteData s) throws IOException {
154         mBuffer.appendSurrogate(s);
155     }
156
157     public void addCaptureBuffer(ByteBuffer buffer) throws IOException {
158         mBuffer.addCaptureBuffer(buffer);
159     }
160     
161     public void removeCaptureBuffer(ByteBuffer buffer) throws IOException {
162         mBuffer.removeCaptureBuffer(buffer);
163     }
164     
165     public void append(char c) throws IOException {
166         mBuffer.append(c);
167     }
168     
169     public void append(char[] chars) throws IOException {
170         mBuffer.append(chars);
171     }
172     
173     public void append(char[] chars, int offset, int length)
174         throws IOException {
175
176         mBuffer.append(chars, offset, length);
177     }
178     
179     public void append(String JavaDoc str) throws IOException {
180         Map cache;
181         if ((cache = getConvertedCache(str)) == null) {
182             mBuffer.append(str);
183             return;
184         }
185
186         // Caching performed using a two pass technique. This is done to
187
// avoid the cost of String.getBytes() for strings that aren't
188
// actually interned.
189

190         Object JavaDoc value;
191
192         if ((value = cache.get(str)) != null) {
193             byte[] bytes;
194             if (value != MARKER) {
195                 bytes = (byte[])value;
196             }
197             else {
198                 // This is at least the second time the string has been seen,
199
// so assume it has been interned and call String.getBytes().
200
String JavaDoc enc = getEncoding();
201                 if (enc != null) {
202                     bytes = str.getBytes(enc);
203                 }
204                 else {
205                     // no encoding specified so use default.
206
bytes = str.getBytes();
207                 }
208                 cache.put(str, bytes);
209             }
210
211             mBuffer.append(bytes);
212         }
213         else {
214             // Just put a marker at first to indicate that the string has been
215
// seen, but don't call String.getBytes() just yet.
216
if (mRandom == null) {
217                 mRandom = getRandom();
218             }
219             if ((mRandom.nextInt() % 20) == 0) {
220                 // Only mark sometimes in order to reduce the amount of times
221
// put is called for strings that will never be seen again.
222
// Calculating a random number is cheaper than putting into an
223
// IdentityMap because no objects are created. A consequence of
224
// this optimization is that it will take more iterations to
225
// discover the real string literals, but they will be
226
// discovered eventually.
227
cache.put(str, MARKER);
228             }
229             mBuffer.append(str);
230         }
231     }
232     
233     public void append(String JavaDoc str, int offset, int length) throws IOException {
234         mBuffer.append(str, offset, length);
235     }
236
237     public void reset() throws IOException {
238         mBuffer.reset();
239     }
240
241     public void drain() throws IOException {
242         mBuffer.drain();
243     }
244
245     private Map getConvertedCache(String JavaDoc str) {
246         if (str.length() < MIN_LENGTH) {
247             return null;
248         }
249         return mConvertedCaches[str.charAt(1) % CACHES_PER_ENCODING];
250     }
251
252     private void readObject(ObjectInputStream in)
253         throws IOException, ClassNotFoundException JavaDoc
254     {
255         in.defaultReadObject();
256         mConvertedCaches = getConvertedCaches(mBuffer.getEncoding());
257     }
258 }
259
Popular Tags