KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > enhydra > apache > xerces > utils > CharDataChunk


1 /*
2  * The Apache Software License, Version 1.1
3  *
4  *
5  * Copyright (c) 1999 The Apache Software Foundation. All rights
6  * 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  * Apache Software Foundation (http://www.apache.org/)."
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 "Xerces" and "Apache Software Foundation" 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 apache@apache.org.
31  *
32  * 5. Products derived from this software may not be called "Apache",
33  * nor may "Apache" appear in their name, without prior written
34  * permission of the Apache Software Foundation.
35  *
36  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
37  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
38  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
39  * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
40  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
41  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
42  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
43  * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
44  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
45  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
46  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
47  * SUCH DAMAGE.
48  * ====================================================================
49  *
50  * This software consists of voluntary contributions made by many
51  * individuals on behalf of the Apache Software Foundation and was
52  * originally based on software copyright (c) 1999, International
53  * Business Machines, Inc., http://www.apache.org. For more
54  * information on the Apache Software Foundation, please see
55  * <http://www.apache.org/>.
56  */

57
58 package org.enhydra.apache.xerces.utils;
59
60 import org.enhydra.apache.xerces.readers.XMLEntityHandler;
61
62 /**
63  * This class provides the character buffers used by some of the
64  * reader classes. The instances of this class are reference
65  * counted and placed upon a free list for reallocation when no
66  * longer in use so that they are reclaimed faster and with less
67  * overhead than using the garbage collector.
68  *
69  * @version
70  */

