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 package org.apache.tomcat.util.buf;
18
19 import java.io.IOException;
20 import java.io.Serializable;
21 import java.nio.ByteBuffer;
22 import java.nio.charset.Charset;
23 import java.util.Locale;
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, Serializable {
38     private static final long serialVersionUID = 1L;
39
40     // primary type ( whatever is set as original value )
41     private int type = T_NULL;
42
43     public static final int T_NULL = 0;
44     /** getType() is T_STR if the the object used to create the MessageBytes
45         was a String */

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

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

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

73     private MessageBytes() {
74     }
75
76     /**
77      * Construct a new MessageBytes instance.
78      * @return the instance
79      */

80     public static MessageBytes newInstance() {
81         return factory.newInstance();
82     }
83
84     @Override
85     public Object clone() throws CloneNotSupportedException {
86         return super.clone();
87     }
88
89     public boolean isNull() {
90         return byteC.isNull() && charC.isNull() && !hasStrValue;
91     }
92
93     /**
94      * Resets the message bytes to an uninitialized (NULL) state.
95      */

96     public void recycle() {
97         type=T_NULL;
98         byteC.recycle();
99         charC.recycle();
100
101         strValue=null;
102
103         hasStrValue=false;
104         hasHashCode=false;
105         hasLongValue=false;
106     }
107
108
109     /**
110      * Sets the content to the specified subarray of bytes.
111      *
112      * @param b the bytes
113      * @param off the start offset of the bytes
114      * @param len the length of the bytes
115      */

116     public void setBytes(byte[] b, int off, int len) {
117         byteC.setBytes( b, off, len );
118         type=T_BYTES;
119         hasStrValue=false;
120         hasHashCode=false;
121         hasLongValue=false;
122     }
123
124     /**
125      * Sets the content to be a char[]
126      *
127      * @param c the chars
128      * @param off the start offset of the chars
129      * @param len the length of the chars
130      */

131     public void setChars( char[] c, int off, int len ) {
132         charC.setChars( c, off, len );
133         type=T_CHARS;
134         hasStrValue=false;
135         hasHashCode=false;
136         hasLongValue=false;
137     }
138
139     /**
140      * Set the content to be a string
141      * @param s The string
142      */

143     public void setString( String s ) {
144         strValue=s;
145         hasHashCode=false;
146         hasLongValue=false;
147         if (s == null) {
148             hasStrValue=false;
149             type=T_NULL;
150         } else {
151             hasStrValue=true;
152             type=T_STR;
153         }
154     }
155
156     // -------------------- Conversion and getters --------------------
157
158     /**
159      * Compute the string value.
160      * @return the string
161      */

162     @Override
163     public String toString() {
164         if (hasStrValue) {
165             return strValue;
166         }
167
168         switch (type) {
169         case T_CHARS:
170             strValue = charC.toString();
171             hasStrValue = true;
172             return strValue;
173         case T_BYTES:
174             strValue = byteC.toString();
175             hasStrValue = true;
176             return strValue;
177         }
178         return null;
179     }
180
181     //----------------------------------------
182     /**
183      * Return the type of the original content. Can be
184      * T_STR, T_BYTES, T_CHARS or T_NULL
185      * @return the type
186      */

187     public int getType() {
188         return type;
189     }
190
191     /**
192      * Returns the byte chunk, representing the byte[] and offset/length.
193      * Valid only if T_BYTES or after a conversion was made.
194      * @return the byte chunk
195      */

196     public ByteChunk getByteChunk() {
197         return byteC;
198     }
199
200     /**
201      * Returns the char chunk, representing the char[] and offset/length.
202      * Valid only if T_CHARS or after a conversion was made.
203      * @return the char chunk
204      */

205     public CharChunk getCharChunk() {
206         return charC;
207     }
208
209     /**
210      * Returns the string value.
211      * Valid only if T_STR or after a conversion was made.
212      * @return the string
213      */

214     public String getString() {
215         return strValue;
216     }
217
218     /**
219      * @return the Charset used for string<->byte conversions.
220      */

221     public Charset getCharset() {
222         return byteC.getCharset();
223     }
224
225     /**
226      * Set the Charset used for string<->byte conversions.
227      * @param charset The charset
228      */

229     public void setCharset(Charset charset) {
230         byteC.setCharset(charset);
231     }
232
233
234     /**
235      * Do a char->byte conversion.
236      */

237     public void toBytes() {
238         if (isNull()) {
239             return;
240         }
241         if (!byteC.isNull()) {
242             type = T_BYTES;
243             return;
244         }
245         toString();
246         type = T_BYTES;
247         Charset charset = byteC.getCharset();
248         ByteBuffer result = charset.encode(strValue);
249         byteC.setBytes(result.array(), result.arrayOffset(), result.limit());
250     }
251
252
253     /**
254      * Convert to char[] and fill the CharChunk.
255      * XXX Not optimized - it converts to String first.
256      */

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

279     public int getLength() {
280         if(type==T_BYTES) {
281             return byteC.getLength();
282         }
283         if(type==T_CHARS) {
284             return charC.getLength();
285         }
286         if(type==T_STR) {
287             return strValue.length();
288         }
289         toString();
290         if( strValue==null ) {
291             return 0;
292         }
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 <code>true</code> if the comparison succeeded, <code>false</code> otherwise
302      */

303     public boolean equals(String s) {
304         switch (type) {
305         case T_STR:
306             if (strValue == null) {
307                 return s == null;
308             }
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 <code>true</code> if the comparison succeeded, <code>false</code> otherwise
323      */

324     public boolean equalsIgnoreCase(String s) {
325         switch (type) {
326         case T_STR:
327             if (strValue == null) {
328                 return s == null;
329             }
330             return strValue.equalsIgnoreCase( s );
331         case T_CHARS:
332             return charC.equalsIgnoreCase( s );
333         case T_BYTES:
334             return byteC.equalsIgnoreCase( s );
335         default:
336             return false;
337         }
338     }
339
340     @Override
341     public boolean equals(Object obj) {
342         if (obj instanceof MessageBytes) {
343             return equals((MessageBytes) obj);
344         }
345         return false;
346     }
347
348     public boolean equals(MessageBytes mb) {
349         switch (type) {
350         case T_STR:
351             return mb.equals( strValue );
352         }
353
354         if( mb.type != T_CHARS &&
355             mb.type!= T_BYTES ) {
356             // it's a string or int/date string value
357             return equals( mb.toString() );
358         }
359
360         // mb is either CHARS or BYTES.
361         // this is either CHARS or BYTES
362         // Deal with the 4 cases ( in fact 3, one is symmetric)
363
364         if( mb.type == T_CHARS && type==T_CHARS ) {
365             return charC.equals( mb.charC );
366         }
367         if( mb.type==T_BYTES && type== T_BYTES ) {
368             return byteC.equals( mb.byteC );
369         }
370         if( mb.type== T_CHARS && type== T_BYTES ) {
371             return byteC.equals( mb.charC );
372         }
373         if( mb.type== T_BYTES && type== T_CHARS ) {
374             return mb.byteC.equals( charC );
375         }
376         // can't happen
377         return true;
378     }
379
380
381     /**
382      * @return <code>true</code> if the message bytes starts with the specified string.
383      * @param s the string
384      * @param pos The start position
385      */

386     public boolean startsWithIgnoreCase(String s, int pos) {
387         switch (type) {
388         case T_STR:
389             if( strValue==null ) {
390                 return false;
391             }
392             if( strValue.length() < pos + s.length() ) {
393                 return false;
394             }
395
396             forint i=0; i<s.length(); i++ ) {
397                 if( Ascii.toLower( s.charAt( i ) ) !=
398                     Ascii.toLower( strValue.charAt( pos + i ))) {
399                     return false;
400                 }
401             }
402             return true;
403         case T_CHARS:
404             return charC.startsWithIgnoreCase( s, pos );
405         case T_BYTES:
406             return byteC.startsWithIgnoreCase( s, pos );
407         default:
408             return false;
409         }
410     }
411
412
413     // -------------------- Hash code  --------------------
414     @Override
415     public  int hashCode() {
416         if( hasHashCode ) {
417             return hashCode;
418         }
419         int code = 0;
420
421         code=hash();
422         hashCode=code;
423         hasHashCode=true;
424         return code;
425     }
426
427     // normal hash.
428     private int hash() {
429         int code=0;
430         switch (type) {
431         case T_STR:
432             // We need to use the same hash function
433             for (int i = 0; i < strValue.length(); i++) {
434                 code = code * 37 + strValue.charAt( i );
435             }
436             return code;
437         case T_CHARS:
438             return charC.hash();
439         case T_BYTES:
440             return byteC.hash();
441         default:
442             return 0;
443         }
444     }
445
446     // Inefficient initial implementation. Will be replaced on the next
447     // round of tune-up
448     public int indexOf(String s, int starting) {
449         toString();
450         return strValue.indexOf( s, starting );
451     }
452
453     // Inefficient initial implementation. Will be replaced on the next
454     // round of tune-up
455     public int indexOf(String s) {
456         return indexOf( s, 0 );
457     }
458
459     public int indexOfIgnoreCase(String s, int starting) {
460         toString();
461         String upper=strValue.toUpperCase(Locale.ENGLISH);
462         String sU=s.toUpperCase(Locale.ENGLISH);
463         return upper.indexOf( sU, starting );
464     }
465
466     /**
467      * Copy the src into this MessageBytes, allocating more space if needed.
468      * @param src The source
469      * @throws IOException Writing overflow data to the output channel failed
470      */

471     public void duplicate( MessageBytes src ) throws IOException
472     {
473         switch( src.getType() ) {
474         case MessageBytes.T_BYTES:
475             type=T_BYTES;
476             ByteChunk bc=src.getByteChunk();
477             byteC.allocate( 2 * bc.getLength(), -1 );
478             byteC.append( bc );
479             break;
480         case MessageBytes.T_CHARS:
481             type=T_CHARS;
482             CharChunk cc=src.getCharChunk();
483             charC.allocate( 2 * cc.getLength(), -1 );
484             charC.append( cc );
485             break;
486         case MessageBytes.T_STR:
487             type=T_STR;
488             String sc=src.getString();
489             this.setString( sc );
490             break;
491         }
492         setCharset(src.getCharset());
493     }
494
495     // -------------------- Deprecated code --------------------
496     // efficient long
497     // XXX used only for headers - shouldn't be stored here.
498     private long longValue;
499     private boolean hasLongValue=false;
500
501     /**
502      * Set the buffer to the representation of a long.
503      * @param l The long
504      */

505     public void setLong(long l) {
506         byteC.allocate(32, 64);
507         long current = l;
508         byte[] buf = byteC.getBuffer();
509         int start = 0;
510         int end = 0;
511         if (l == 0) {
512             buf[end++] = (byte) '0';
513         }
514         if (l < 0) {
515             current = -l;
516             buf[end++] = (byte) '-';
517         }
518         while (current > 0) {
519             int digit = (int) (current % 10);
520             current = current / 10;
521             buf[end++] = HexUtils.getHex(digit);
522         }
523         byteC.setOffset(0);
524         byteC.setEnd(end);
525         // Inverting buffer
526         end--;
527         if (l < 0) {
528             start++;
529         }
530         while (end > start) {
531             byte temp = buf[start];
532             buf[start] = buf[end];
533             buf[end] = temp;
534             start++;
535             end--;
536         }
537         longValue=l;
538         hasStrValue=false;
539         hasHashCode=false;
540         hasLongValue=true;
541         type=T_BYTES;
542     }
543
544     // Used for headers conversion
545     /**
546      * Convert the buffer to a long, cache the value.
547      * @return the long value
548      */

549     public long getLong() {
550         if( hasLongValue ) {
551             return longValue;
552         }
553
554         switch (type) {
555         case T_BYTES:
556             longValue=byteC.getLong();
557             break;
558         default:
559             longValue=Long.parseLong(toString());
560         }
561
562         hasLongValue=true;
563         return longValue;
564
565      }
566
567     // -------------------- Future may be different --------------------
568
569     private static final MessageBytesFactory factory=new MessageBytesFactory();
570
571     private static class MessageBytesFactory {
572         protected MessageBytesFactory() {
573         }
574         public MessageBytes newInstance() {
575             return new MessageBytes();
576         }
577     }
578 }
579