KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > tomcat > util > buf > CharChunk


1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17
18 package org.apache.tomcat.util.buf;
19
20 import java.io.IOException JavaDoc;
21 import java.io.Serializable JavaDoc;
22
23 /**
24  * Utilities to manipluate char chunks. While String is
25  * the easiest way to manipulate chars ( search, substrings, etc),
26  * it is known to not be the most efficient solution - Strings are
27  * designed as imutable and secure objects.
28  *
29  * @author dac@sun.com
30  * @author James Todd [gonzo@sun.com]
31  * @author Costin Manolache
32  * @author Remy Maucherat
33  */

34 public final class CharChunk implements Cloneable JavaDoc, Serializable JavaDoc, CharSequence JavaDoc {
35
36     // Input interface, used when the buffer is emptied.
37
public static interface CharInputChannel {
38         /**
39          * Read new bytes ( usually the internal conversion buffer ).
40          * The implementation is allowed to ignore the parameters,
41          * and mutate the chunk if it wishes to implement its own buffering.
42          */

43         public int realReadChars(char cbuf[], int off, int len)
44             throws IOException JavaDoc;
45     }
46     /**
47      * When we need more space we'll either
48      * grow the buffer ( up to the limit ) or send it to a channel.
49      */

50     public static interface CharOutputChannel {
51     /** Send the bytes ( usually the internal conversion buffer ).
52      * Expect 8k output if the buffer is full.
53      */

54         public void realWriteChars(char cbuf[], int off, int len)
55             throws IOException JavaDoc;
56     }
57     
58     // --------------------
59
// char[]
60
private char buff[];
61
62     private int start;
63     private int end;
64
65     private boolean isSet=false; // XXX
66

67     private boolean isOutput=false;
68
69     // -1: grow undefinitely
70
// maximum amount to be cached
71
private int limit=-1;
72
73     private CharInputChannel in = null;
74     private CharOutputChannel out = null;
75     
76     private boolean optimizedWrite=true;
77
78     /**
79      * Creates a new, uninitialized CharChunk object.
80      */

81     public CharChunk() {
82     }
83
84     public CharChunk(int size) {
85     allocate( size, -1 );
86     }
87
88     // --------------------
89

90     public CharChunk getClone() {
91     try {
92         return (CharChunk)this.clone();
93     } catch( Exception JavaDoc ex) {
94         return null;
95     }
96     }
97
98     public boolean isNull() {
99     if( end > 0 ) return false;
100     return !isSet; //XXX
101
}
102     
103     /**
104      * Resets the message bytes to an uninitialized state.
105      */

106     public void recycle() {
107     // buff=null;
108
isSet=false; // XXX
109
start=0;
110     end=0;
111     }
112
113     public void reset() {
114     buff=null;
115     }
116
117     // -------------------- Setup --------------------
118

119     public void allocate( int initial, int limit ) {
120     isOutput=true;
121     if( buff==null || buff.length < initial ) {
122         buff=new char[initial];
123     }
124     this.limit=limit;
125     start=0;
126     end=0;
127     isOutput=true;
128     isSet=true;
129     }
130
131
132     public void setOptimizedWrite(boolean optimizedWrite) {
133         this.optimizedWrite = optimizedWrite;
134     }
135
136     public void setChars( char[] c, int off, int len ) {
137         buff=c;
138         start=off;
139         end=start + len;
140         isSet=true;
141     }
142
143     /** Maximum amount of data in this buffer.
144      *
145      * If -1 or not set, the buffer will grow undefinitely.
146      * Can be smaller than the current buffer size ( which will not shrink ).
147      * When the limit is reached, the buffer will be flushed ( if out is set )
148      * or throw exception.
149      */

150     public void setLimit(int limit) {
151     this.limit=limit;
152     }
153     
154     public int getLimit() {
155     return limit;
156     }
157
158     /**
159      * When the buffer is empty, read the data from the input channel.
160      */

161     public void setCharInputChannel(CharInputChannel in) {
162         this.in = in;
163     }
164
165     /** When the buffer is full, write the data to the output channel.
166      * Also used when large amount of data is appended.
167      *
168      * If not set, the buffer will grow to the limit.
169      */

170     public void setCharOutputChannel(CharOutputChannel out) {
171     this.out=out;
172     }
173
174     // compat
175
public char[] getChars()
176     {
177     return getBuffer();
178     }
179     
180     public char[] getBuffer()
181     {
182     return buff;
183     }
184     
185     /**
186      * Returns the start offset of the bytes.
187      * For output this is the end of the buffer.
188      */

189     public int getStart() {
190     return start;
191     }
192     
193     public int getOffset() {
194     return start;
195     }
196
197     /**
198      * Returns the start offset of the bytes.
199      */

200     public void setOffset(int off) {
201     start=off;
202     }
203
204     /**
205      * Returns the length of the bytes.
206      */

207     public int getLength() {
208     return end-start;
209     }
210
211
212     public int getEnd() {
213     return end;
214     }
215
216     public void setEnd( int i ) {
217     end=i;
218     }
219
220     // -------------------- Adding data --------------------
221

222     public void append( char b )
223     throws IOException JavaDoc
224     {
225     makeSpace( 1 );
226
227     // couldn't make space
228
if( limit >0 && end >= limit ) {
229         flushBuffer();
230     }
231     buff[end++]=b;
232     }
233     
234     public void append( CharChunk src )
235     throws IOException JavaDoc
236     {
237     append( src.getBuffer(), src.getOffset(), src.getLength());
238     }
239
240     /** Add data to the buffer
241      */

242     public void append( char src[], int off, int len )
243     throws IOException JavaDoc
244     {
245     // will grow, up to limit
246
makeSpace( len );
247
248     // if we don't have limit: makeSpace can grow as it wants
249
if( limit < 0 ) {
250         // assert: makeSpace made enough space
251
System.arraycopy( src, off, buff, end, len );
252         end+=len;
253         return;
254     }
255
256         // Optimize on a common case.
257
// If the source is going to fill up all the space in buffer, may
258
// as well write it directly to the output, and avoid an extra copy
259
if ( optimizedWrite && len == limit && end == start && out != null ) {
260             out.realWriteChars( src, off, len );
261             return;
262         }
263     
264     // if we have limit and we're below
265
if( len <= limit - end ) {
266         // makeSpace will grow the buffer to the limit,
267
// so we have space
268
System.arraycopy( src, off, buff, end, len );
269             
270         end+=len;
271         return;
272     }
273
274     // need more space than we can afford, need to flush
275
// buffer
276

277     // the buffer is already at ( or bigger than ) limit
278

279     // Optimization:
280
// If len-avail < length ( i.e. after we fill the buffer with
281
// what we can, the remaining will fit in the buffer ) we'll just
282
// copy the first part, flush, then copy the second part - 1 write
283
// and still have some space for more. We'll still have 2 writes, but
284
// we write more on the first.
285

286     if( len + end < 2 * limit ) {
287         /* If the request length exceeds the size of the output buffer,
288            flush the output buffer and then write the data directly.
289            We can't avoid 2 writes, but we can write more on the second
290         */

291         int avail=limit-end;
292         System.arraycopy(src, off, buff, end, avail);
293         end += avail;
294         
295         flushBuffer();
296         
297         System.arraycopy(src, off+avail, buff, end, len - avail);
298         end+= len - avail;
299         
300     } else { // len > buf.length + avail
301
// long write - flush the buffer and write the rest
302
// directly from source
303
flushBuffer();
304         
305         out.realWriteChars( src, off, len );
306     }
307     }
308
309
310     /** Add data to the buffer
311      */

312     public void append( StringBuffer JavaDoc sb )
313     throws IOException JavaDoc
314     {
315     int len=sb.length();
316
317     // will grow, up to limit
318
makeSpace( len );
319
320     // if we don't have limit: makeSpace can grow as it wants
321
if( limit < 0 ) {
322         // assert: makeSpace made enough space
323
sb.getChars(0, len, buff, end );
324         end+=len;
325         return;
326     }
327
328     int off=0;
329     int sbOff = off;
330     int sbEnd = off + len;
331     while (sbOff < sbEnd) {
332         int d = min(limit - end, sbEnd - sbOff);
333         sb.getChars( sbOff, sbOff+d, buff, end);
334         sbOff += d;
335         end += d;
336         if (end >= limit)
337         flushBuffer();
338     }
339     }
340
341     /** Append a string to the buffer
342      */

343     public void append(String JavaDoc s) throws IOException JavaDoc {
344         append(s, 0, s.length());
345     }
346     
347     /** Append a string to the buffer
348      */

349     public void append(String JavaDoc s, int off, int len) throws IOException JavaDoc {
350     if (s==null) return;
351     
352     // will grow, up to limit
353
makeSpace( len );
354
355     // if we don't have limit: makeSpace can grow as it wants
356
if( limit < 0 ) {
357         // assert: makeSpace made enough space
358
s.getChars(off, off+len, buff, end );
359         end+=len;
360         return;
361     }
362
363     int sOff = off;
364     int sEnd = off + len;
365     while (sOff < sEnd) {
366         int d = min(limit - end, sEnd - sOff);
367         s.getChars( sOff, sOff+d, buff, end);
368         sOff += d;
369         end += d;
370         if (end >= limit)
371         flushBuffer();
372     }
373     }
374     
375     // -------------------- Removing data from the buffer --------------------
376

377     public int substract()
378         throws IOException JavaDoc {
379
380         if ((end - start) == 0) {
381             if (in == null)
382                 return -1;
383             int n = in.realReadChars(buff, end, buff.length - end);
384             if (n < 0)
385                 return -1;
386         }
387
388         return (buff[start++]);
389
390     }
391
392     public int substract(CharChunk src)
393         throws IOException JavaDoc {
394
395         if ((end - start) == 0) {
396             if (in == null)
397                 return -1;
398             int n = in.realReadChars( buff, end, buff.length - end);
399             if (n < 0)
400                 return -1;
401         }
402
403         int len = getLength();
404         src.append(buff, start, len);
405         start = end;
406         return len;
407
408     }
409
410     public int substract( char src[], int off, int len )
411         throws IOException JavaDoc {
412
413         if ((end - start) == 0) {
414             if (in == null)
415                 return -1;
416             int n = in.realReadChars( buff, end, buff.length - end);
417             if (n < 0)
418                 return -1;
419         }
420
421         int n = len;
422         if (len > getLength()) {
423             n = getLength();
424         }
425         System.arraycopy(buff, start, src, off, n);
426         start += n;
427         return n;
428
429     }
430
431
432     public void flushBuffer()
433     throws IOException JavaDoc
434     {
435     //assert out!=null
436
if( out==null ) {
437         throw new IOException JavaDoc( "Buffer overflow, no sink " + limit + " " +
438                    buff.length );
439     }
440     out.realWriteChars( buff, start, end - start );
441     end=start;
442     }
443
444     /** Make space for len chars. If len is small, allocate
445      * a reserve space too. Never grow bigger than limit.
446      */

447     private void makeSpace(int count)
448     {
449     char[] tmp = null;
450
451     int newSize;
452     int desiredSize=end + count;
453
454     // Can't grow above the limit
455
if( limit > 0 &&
456         desiredSize > limit) {
457         desiredSize=limit;
458     }
459
460     if( buff==null ) {
461         if( desiredSize < 256 ) desiredSize=256; // take a minimum
462
buff=new char[desiredSize];
463     }
464
465     // limit < buf.length ( the buffer is already big )
466
// or we already have space XXX
467
if( desiredSize <= buff.length) {
468         return;
469     }
470     // grow in larger chunks
471
if( desiredSize < 2 * buff.length ) {
472         newSize= buff.length * 2;
473         if( limit >0 &&
474         newSize > limit ) newSize=limit;
475         tmp=new char[newSize];
476     } else {
477         newSize= buff.length * 2 + count ;
478         if( limit > 0 &&
479         newSize > limit ) newSize=limit;
480         tmp=new char[newSize];
481     }
482     
483     System.arraycopy(buff, start, tmp, start, end-start);
484     buff = tmp;
485     tmp = null;
486     }
487     
488     // -------------------- Conversion and getters --------------------
489

490     public String JavaDoc toString() {
491         if (null == buff) {
492             return null;
493         } else if (end-start == 0) {
494             return "";
495         }
496         return StringCache.toString(this);
497     }
498     
499     public String JavaDoc toStringInternal() {
500         return new String JavaDoc(buff, start, end-start);
501     }
502
503     public int getInt()
504     {
505     return Ascii.parseInt(buff, start,
506                 end-start);
507     }
508     
509     // -------------------- equals --------------------
510

511     /**
512      * Compares the message bytes to the specified String object.
513      * @param s the String to compare
514      * @return true if the comparison succeeded, false otherwise
515      */

516     public boolean equals(String JavaDoc s) {
517     char[] c = buff;
518     int len = end-start;
519     if (c == null || len != s.length()) {
520         return false;
521     }
522     int off = start;
523     for (int i = 0; i < len; i++) {
524         if (c[off++] != s.charAt(i)) {
525         return false;
526         }
527     }
528     return true;
529     }
530
531     /**
532      * Compares the message bytes to the specified String object.
533      * @param s the String to compare
534      * @return true if the comparison succeeded, false otherwise
535      */

536     public boolean equalsIgnoreCase(String JavaDoc s) {
537     char[] c = buff;
538     int len = end-start;
539     if (c == null || len != s.length()) {
540         return false;
541     }
542     int off = start;
543     for (int i = 0; i < len; i++) {
544         if (Ascii.toLower( c[off++] ) != Ascii.toLower( s.charAt(i))) {
545         return false;
546         }
547     }
548     return true;
549     }
550
551     public boolean equals(CharChunk cc) {
552     return equals( cc.getChars(), cc.getOffset(), cc.getLength());
553     }
554
555     public boolean equals(char b2[], int off2, int len2) {
556     char b1[]=buff;
557     if( b1==null && b2==null ) return true;
558     
559     if (b1== null || b2==null || end-start != len2) {
560         return false;
561     }
562     int off1 = start;
563     int len=end-start;
564     while ( len-- > 0) {
565         if (b1[off1++] != b2[off2++]) {
566         return false;
567         }
568     }
569     return true;
570     }
571
572     public boolean equals(byte b2[], int off2, int len2) {
573     char b1[]=buff;
574     if( b2==null && b1==null ) return true;
575
576     if (b1== null || b2==null || end-start != len2) {
577         return false;
578     }
579     int off1 = start;
580     int len=end-start;
581     
582     while ( len-- > 0) {
583         if ( b1[off1++] != (char)b2[off2++]) {
584         return false;
585         }
586     }
587     return true;
588     }
589     
590     /**
591      * Returns true if the message bytes starts with the specified string.
592      * @param s the string
593      */

594     public boolean startsWith(String JavaDoc s) {
595     char[] c = buff;
596     int len = s.length();
597     if (c == null || len > end-start) {
598         return false;
599     }
600     int off = start;
601     for (int i = 0; i < len; i++) {
602         if (c[off++] != s.charAt(i)) {
603         return false;
604         }
605     }
606     return true;
607     }
608     
609     /**
610      * Returns true if the message bytes starts with the specified string.
611      * @param s the string
612      */

613     public boolean startsWithIgnoreCase(String JavaDoc s, int pos) {
614     char[] c = buff;
615     int len = s.length();
616     if (c == null || len+pos > end-start) {
617         return false;
618     }
619     int off = start+pos;
620     for (int i = 0; i < len; i++) {
621         if (Ascii.toLower( c[off++] ) != Ascii.toLower( s.charAt(i))) {
622         return false;
623         }
624     }
625     return true;
626     }
627     
628
629     // -------------------- Hash code --------------------
630

631     // normal hash.
632
public int hash() {
633     int code=0;
634     for (int i = start; i < start + end-start; i++) {
635         code = code * 37 + buff[i];
636     }
637     return code;
638     }
639
640     // hash ignoring case
641
public int hashIgnoreCase() {
642     int code=0;
643     for (int i = start; i < end; i++) {
644         code = code * 37 + Ascii.toLower(buff[i]);
645     }
646     return code;
647     }
648
649     public int indexOf(char c) {
650     return indexOf( c, start);
651     }
652     
653     /**
654      * Returns true if the message bytes starts with the specified string.
655      * @param c the character
656      */

657     public int indexOf(char c, int starting) {
658     int ret = indexOf( buff, start+starting, end, c );
659     return (ret >= start) ? ret - start : -1;
660     }
661
662     public static int indexOf( char chars[], int off, int cend, char qq )
663     {
664     while( off < cend ) {
665         char b=chars[off];
666         if( b==qq )
667         return off;
668         off++;
669     }
670     return -1;
671     }
672     
673
674     public int indexOf( String JavaDoc src, int srcOff, int srcLen, int myOff ) {
675     char first=src.charAt( srcOff );
676
677     // Look for first char
678
int srcEnd = srcOff + srcLen;
679         
680     for( int i=myOff+start; i <= (end - srcLen); i++ ) {
681         if( buff[i] != first ) continue;
682         // found first char, now look for a match
683
int myPos=i+1;
684         for( int srcPos=srcOff + 1; srcPos< srcEnd; ) {
685                 if( buff[myPos++] != src.charAt( srcPos++ ))
686             break;
687                 if( srcPos==srcEnd ) return i-start; // found it
688
}
689     }
690     return -1;
691     }
692
693     // -------------------- utils
694
private int min(int a, int b) {
695     if (a < b) return a;
696     return b;
697     }
698
699     // Char sequence impl
700

701     public char charAt(int index) {
702         return buff[index + start];
703     }
704     
705     public CharSequence JavaDoc subSequence(int start, int end) {
706         try {
707             CharChunk result = (CharChunk) this.clone();
708             result.setOffset(this.start + start);
709             result.setEnd(this.start + end);
710             return result;
711         } catch (CloneNotSupportedException JavaDoc e) {
712             // Cannot happen
713
return null;
714         }
715     }
716     
717     public int length() {
718         return end - start;
719     }
720     
721 }
722
Popular Tags