Save This Page
Home » apache-tomcat-6.0.16-src » org.apache » tomcat » util » buf » [javadoc | source]
    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;
   23   import java.io.IOException;
   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       // 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 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 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 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 s ) {
  186           strValue=s;
  187           hasHashCode=false;
  188           hasIntValue=false;
  189           hasLongValue=false;
  190           hasDateValue=false; 
  191           if (s == null) {
  192               hasStrValue=false;
  193               type=T_NULL;
  194           } else {
  195               hasStrValue=true;
  196               type=T_STR;
  197           }
  198       }
  199   
  200       // -------------------- Conversion and getters --------------------
  201   
  202       /** Compute the string value
  203        */
  204       public String toString() {
  205           if( hasStrValue ) return strValue;
  206           
  207           switch (type) {
  208           case T_CHARS:
  209               strValue=charC.toString();
  210               hasStrValue=true;
  211               return strValue;
  212           case T_BYTES:
  213               strValue=byteC.toString();
  214               hasStrValue=true;
  215               return strValue;
  216           }
  217           return null;
  218       }
  219   
  220       //----------------------------------------
  221       /** Return the type of the original content. Can be
  222        *  T_STR, T_BYTES, T_CHARS or T_NULL
  223        */
  224       public int getType() {
  225   	return type;
  226       }
  227       
  228       /**
  229        * Returns the byte chunk, representing the byte[] and offset/length.
  230        * Valid only if T_BYTES or after a conversion was made.
  231        */
  232       public ByteChunk getByteChunk() {
  233   	return byteC;
  234       }
  235   
  236       /**
  237        * Returns the char chunk, representing the char[] and offset/length.
  238        * Valid only if T_CHARS or after a conversion was made.
  239        */
  240       public CharChunk getCharChunk() {
  241   	return charC;
  242       }
  243   
  244       /**
  245        * Returns the string value.
  246        * Valid only if T_STR or after a conversion was made.
  247        */
  248       public String getString() {
  249   	return strValue;
  250       }
  251   
  252       /** Unimplemented yet. Do a char->byte conversion.
  253        */
  254       public void toBytes() {
  255           if( ! byteC.isNull() ) {
  256               type=T_BYTES;
  257               return;
  258           }
  259           toString();
  260           type=T_BYTES;
  261           byte bb[] = strValue.getBytes();
  262           byteC.setBytes(bb, 0, bb.length);
  263       }
  264   
  265       /** Convert to char[] and fill the CharChunk.
  266        *  XXX Not optimized - it converts to String first.
  267        */
  268       public void toChars() {
  269   	if( ! charC.isNull() ) {
  270               type=T_CHARS;
  271   	    return;
  272   	}
  273   	// inefficient
  274   	toString();
  275           type=T_CHARS;
  276   	char cc[]=strValue.toCharArray();
  277   	charC.setChars(cc, 0, cc.length);
  278       }
  279       
  280   
  281       /**
  282        * Returns the length of the original buffer.
  283        * Note that the length in bytes may be different from the length
  284        * in chars.
  285        */
  286       public int getLength() {
  287   	if(type==T_BYTES)
  288   	    return byteC.getLength();
  289   	if(type==T_CHARS) {
  290   	    return charC.getLength();
  291   	}
  292   	if(type==T_STR)
  293   	    return strValue.length();
  294   	toString();
  295   	if( strValue==null ) return 0;
  296   	return strValue.length();
  297       }
  298   
  299       // -------------------- equals --------------------
  300   
  301       /**
  302        * Compares the message bytes to the specified String object.
  303        * @param s the String to compare
  304        * @return true if the comparison succeeded, false otherwise
  305        */
  306       public boolean equals(String s) {
  307   	if( ! caseSensitive )
  308   	    return equalsIgnoreCase( s );
  309   	switch (type) {
  310   	case T_STR:
  311   	    if( strValue==null && s!=null) return false;
  312   	    return strValue.equals( s );
  313   	case T_CHARS:
  314   	    return charC.equals( s );
  315   	case T_BYTES:
  316   	    return byteC.equals( s );
  317   	default:
  318   	    return false;
  319   	}
  320       }
  321   
  322       /**
  323        * Compares the message bytes to the specified String object.
  324        * @param s the String to compare
  325        * @return true if the comparison succeeded, false otherwise
  326        */
  327       public boolean equalsIgnoreCase(String s) {
  328   	switch (type) {
  329   	case T_STR:
  330   	    if( strValue==null && s!=null) return false;
  331   	    return strValue.equalsIgnoreCase( s );
  332   	case T_CHARS:
  333   	    return charC.equalsIgnoreCase( s );
  334   	case T_BYTES:
  335   	    return byteC.equalsIgnoreCase( s );
  336   	default:
  337   	    return false;
  338   	}
  339       }
  340   
  341       public boolean equals(MessageBytes mb) {
  342   	switch (type) {
  343   	case T_STR:
  344   	    return mb.equals( strValue );
  345   	}
  346   
  347   	if( mb.type != T_CHARS &&
  348   	    mb.type!= T_BYTES ) {
  349   	    // it's a string or int/date string value
  350   	    return equals( mb.toString() );
  351   	}
  352   
  353   	// mb is either CHARS or BYTES.
  354   	// this is either CHARS or BYTES
  355   	// Deal with the 4 cases ( in fact 3, one is simetric)
  356   	
  357   	if( mb.type == T_CHARS && type==T_CHARS ) {
  358   	    return charC.equals( mb.charC );
  359   	} 
  360   	if( mb.type==T_BYTES && type== T_BYTES ) {
  361   	    return byteC.equals( mb.byteC );
  362   	}
  363   	if( mb.type== T_CHARS && type== T_BYTES ) {
  364   	    return byteC.equals( mb.charC );
  365   	}
  366   	if( mb.type== T_BYTES && type== T_CHARS ) {
  367   	    return mb.byteC.equals( charC );
  368   	}
  369   	// can't happen
  370   	return true;
  371       }
  372   
  373       
  374       /**
  375        * Returns true if the message bytes starts with the specified string.
  376        * @param s the string
  377        */
  378       public boolean startsWith(String s) {
  379   	switch (type) {
  380   	case T_STR:
  381   	    return strValue.startsWith( s );
  382   	case T_CHARS:
  383   	    return charC.startsWith( s );
  384   	case T_BYTES:
  385   	    return byteC.startsWith( s );
  386   	default:
  387   	    return false;
  388   	}
  389       }
  390   
  391       /**
  392        * Returns true if the message bytes starts with the specified string.
  393        * @param s the string
  394        * @param pos The start position
  395        */
  396       public boolean startsWithIgnoreCase(String s, int pos) {
  397   	switch (type) {
  398   	case T_STR:
  399   	    if( strValue==null ) return false;
  400   	    if( strValue.length() < pos + s.length() ) return false;
  401   	    
  402   	    for( int i=0; i<s.length(); i++ ) {
  403   		if( Ascii.toLower( s.charAt( i ) ) !=
  404   		    Ascii.toLower( strValue.charAt( pos + i ))) {
  405   		    return false;
  406   		}
  407   	    }
  408   	    return true;
  409   	case T_CHARS:
  410   	    return charC.startsWithIgnoreCase( s, pos );
  411   	case T_BYTES:
  412   	    return byteC.startsWithIgnoreCase( s, pos );
  413   	default:
  414   	    return false;
  415   	}
  416       }
  417   
  418       
  419   
  420       // -------------------- Hash code  --------------------
  421       public  int hashCode() {
  422   	if( hasHashCode ) return hashCode;
  423   	int code = 0;
  424   
  425   	if( caseSensitive ) 
  426   	    code=hash(); 
  427   	else
  428   	    code=hashIgnoreCase();
  429   	hashCode=code;
  430   	hasHashCode=true;
  431   	return code;
  432       }
  433   
  434       // normal hash. 
  435       private int hash() {
  436   	int code=0;
  437   	switch (type) {
  438   	case T_STR:
  439   	    // We need to use the same hash function
  440   	    for (int i = 0; i < strValue.length(); i++) {
  441   		code = code * 37 + strValue.charAt( i );
  442   	    }
  443   	    return code;
  444   	case T_CHARS:
  445   	    return charC.hash();
  446   	case T_BYTES:
  447   	    return byteC.hash();
  448   	default:
  449   	    return 0;
  450   	}
  451       }
  452   
  453       // hash ignoring case
  454       private int hashIgnoreCase() {
  455   	int code=0;
  456   	switch (type) {
  457   	case T_STR:
  458   	    for (int i = 0; i < strValue.length(); i++) {
  459   		code = code * 37 + Ascii.toLower(strValue.charAt( i ));
  460   	    }
  461   	    return code;
  462   	case T_CHARS:
  463   	    return charC.hashIgnoreCase();
  464   	case T_BYTES:
  465   	    return byteC.hashIgnoreCase();
  466   	default:
  467   	    return 0;
  468   	}
  469       }
  470   
  471       public int indexOf(char c) {
  472   	return indexOf( c, 0);
  473       }
  474   
  475       // Inefficient initial implementation. Will be replaced on the next
  476       // round of tune-up
  477       public int indexOf(String s, int starting) {
  478   	toString();
  479   	return strValue.indexOf( s, starting );
  480       }
  481       
  482       // Inefficient initial implementation. Will be replaced on the next
  483       // round of tune-up
  484       public int indexOf(String s) {
  485   	return indexOf( s, 0 );
  486       }
  487       
  488       public int indexOfIgnoreCase(String s, int starting) {
  489   	toString();
  490   	String upper=strValue.toUpperCase();
  491   	String sU=s.toUpperCase();
  492   	return upper.indexOf( sU, starting );
  493       }
  494       
  495       /**
  496        * Returns true if the message bytes starts with the specified string.
  497        * @param c the character
  498        * @param starting The start position
  499        */
  500       public int indexOf(char c, int starting) {
  501   	switch (type) {
  502   	case T_STR:
  503   	    return strValue.indexOf( c, starting );
  504   	case T_CHARS:
  505   	    return charC.indexOf( c, starting);
  506   	case T_BYTES:
  507   	    return byteC.indexOf( c, starting );
  508   	default:
  509   	    return -1;
  510   	}
  511       }
  512   
  513       /** Copy the src into this MessageBytes, allocating more space if
  514        *  needed
  515        */
  516       public void duplicate( MessageBytes src ) throws IOException
  517       {
  518   	switch( src.getType() ) {
  519   	case MessageBytes.T_BYTES:
  520   	    type=T_BYTES;
  521   	    ByteChunk bc=src.getByteChunk();
  522   	    byteC.allocate( 2 * bc.getLength(), -1 );
  523   	    byteC.append( bc );
  524   	    break;
  525   	case MessageBytes.T_CHARS:
  526   	    type=T_CHARS;
  527   	    CharChunk cc=src.getCharChunk();
  528   	    charC.allocate( 2 * cc.getLength(), -1 );
  529   	    charC.append( cc );
  530   	    break;
  531   	case MessageBytes.T_STR:
  532   	    type=T_STR;
  533   	    String sc=src.getString();
  534   	    this.setString( sc );
  535   	    break;
  536   	}
  537       }
  538   
  539       // -------------------- Deprecated code --------------------
  540       // efficient int, long and date
  541       // XXX used only for headers - shouldn't be
  542       // stored here.
  543       private int intValue;
  544       private boolean hasIntValue=false;
  545       private long longValue;
  546       private boolean hasLongValue=false;
  547       private Date dateValue;
  548       private boolean hasDateValue=false;
  549       
  550       /**
  551        *  @deprecated The buffer are general purpose, caching for headers should
  552        *  be done in headers. The second parameter allows us to pass a date format
  553        * instance to avoid synchronization problems.
  554        */
  555       public void setTime(long t, DateFormat df) {
  556   	// XXX replace it with a byte[] tool
  557   	recycle();
  558   	if( dateValue==null)
  559   	    dateValue=new Date(t);
  560   	else
  561   	    dateValue.setTime(t);
  562   	if( df==null )
  563   	    strValue=DateTool.format1123(dateValue);
  564   	else
  565   	    strValue=DateTool.format1123(dateValue,df);
  566   	hasStrValue=true;
  567   	hasDateValue=true;
  568   	type=T_STR;   
  569       }
  570   
  571       public void setTime(long t) {
  572   	setTime( t, null );
  573       }
  574   
  575       /** Set the buffer to the representation of an int
  576        */
  577       public void setInt(int i) {
  578           byteC.allocate(16, 32);
  579           int current = i;
  580           byte[] buf = byteC.getBuffer();
  581           int start = 0;
  582           int end = 0;
  583           if (i == 0) {
  584               buf[end++] = (byte) '0';
  585           }
  586           if (i < 0) {
  587               current = -i;
  588               buf[end++] = (byte) '-';
  589           }
  590           while (current > 0) {
  591               int digit = current % 10;
  592               current = current / 10;
  593               buf[end++] = HexUtils.HEX[digit];
  594           }
  595           byteC.setOffset(0);
  596           byteC.setEnd(end);
  597           // Inverting buffer
  598           end--;
  599           if (i < 0) {
  600               start++;
  601           }
  602           while (end > start) {
  603               byte temp = buf[start];
  604               buf[start] = buf[end];
  605               buf[end] = temp;
  606               start++;
  607               end--;
  608           }
  609           intValue=i;
  610           hasStrValue=false;
  611           hasHashCode=false;
  612           hasIntValue=true;
  613           hasLongValue=false;
  614           hasDateValue=false; 
  615           type=T_BYTES;
  616       }
  617   
  618       /** Set the buffer to the representation of an long
  619        */
  620       public void setLong(long l) {
  621           byteC.allocate(32, 64);
  622           long current = l;
  623           byte[] buf = byteC.getBuffer();
  624           int start = 0;
  625           int end = 0;
  626           if (l == 0) {
  627               buf[end++] = (byte) '0';
  628           }
  629           if (l < 0) {
  630               current = -l;
  631               buf[end++] = (byte) '-';
  632           }
  633           while (current > 0) {
  634               int digit = (int) (current % 10);
  635               current = current / 10;
  636               buf[end++] = HexUtils.HEX[digit];
  637           }
  638           byteC.setOffset(0);
  639           byteC.setEnd(end);
  640           // Inverting buffer
  641           end--;
  642           if (l < 0) {
  643               start++;
  644           }
  645           while (end > start) {
  646               byte temp = buf[start];
  647               buf[start] = buf[end];
  648               buf[end] = temp;
  649               start++;
  650               end--;
  651           }
  652           longValue=l;
  653           hasStrValue=false;
  654           hasHashCode=false;
  655           hasIntValue=false;
  656           hasLongValue=true;
  657           hasDateValue=false; 
  658           type=T_BYTES;
  659       }
  660   
  661       /**
  662        *  @deprecated The buffer are general purpose, caching for headers should
  663        *  be done in headers
  664        */
  665       public  long getTime()
  666       {
  667        	if( hasDateValue ) {
  668   	    if( dateValue==null) return -1;
  669   	    return dateValue.getTime();
  670        	}
  671   	
  672        	long l=DateTool.parseDate( this );
  673        	if( dateValue==null)
  674        	    dateValue=new Date(l);
  675        	else
  676        	    dateValue.setTime(l);
  677        	hasDateValue=true;
  678        	return l;
  679       }
  680       
  681   
  682       // Used for headers conversion
  683       /** Convert the buffer to an int, cache the value
  684        */ 
  685       public int getInt() 
  686       {
  687   	if( hasIntValue )
  688   	    return intValue;
  689   	
  690   	switch (type) {
  691   	case T_BYTES:
  692   	    intValue=byteC.getInt();
  693   	    break;
  694   	default:
  695   	    intValue=Integer.parseInt(toString());
  696   	}
  697   	hasIntValue=true;
  698   	return intValue;
  699       }
  700   
  701       // Used for headers conversion
  702       /** Convert the buffer to an long, cache the value
  703        */ 
  704       public long getLong() {
  705           if( hasLongValue )
  706               return longValue;
  707           
  708           switch (type) {
  709           case T_BYTES:
  710               longValue=byteC.getLong();
  711               break;
  712           default:
  713               longValue=Long.parseLong(toString());
  714           }
  715   
  716           hasLongValue=true;
  717           return longValue;
  718   
  719        }
  720   
  721       // -------------------- Future may be different --------------------
  722       
  723       private static MessageBytesFactory factory=new MessageBytesFactory();
  724   
  725       public static void setFactory( MessageBytesFactory mbf ) {
  726   	factory=mbf;
  727       }
  728       
  729       public static class MessageBytesFactory {
  730   	protected MessageBytesFactory() {
  731   	}
  732   	public MessageBytes newInstance() {
  733   	    return new MessageBytes();
  734   	}
  735       }
  736   }

Save This Page
Home » apache-tomcat-6.0.16-src » org.apache » tomcat » util » buf » [javadoc | source]