71 public final class CharDataChunk implements StringPool.StringProducer {
72     /**
73      * Chunk size constants
74      *
75      * The reader classes use the chunk size directly for better performance.
76      */

77     public static final int CHUNK_SHIFT = 14; // 2^14 = 16k
78
public static final int CHUNK_SIZE = (1 << CHUNK_SHIFT);
79     public static final int CHUNK_MASK = CHUNK_SIZE - 1;
80     /**
81      * Public constructor (factory)
82      *
83      * If there are any free instances available, remove them from the
84      * free list and reinitialize them. If not, allocate a new one.
85      *
86      * @param stringPool The string pool.
87      * @param prev The chunk that precedes this one, or null if this is
88      * the first chunk.
89      * @return The instance reused or created.
90      */

91     public static CharDataChunk createChunk(StringPool stringPool, CharDataChunk prev) {
92
93         CharDataChunk newChunk = null;
94         synchronized (CharDataChunk.class) {
95             newChunk = fgFreeChunks;
96             if (newChunk != null) {
97                 fgFreeChunks = newChunk.fNextChunk;
98             } else {
99                 newChunk = new CharDataChunk();
100             }
101         }
102         newChunk.fStringPool = stringPool;
103         newChunk.fRefCount = 1; // account for the reference we return to the caller
104
newChunk.fChunk = prev == null ? 0 : prev.fChunk + 1;
105         newChunk.fNextChunk = null;
106         newChunk.fPreviousChunk = prev;
107         if (prev != null) {
108             //
109
// You might think that we should call prev.addRef() here,
110
// and you would normally be correct. However, the reader
111
// that calls us is doing something like this:
112
//
113
// fCurrentChunk = CharDataChunk.createChunk(fStringPool, fCurrentChunk);
114
//
115
// During this call, the fCurrentChunk changes from the
116
// previous chunk to this chunk, losing the reference to
117
// the previous chunk. To avoid needing code like this:
118
//
119
// CharDataChunk prevChunk = fCurrentChunk;
120
// fCurrentChunk = CharDataChunk.createChunk(fStringPool, prevChunk);
121
// prevChunk.releaseChunk();
122
//
123
// We "adopt the reference" to the previous chunk into our
124
// fPreviousChunk field, since the addRef() followed by a
125
// removeRef() from the caller after we return just cancel
126
// each other out. The previous chunk reference will go
127
// away later when clearPreviousChunk is called.
128
//
129
prev.setNextChunk(newChunk);
130         }
131         return newChunk;
132     }
133     /**
134      * Return the instance that contains the specified offset.
135      *
136      * This method must always be invoked on an instance that
137      * contains the specified offset, or an instance the contains
138      * an offset greater than, i.e. after, the instance we are
139      * to return.
140      *
141      * @param offset The offset to find.
142      * @return The instance containing the offset.
143      */

144     public CharDataChunk chunkFor(int offset) {
145         int firstChunk = offset >> CHUNK_SHIFT;
146         if (firstChunk == fChunk)
147             return this;
148         CharDataChunk dataChunk = fPreviousChunk;
149         while (firstChunk != dataChunk.fChunk)
150             dataChunk = dataChunk.fPreviousChunk;
151         return dataChunk;
152     }
153     /**
154      * Get the character array of this instance.
155      *
156      * The reader classes access the data of each instance directly.
157      * This class only exists to manage the lifetime of the references
158      * to each instance. It is not intended to hide from the readers
159      * the fact that each instance contains a buffer of character data.
160      *
161      * @return The character data.
162      */

163     public char[] toCharArray() {
164         return fData;
165     }
166     /**
167      * Set the character array for this instance.
168      *
169      * @param data The character data.
170      */

171     public void setCharArray(char[] data) {
172         fData = data;
173     }
174     /**
175      * Get the next chunk.
176      *
177      * @return The instance that follows this one in the list of chunks,
178      * or null if there is no such instance.
179      */

180     public CharDataChunk nextChunk() {
181         return fNextChunk;
182     }
183     /**
184      * Clean the previous chunk reference.
185      *
186      * When a reader has reached a point where it knows that it will no
187      * longer call the addString, addSymbol, or append methods with an
188      * offset that is contained within a chunk that precedes this one,
189      * it will call this method to clear the reference from this chunk to
190      * the one preceding it. This allows the references between chunks
191      * to be dropped as we go and allow the unused instances to be placed
192      * upon the free list for reuse.
193      *
194      * @return <code>true</code> if we cleared the previous chunk pointer;
195      * otherwise <code>false</code> if the pointer is already null.
196      */

197     public boolean clearPreviousChunk() {
198         if (fPreviousChunk != null) {
199             fPreviousChunk.clearNextChunk();
200             fPreviousChunk.removeRef();
201             fPreviousChunk = null;
202             return true;
203         }
204         return false;
205     }
206     /**
207      * Release the reference to this chunk held by the reader that allocated
208      * this instance. Called at end of input to release the last chunk in the
209      * list used by the reader.
210      */

211     public void releaseChunk() {
212         removeRef();
213     }
214     /**
215      * Add a range from this chunk to the <code>StringPool</code>
216      *
217      * @param offset the offset of the first character to be added
218      * @param length the number of characters to add
219      * @return the <code>StringPool</code> handle that was added.
220      */

221     public int addString(int offset, int length) {
222         int chunk = offset >> CHUNK_SHIFT;
223         if (chunk != fChunk) {
224             if (fPreviousChunk == null)
225                 throw new RuntimeException JavaDoc(new ImplementationMessages().createMessage(null, ImplementationMessages.INT_PCN, 0, null));
226             return fPreviousChunk.addString(offset, length);
227         }
228         int lastChunk = (offset + length - 1) >> CHUNK_SHIFT;
229         if (chunk == lastChunk) {
230             addRef();
231             return fStringPool.addString(this, offset & CHUNK_MASK, length);
232         }
233         String JavaDoc str = toString(offset & CHUNK_MASK, length);
234         return fStringPool.addString(str);
235     }
236     /**
237      * Add a range from this chunk to the <code>StringPool</code> as a symbol
238      *
239      * @param offset the offset of the first character to be added
240      * @param length the number of characters to add
241      * @param hashcode hashcode to match to ensure uniqueness
242      * @return the <code>StringPool</code> handle that was added.
243      */

244     public int addSymbol(int offset, int length, int hashcode) {
245         int chunk = offset >> CHUNK_SHIFT;
246         if (chunk != fChunk) {
247             if (fPreviousChunk == null)
248                 throw new RuntimeException JavaDoc(new ImplementationMessages().createMessage(null, ImplementationMessages.INT_PCN, 0, null));
249             return fPreviousChunk.addSymbol(offset, length, hashcode);
250         }
251         int lastChunk = (offset + length - 1) >> CHUNK_SHIFT;
252         int index = offset & CHUNK_MASK;
253         if (chunk == lastChunk) {
254             if (hashcode == 0)
255                 hashcode = StringHasher.hashChars(fData, index, length);
256             int symbol = fStringPool.lookupSymbol(this, offset & CHUNK_MASK, length, hashcode);
257             if (symbol == -1) {
258                 String JavaDoc str = toString(offset & CHUNK_MASK, length);
259                 symbol = fStringPool.addNewSymbol(str, hashcode);
260             }
261             return symbol;
262         }
263         String JavaDoc str = toString(offset & CHUNK_MASK, length);
264         return fStringPool.addSymbol(str);
265     }
266     /**
267      * Append data from a <code>CharBuffer</code> to this chunk.
268      *
269      * @param charBuffer the buffer to be appended.
270      * @param offset the offset of the first character to be appended.
271      * @param length the number of characters to append.
272      */

273     public void append(XMLEntityHandler.CharBuffer charBuffer, int offset, int length) {
274         //
275
// Setup for the operation.
276
//
277
CharDataChunk dataChunk = chunkFor(offset);
278         int index = offset & CHUNK_MASK;
279         int nbytes = (index + length <= CHUNK_SIZE) ? length : CHUNK_SIZE - index;
280         //
281
// Visit each Chunk in turn until we are done.
282
//
283
while (true) {
284             charBuffer.append(dataChunk.fData, index, nbytes);
285             length -= nbytes;
286             if (length == 0)
287                 break;
288             dataChunk = dataChunk.fNextChunk;
289             index = 0;
290             nbytes = length <= CHUNK_SIZE ? length : CHUNK_SIZE;
291         }
292     }
293     //
294
// StringProducer interfaces
295
//
296
/**
297      * Return a range of characters as a <code>String</code>.
298      *
299      * @param offset the offset of the first character to convert.
300      * @param length the number of characters to convert.
301      * @return the <code>String</code>
302      */

303     public String JavaDoc toString(int offset, int length) {
304         if (offset + length <= CHUNK_SIZE) {
305             //
306
// All the chars are in the same chunk
307
//
308
return new String JavaDoc(fData, offset, length);
309         }
310         //
311
// The data is spread across chunks, so we need to build it in pieces.
312
//
313
StringBuffer JavaDoc sb = new StringBuffer JavaDoc(length);
314         //
315
// Copy the partial data from the first chunk.
316
//
317
int nbytes = CHUNK_SIZE - offset;
318         sb.append(fData, offset, nbytes);
319         length -= nbytes;
320         //
321
// Use each chunk in turn until we are done.
322
//
323
CharDataChunk aChunk = fNextChunk;
324         do {
325             nbytes = length <= CHUNK_SIZE ? length : CHUNK_SIZE;
326             sb.append(aChunk.fData, 0, nbytes);
327             length -= nbytes;
328             aChunk = aChunk.fNextChunk;
329         } while (length > 0);
330         String JavaDoc retval = sb.toString();
331         sb = null; // REVISIT - does this help gc ?
332
return retval;
333     }
334     /**
335      * Release a string from this chunk
336      *
337      * @param offset the offset of the first character to be released
338      * @param length the number of characters to release.
339      */

340     public void releaseString(int offset, int length) {
341         removeRef();
342     }
343     /**
344      * Compare a range in this chunk and a range in a character array for equality
345      *
346      * @param offset the offset of the first character in the range in this chunk
347      * @param length the number of characters in the range to compare
348      * @param strChars the character array to compare
349      * @param strOffset the offset of the first character in the range in strChars
350      * @param strLength the number of characters to release.
351      * @return true if the ranges are character-wise equal, otherwise false.
352      */

353     public boolean equalsString(int offset, int length, char[] strChars, int strOffset, int strLength) {
354         if (length != strLength)
355             return false;
356         if (offset + length <= CHUNK_SIZE) {
357             //
358
// All the chars are in the same chunk
359
//
360
for (int i = 0; i < length; i++) {
361                 if (fData[offset++] != strChars[strOffset++])
362                     return false;
363             }
364             return true;
365         }
366         //
367
// Compare the partial data from the first chunk.
368
//
369
int nbytes = CHUNK_SIZE - offset;
370         length -= nbytes;
371         while (nbytes-- > 0) {
372             if (fData[offset++] != strChars[strOffset++])
373                 return false;
374         }
375         //
376
// Check each chunk in turn until we are done.
377
//
378
CharDataChunk aChunk = fNextChunk;
379         do {
380             offset = 0;
381             nbytes = length <= CHUNK_SIZE ? length : CHUNK_SIZE;
382             length -= nbytes;
383             while (nbytes-- > 0) {
384                 if (aChunk.fData[offset++] != strChars[strOffset++])
385                     return false;
386             }
387             aChunk = aChunk.fNextChunk;
388         } while (length > 0);
389         return true;
390     }
391     //
392
// Private methods
393
//
394

395     //
396
// Constructor for factory method.
397
//
398
private CharDataChunk() {}
399     //
400
//
401
//
402
private void addRef() {
403         fRefCount++;
404     }
405     //
406
//
407
//
408
private void removeRef() {
409         fRefCount--;
410         if (fRefCount == 0) {
411             fStringPool = null;
412             fChunk = -1;
413             fPreviousChunk = null;
414             synchronized (CharDataChunk.class) {
415                 /*** Only keep one free chunk at a time! ***
416                 fNextChunk = fgFreeChunks;
417                 /***/

418                 fNextChunk = null;
419                 fgFreeChunks = this;
420             }
421         }
422     }
423     //
424
//
425
//
426
private void clearNextChunk() {
427         if (fNextChunk != null)
428             fNextChunk.removeRef();
429         fNextChunk = null;
430     }
431     //
432
//
433
//
434
private void setNextChunk(CharDataChunk nextChunk) {
435         if (fNextChunk != null) {
436             throw new RuntimeException JavaDoc("CharDataChunk::setNextChunk");
437         }
438         nextChunk.addRef();
439         fNextChunk = nextChunk;
440     }
441     //
442
// Private instance variables.
443
//
444
private StringPool fStringPool;
445     private int fRefCount;
446     private int fChunk;
447     private char[] fData = null;
448     private CharDataChunk fNextChunk;
449     private CharDataChunk fPreviousChunk;
450     private static CharDataChunk fgFreeChunks = null;
451 }
452
Popular Tags