KickJava   Java API By Example, From Geeks To Geeks.

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


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.text.*;
21 import java.util.*;
22 import java.io.Serializable JavaDoc;
23 import java.io.IOException JavaDoc;
24
25 /**
26  * This class is used to represent a subarray of bytes in an HTTP message.
27  * It represents all request/response elements. The byte/char conversions are
28  * delayed and cached. Everything is recyclable.
29  *
30  * The object can represent a byte[], a char[], or a (sub) String. All
31  * operations can be made in case sensitive mode or not.
32  *
33  * @author dac@eng.sun.com
34  * @author James Todd [gonzo@eng.sun.com]
35  * @author Costin Manolache
36  */

37 public final class MessageBytes implements Cloneable JavaDoc, Serializable JavaDoc {
38     // primary type ( whatever is set as original value )
39
private int type = T_NULL;
40
41     public static final int T_NULL = 0;
42     /** getType() is T_STR if the the object used to create the MessageBytes
43         was a String */

44     public static final int T_STR = 1;
45     /** getType() is T_STR if the the object used to create the MessageBytes
46         was a byte[] */

47     public static final int T_BYTES = 2;
48     /** getType() is T_STR if the the object used to create the MessageBytes
49         was a char[] */

50     public static final int T_CHARS = 3;
51
52     private int hashCode=0;
53     // did we computed the hashcode ?
54
private boolean hasHashCode=false;
55
56     // Is the represented object case sensitive ?
57
private boolean caseSensitive=true;
58
59     // Internal objects to represent array + offset, and specific methods
60
private ByteChunk byteC=new ByteChunk();
61     private CharChunk charC=new CharChunk();
62     
63     // String
64
private String JavaDoc strValue;
65     // true if a String value was computed. Probably not needed,
66
// strValue!=null is the same
67
private boolean hasStrValue=false;
68
69     /**
70      * Creates a new, uninitialized MessageBytes object.
71      * @deprecated Use static newInstance() in order to allow
72      * future hooks.
73      */

74     public MessageBytes() {
75     }
76
77     /** Construct a new MessageBytes instance
78      */

79     public static MessageBytes newInstance() {
80     return factory.newInstance();
81     }
82
83     /** Configure the case sensitivity
84      */

85     public void setCaseSenitive( boolean b ) {
86     caseSensitive=b;
87     }
88
89     public MessageBytes getClone() {
90     try {
91         return (MessageBytes)this.clone();
92     } catch( Exception JavaDoc ex) {
93         return null;
94     }
95     }
96
97     public boolean isNull() {
98 // should we check also hasStrValue ???
99
return byteC.isNull() && charC.isNull() && ! hasStrValue;
100     // bytes==null && strValue==null;
101
}
102     
103     /**
104      * Resets the message bytes to an uninitialized (NULL) state.
105      */

106     public void recycle() {
107     type=T_NULL;
108     byteC.recycle();
109     charC.recycle();
110
111     strValue=null;
112     caseSensitive=true;
113
114     hasStrValue=false;
115     hasHashCode=false;
116     hasIntValue=false;
117     hasLongValue=false;
118     hasDateValue=false;
119     }
120
121
122     /**
123      * Sets the content to the specified subarray of bytes.
124      *
125      * @param b the bytes
126      * @param off the start offset of the bytes
127      * @param len the length of the bytes
128      */

129     public void setBytes(byte[] b, int off, int len) {
130         byteC.setBytes( b, off, len );
131         type=T_BYTES;
132         hasStrValue=false;
133         hasHashCode=false;
134         hasIntValue=false;
135         hasLongValue=false;
136         hasDateValue=false;
137     }
138
139     /** Set the encoding. If the object was constructed from bytes[]. any
140      * previous conversion is reset.
141      * If no encoding is set, we'll use 8859-1.
142      */

143     public void setEncoding( String JavaDoc enc ) {
144     if( !byteC.isNull() ) {
145         // if the encoding changes we need to reset the converion results
146
charC.recycle();
147         hasStrValue=false;
148     }
149     byteC.setEncoding(enc);
150     }
151
152     /**
153      * Sets the content to be a char[]
154      *
155      * @param c the bytes
156      * @param off the start offset of the bytes
157      * @param len the length of the bytes
158      */

159     public void setChars( char[] c, int off, int len ) {
160         charC.setChars( c, off, len );
161         type=T_CHARS;
162         hasStrValue=false;
163         hasHashCode=false;
164         hasIntValue=false;
165         hasLongValue=false;
166         hasDateValue=false;
167     }
168
169     /** Remove the cached string value. Use it after a conversion on the
170      * byte[] or after the encoding is changed
171      * XXX Is this needed ?
172      */

173     public void resetStringValue() {
174     if( type != T_STR ) {
175         // If this was cread as a byte[] or char[], we remove
176
// the old string value
177
hasStrValue=false;
178         strValue=null;
179     }
180     }
181
182     /**
183      * Set the content to be a string
184      */

185     public void setString( String JavaDoc s ) {
186         if (s == null)
187             return;
188         strValue=s;
189         hasStrValue=true;
190         hasHashCode=false;
191         hasIntValue=false;
192         hasLongValue=false;
193         hasDateValue=false;
194         type=T_STR;
195     }
196
197     // -------------------- Conversion and getters --------------------
198

199     /** Compute the string value
200      */

201     public String JavaDoc toString() {
202         if( hasStrValue ) return strValue;
203         
204         switch (type) {
205         case T_CHARS:
206             strValue=charC.toString();
207             hasStrValue=true;
208             return strValue;
209         case T_BYTES:
210             strValue=byteC.toString();
211             hasStrValue=true;
212             return strValue;
213         }
214         return null;
215     }
216
217     //----------------------------------------
218
/** Return the type of the original content. Can be
219      * T_STR, T_BYTES, T_CHARS or T_NULL
220      */

221     public int getType() {
222     return type;
223     }
224     
225     /**
226      * Returns the byte chunk, representing the byte[] and offset/length.
227      * Valid only if T_BYTES or after a conversion was made.
228      */

229     public ByteChunk getByteChunk() {
230     return byteC;
231     }
232
233     /**
234      * Returns the char chunk, representing the char[] and offset/length.
235      * Valid only if T_CHARS or after a conversion was made.
236      */

237     public CharChunk getCharChunk() {
238     return charC;
239     }
240
241     /**
242      * Returns the string value.
243      * Valid only if T_STR or after a conversion was made.
244      */

245     public String JavaDoc getString() {
246     return strValue;
247     }
248
249     /** Unimplemented yet. Do a char->byte conversion.
250      */

251     public void toBytes() {
252         if( ! byteC.isNull() ) {
253             type=T_BYTES;
254             return;
255         }
256         toString();
257         type=T_BYTES;
258         byte bb[] = strValue.getBytes();
259         byteC.setBytes(bb, 0, bb.length);
260     }
261
262     /** Convert to char[] and fill the CharChunk.
263      * XXX Not optimized - it converts to String first.
264      */

265     public void toChars() {
266     if( ! charC.isNull() ) {
267             type=T_CHARS;
268         return;
269     }
270     // inefficient
271
toString();
272         type=T_CHARS;
273     char cc[]=strValue.toCharArray();
274     charC.setChars(cc, 0, cc.length);
275     }
276     
277
278     /**
279      * Returns the length of the original buffer.
280      * Note that the length in bytes may be different from the length
281      * in chars.
282      */

283     public int getLength() {
284     if(type==T_BYTES)
285         return byteC.getLength();
286     if(type==T_CHARS) {
287         return charC.getLength();
288     }
289     if(type==T_STR)
290         return strValue.length();
291     toString();
292     if( strValue==null ) return 0;
293     return strValue.length();
294     }
295
296     // -------------------- equals --------------------
297

298     /**
299      * Compares the message bytes to the specified String object.
300      * @param s the String to compare
301      * @return true if the comparison succeeded, false otherwise
302      */

303     public boolean equals(String JavaDoc s) {
304     if( ! caseSensitive )
305         return equalsIgnoreCase( s );
306     switch (type) {
307     case T_STR:
308         if( strValue==null && s!=null) return false;
309         return strValue.equals( s );
310     case T_CHARS:
311         return charC.equals( s );
312     case T_BYTES:
313         return byteC.equals( s );
314     default:
315         return false;
316     }
317     }
318
319     /**
320      * Compares the message bytes to the specified String object.
321      * @param s the String to compare
322      * @return true if the comparison succeeded, false otherwise
323      */

324     public boolean equalsIgnoreCase(String JavaDoc s) {
325     switch (type) {
326     case T_STR:
327         if( strValue==null && s!=null) return false;
328         return strValue.equalsIgnoreCase( s );
329     case T_CHARS:
330         return charC.equalsIgnoreCase( s );
331     case T_BYTES:
332         return byteC.equalsIgnoreCase( s );
333     default:
334         return false;
335     }
336     }
337
338     public boolean equals(MessageBytes mb) {
339     switch (type) {
340     case T_STR:
341         return mb.equals( strValue );
342     }
343
344     if( mb.type != T_CHARS &&
345         mb.type!= T_BYTES ) {
346         // it's a string or int/date string value
347
return equals( mb.toString() );
348     }
349
350     // mb is either CHARS or BYTES.
351
// this is either CHARS or BYTES
352
// Deal with the 4 cases ( in fact 3, one is simetric)
353

354     if( mb.type == T_CHARS && type==T_CHARS ) {
355         return charC.equals( mb.charC );
356     }
357     if( mb.type==T_BYTES && type== T_BYTES ) {
358         return byteC.equals( mb.byteC );
359     }
360     if( mb.type== T_CHARS && type== T_BYTES ) {
361         return byteC.equals( mb.charC );
362     }
363     if( mb.type== T_BYTES && type== T_CHARS ) {
364         return mb.byteC.equals( charC );
365     }
366     // can't happen
367
return true;
368     }
369
370     
371     /**
372      * Returns true if the message bytes starts with the specified string.
373      * @param s the string
374      */

375     public boolean startsWith(String JavaDoc s) {
376     switch (type) {
377     case T_STR:
378         return strValue.startsWith( s );
379     case T_CHARS:
380         return charC.startsWith( s );
381     case T_BYTES:
382         return byteC.startsWith( s );
383     default:
384         return false;
385     }
386     }
387
388     /**
389      * Returns true if the message bytes starts with the specified string.
390      * @param s the string
391      * @param pos The start position
392      */

393     public boolean startsWithIgnoreCase(String JavaDoc s, int pos) {
394     switch (type) {
395     case T_STR:
396         if( strValue==null ) return false;
397         if( strValue.length() < pos + s.length() ) return false;
398         
399         for( int i=0; i<s.length(); i++ ) {
400         if( Ascii.toLower( s.charAt( i ) ) !=
401             Ascii.toLower( strValue.charAt( pos + i ))) {
402             return false;
403         }
404         }
405         return true;
406     case T_CHARS:
407         return charC.startsWithIgnoreCase( s, pos );
408     case T_BYTES:
409         return byteC.startsWithIgnoreCase( s, pos );
410     default:
411         return false;
412     }
413     }
414
415     
416
417     // -------------------- Hash code --------------------
418
public int hashCode() {
419     if( hasHashCode ) return hashCode;
420     int code = 0;
421
422     if( caseSensitive )
423         code=hash();
424     else
425         code=hashIgnoreCase();
426     hashCode=code;
427     hasHashCode=true;
428     return code;
429     }
430
431     // normal hash.
432
private int hash() {
433     int code=0;
434     switch (type) {
435     case T_STR:
436         // We need to use the same hash function
437
for (int i = 0; i < strValue.length(); i++) {
438         code = code * 37 + strValue.charAt( i );
439         }
440         return code;
441     case T_CHARS:
442         return charC.hash();
443     case T_BYTES:
444         return byteC.hash();
445     default:
446         return 0;
447     }
448     }
449
450     // hash ignoring case
451
private int hashIgnoreCase() {
452     int code=0;
453     switch (type) {
454     case T_STR:
455         for (int i = 0; i < strValue.length(); i++) {
456         code = code * 37 + Ascii.toLower(strValue.charAt( i ));
457         }
458         return code;
459     case T_CHARS:
460         return charC.hashIgnoreCase();
461     case T_BYTES:
462         return byteC.hashIgnoreCase();
463     default:
464         return 0;
465     }
466     }
467
468     public int indexOf(char c) {
469     return indexOf( c, 0);
470     }
471
472     // Inefficient initial implementation. Will be replaced on the next
473
// round of tune-up
474
public int indexOf(String JavaDoc s, int starting) {
475     toString();
476     return strValue.indexOf( s, starting );
477     }
478     
479     // Inefficient initial implementation. Will be replaced on the next
480
// round of tune-up
481
public int indexOf(String JavaDoc s) {
482     return indexOf( s, 0 );
483     }
484     
485     public int indexOfIgnoreCase(String JavaDoc s, int starting) {
486     toString();
487     String JavaDoc upper=strValue.toUpperCase();
488     String JavaDoc sU=s.toUpperCase();
489     return upper.indexOf( sU, starting );
490     }
491     
492     /**
493      * Returns true if the message bytes starts with the specified string.
494      * @param c the character
495      * @param starting The start position
496      */

497     public int indexOf(char c, int starting) {
498     switch (type) {
499     case T_STR:
500         return strValue.indexOf( c, starting );
501     case T_CHARS:
502         return charC.indexOf( c, starting);
503     case T_BYTES:
504         return byteC.indexOf( c, starting );
505     default:
506         return -1;
507     }
508     }
509
510     /** Copy the src into this MessageBytes, allocating more space if
511      * needed
512      */

513     public void duplicate( MessageBytes src ) throws IOException JavaDoc
514     {
515     switch( src.getType() ) {
516     case MessageBytes.T_BYTES:
517         type=T_BYTES;
518         ByteChunk bc=src.getByteChunk();
519         byteC.allocate( 2 * bc.getLength(), -1 );
520         byteC.append( bc );
521         break;
522     case MessageBytes.T_CHARS:
523         type=T_CHARS;
524         CharChunk cc=src.getCharChunk();
525         charC.allocate( 2 * cc.getLength(), -1 );
526         charC.append( cc );
527         break;
528     case MessageBytes.T_STR:
529         type=T_STR;
530         String JavaDoc sc=src.getString();
531         this.setString( sc );
532         break;
533     }
534     }
535
536     // -------------------- Deprecated code --------------------
537
// efficient int, long and date
538
// XXX used only for headers - shouldn't be
539
// stored here.
540
private int intValue;
541     private boolean hasIntValue=false;
542     private long longValue;
543     private boolean hasLongValue=false;
544     private Date dateValue;
545     private boolean hasDateValue=false;
546     
547     /**
548      * @deprecated The buffer are general purpose, caching for headers should
549      * be done in headers. The second parameter allows us to pass a date format
550      * instance to avoid synchronization problems.
551      */

552     public void setTime(long t, DateFormat df) {
553     // XXX replace it with a byte[] tool
554
recycle();
555     if( dateValue==null)
556         dateValue=new Date(t);
557     else
558         dateValue.setTime(t);
559     if( df==null )
560         strValue=DateTool.format1123(dateValue);
561     else
562         strValue=DateTool.format1123(dateValue,df);
563     hasStrValue=true;
564     hasDateValue=true;
565     type=T_STR;
566     }
567
568     public void setTime(long t) {
569     setTime( t, null );
570     }
571
572     /** Set the buffer to the representation of an int
573      */

574     public void setInt(int i) {
575         byteC.allocate(16, 32);
576         int current = i;
577         byte[] buf = byteC.getBuffer();
578         int start = 0;
579         int end = 0;
580         if (i == 0) {
581             buf[end++] = (byte) '0';
582         }
583         if (i < 0) {
584             current = -i;
585             buf[end++] = (byte) '-';
586         }
587         while (current > 0) {
588             int digit = current % 10;
589             current = current / 10;
590             buf[end++] = HexUtils.HEX[digit];
591         }
592         byteC.setOffset(0);
593         byteC.setEnd(end);
594         // Inverting buffer
595
end--;
596         if (i < 0) {
597             start++;
598         }
599         while (end > start) {
600             byte temp = buf[start];
601             buf[start] = buf[end];
602             buf[end] = temp;
603             start++;
604             end--;
605         }
606         intValue=i;
607         hasStrValue=false;
608         hasHashCode=false;
609         hasIntValue=true;
610         hasLongValue=false;
611         hasDateValue=false;
612         type=T_BYTES;
613     }
614
615     /** Set the buffer to the representation of an long
616      */

617     public void setLong(long l) {
618         byteC.allocate(32, 64);
619         long current = l;
620         byte[] buf = byteC.getBuffer();
621         int start = 0;
622         int end = 0;
623         if (l == 0) {
624             buf[end++] = (byte) '0';
625         }
626         if (l < 0) {
627             current = -l;
628             buf[end++] = (byte) '-';
629         }
630         while (current > 0) {
631             int digit = (int) (current % 10);
632             current = current / 10;
633             buf[end++] = HexUtils.HEX[digit];
634         }
635         byteC.setOffset(0);
636         byteC.setEnd(end);
637         // Inverting buffer
638
end--;
639         if (l < 0) {
640             start++;
641         }
642         while (end > start) {
643             byte temp = buf[start];
644             buf[start] = buf[end];
645             buf[end] = temp;
646             start++;
647             end--;
648         }
649         longValue=l;
650         hasStrValue=false;
651         hasHashCode=false;
652         hasIntValue=false;
653         hasLongValue=true;
654         hasDateValue=false;
655         type=T_BYTES;
656     }
657
658     /**
659      * @deprecated The buffer are general purpose, caching for headers should
660      * be done in headers
661      */

662     public long getTime()
663     {
664         if( hasDateValue ) {
665         if( dateValue==null) return -1;
666         return dateValue.getTime();
667         }
668     
669         long l=DateTool.parseDate( this );
670         if( dateValue==null)
671             dateValue=new Date(l);
672         else
673             dateValue.setTime(l);
674         hasDateValue=true;
675         return l;
676     }
677     
678
679     // Used for headers conversion
680
/** Convert the buffer to an int, cache the value
681      */

682     public int getInt()
683     {
684     if( hasIntValue )
685         return intValue;
686     
687     switch (type) {
688     case T_BYTES:
689         intValue=byteC.getInt();
690         break;
691     default:
692         intValue=Integer.parseInt(toString());
693     }
694     hasIntValue=true;
695     return intValue;
696     }
697
698     // Used for headers conversion
699
/** Convert the buffer to an long, cache the value
700      */

701     public long getLong() {
702         if( hasLongValue )
703             return longValue;
704         
705         switch (type) {
706         case T_BYTES:
707             longValue=byteC.getLong();
708             break;
709         default:
710             longValue=Long.parseLong(toString());
711         }
712
713         hasLongValue=true;
714         return longValue;
715
716      }
717
718     // -------------------- Future may be different --------------------
719

720     private static MessageBytesFactory factory=new MessageBytesFactory();
721
722     public static void setFactory( MessageBytesFactory mbf ) {
723     factory=mbf;
724     }
725     
726     public static class MessageBytesFactory {
727     protected MessageBytesFactory() {
728     }
729     public MessageBytes newInstance() {
730         return new MessageBytes();
731     }
732     }
733 }
734
Popular Tags