/*
 * Copyright (c) 2010, 2017 Oracle and/or its affiliates. All rights reserved.
 * Copyright 2004 The Apache Software Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.glassfish.grizzly.http.util;


import java.io.IOException;
import java.io.Serializable;
import java.nio.charset.Charset;

This class is used to represent a subarray of bytes in an HTTP message. It represents all request/response elements. The byte/char conversions are delayed and cached. Everything is recyclable. The object can represent a byte[], a char[], or a (sub) String. All operations can be made in case sensitive mode or not.
Author:dac@eng.sun.com, James Todd [gonzo@eng.sun.com], Costin Manolache
/** * This class is used to represent a subarray of bytes in an HTTP message. * It represents all request/response elements. The byte/char conversions are * delayed and cached. Everything is recyclable. * * The object can represent a byte[], a char[], or a (sub) String. All * operations can be made in case sensitive mode or not. * * @author dac@eng.sun.com * @author James Todd [gonzo@eng.sun.com] * @author Costin Manolache */
public final class MessageBytes implements Cloneable, Serializable { // primary type ( whatever is set as original value ) private int type = T_NULL; public static final int T_NULL = 0;
getType() is T_STR if the the object used to create the MessageBytes was a String
/** getType() is T_STR if the the object used to create the MessageBytes * was a String */
public static final int T_STR = 1;
getType() is T_STR if the the object used to create the MessageBytes was a byte[]
/** getType() is T_STR if the the object used to create the MessageBytes * was a byte[] */
public static final int T_BYTES = 2;
getType() is T_STR if the the object used to create the MessageBytes was a char[]
/** getType() is T_STR if the the object used to create the MessageBytes * was a char[] */
public static final int T_CHARS = 3; private int hashCode=0; // did we computed the hashcode ? private boolean hasHashCode=false; // Is the represented object case sensitive ? private boolean caseSensitive=true; // Internal objects to represent array + offset, and specific methods private final ByteChunk byteC=new ByteChunk(); private final CharChunk charC=new CharChunk(); // String private String strValue; // true if a String value was computed. Probably not needed, // strValue!=null is the same private boolean hasStrValue=false;
Creates a new, uninitialized MessageBytes object.
Deprecated:Use static newInstance() in order to allow future hooks.
/** * Creates a new, uninitialized MessageBytes object. * @deprecated Use static newInstance() in order to allow * future hooks. */
public MessageBytes() { }
Construct a new MessageBytes instance
/** Construct a new MessageBytes instance */
public static MessageBytes newInstance() { return factory.newInstance(); }
Configure the case sensitivity
/** Configure the case sensitivity */
public void setCaseSenitive( boolean b ) { caseSensitive=b; } public MessageBytes getClone() { try { return (MessageBytes)this.clone(); } catch( Exception ex) { return null; } } public boolean isNull() { // should we check also hasStrValue ??? return byteC.isNull() && charC.isNull() && ! hasStrValue; // bytes==null && strValue==null; }
Resets the message bytes to an uninitialized (NULL) state.
/** * Resets the message bytes to an uninitialized (NULL) state. */
public void recycle() { type=T_NULL; byteC.recycle(); charC.recycle(); strValue=null; caseSensitive=true; hasStrValue=false; hasHashCode=false; hasIntValue=false; hasLongValue=false; }
Sets the content to the specified subarray of bytes.
Params:
  • b – the bytes
  • off – the start offset of the bytes
  • len – the length of the bytes
/** * Sets the content to the specified subarray of bytes. * * @param b the bytes * @param off the start offset of the bytes * @param len the length of the bytes */
public void setBytes(byte[] b, int off, int len) { byteC.setBytes( b, off, len ); type=T_BYTES; hasStrValue=false; hasHashCode=false; hasIntValue=false; hasLongValue=false; }
Set the encoding. If the object was constructed from bytes[]. any previous conversion is reset. If no encoding is set, we'll use 8859-1.
/** Set the encoding. If the object was constructed from bytes[]. any * previous conversion is reset. * If no encoding is set, we'll use 8859-1. */
public void setCharset( Charset enc ) { if( !byteC.isNull() ) { // if the encoding changes we need to reset the converion results charC.recycle(); hasStrValue=false; } byteC.setCharset(enc); }
Sets the content to be a char[]
Params:
  • c – the bytes
  • off – the start offset of the bytes
  • len – the length of the bytes
/** * Sets the content to be a char[] * * @param c the bytes * @param off the start offset of the bytes * @param len the length of the bytes */
public void setChars( char[] c, int off, int len ) { charC.setChars( c, off, len ); type=T_CHARS; hasStrValue=false; hasHashCode=false; hasIntValue=false; hasLongValue=false; }
Remove the cached string value. Use it after a conversion on the byte[] or after the encoding is changed XXX Is this needed ?
/** Remove the cached string value. Use it after a conversion on the * byte[] or after the encoding is changed * XXX Is this needed ? */
public void resetStringValue() { if( type != T_STR ) { // If this was cread as a byte[] or char[], we remove // the old string value hasStrValue=false; strValue=null; } }
Set the content to be a string
/** * Set the content to be a string */
public void setString( String s ) { strValue=s; hasHashCode=false; hasIntValue=false; hasLongValue=false; if (s == null) { hasStrValue=false; type=T_NULL; } else { hasStrValue=true; type=T_STR; } } // -------------------- Conversion and getters --------------------
Compute the string value
/** Compute the string value */
@Override public String toString() { if( hasStrValue ) return strValue; hasStrValue=true; switch (type) { case T_CHARS: strValue=charC.toString(); return strValue; case T_BYTES: strValue=byteC.toString(); return strValue; } return ""; } //----------------------------------------
Return the type of the original content. Can be T_STR, T_BYTES, T_CHARS or T_NULL
/** Return the type of the original content. Can be * T_STR, T_BYTES, T_CHARS or T_NULL */
public int getType() { return type; }
Returns the byte chunk, representing the byte[] and offset/length. Valid only if T_BYTES or after a conversion was made.
/** * Returns the byte chunk, representing the byte[] and offset/length. * Valid only if T_BYTES or after a conversion was made. */
public ByteChunk getByteChunk() { return byteC; }
Returns the char chunk, representing the char[] and offset/length. Valid only if T_CHARS or after a conversion was made.
/** * Returns the char chunk, representing the char[] and offset/length. * Valid only if T_CHARS or after a conversion was made. */
public CharChunk getCharChunk() { return charC; }
Returns the string value. Valid only if T_STR or after a conversion was made.
/** * Returns the string value. * Valid only if T_STR or after a conversion was made. */
public String getString() { return strValue; }
Unimplemented yet. Do a char->byte conversion.
/** Unimplemented yet. Do a char->byte conversion. */
public void toBytes() { if( ! byteC.isNull() ) { type=T_BYTES; return; } toString(); type=T_BYTES; byte bb[] = strValue.getBytes(byteC.getCharset()); byteC.setBytes(bb, 0, bb.length); }
Convert to char[] and fill the CharChunk. XXX Not optimized - it converts to String first.
/** Convert to char[] and fill the CharChunk. * XXX Not optimized - it converts to String first. */
public void toChars() { if( ! charC.isNull() ) { type=T_CHARS; return; } // inefficient toString(); type=T_CHARS; char cc[]=strValue.toCharArray(); charC.setChars(cc, 0, cc.length); }
Returns the length of the original buffer. Note that the length in bytes may be different from the length in chars.
/** * Returns the length of the original buffer. * Note that the length in bytes may be different from the length * in chars. */
public int getLength() { if(type==T_BYTES) return byteC.getLength(); if(type==T_CHARS) { return charC.getLength(); } if(type==T_STR) return strValue.length(); toString(); if( strValue==null ) return 0; return strValue.length(); } // -------------------- equals --------------------
Compares the message bytes to the specified String object.
Params:
  • s – the String to compare
Returns:true if the comparison succeeded, false otherwise
/** * Compares the message bytes to the specified String object. * @param s the String to compare * @return true if the comparison succeeded, false otherwise */
public boolean equals(String s) { if( s == null ) return false; if( ! caseSensitive ) return equalsIgnoreCase( s ); switch (type) { case T_STR: return strValue != null && strValue.equals(s); case T_CHARS: return charC.equals( s ); case T_BYTES: return byteC.equals( s ); default: return false; } }
Compares the message bytes to the specified String object.
Params:
  • s – the String to compare
Returns:true if the comparison succeeded, false otherwise
/** * Compares the message bytes to the specified String object. * @param s the String to compare * @return true if the comparison succeeded, false otherwise */
public boolean equalsIgnoreCase(String s) { if (s == null) { return false; } switch (type) { case T_STR: return strValue != null && strValue.equalsIgnoreCase(s); case T_CHARS: return charC.equalsIgnoreCase( s ); case T_BYTES: return byteC.equalsIgnoreCase( s ); default: return false; } } @Override public boolean equals(Object obj) { if (obj == null || (!(obj instanceof MessageBytes))) { return false; } MessageBytes mb = (MessageBytes) obj; switch (type) { case T_STR: return mb.equals( strValue ); } if( mb.type != T_CHARS && mb.type!= T_BYTES ) { // it's a string or int/date string value return equals( mb.toString() ); } // mb is either CHARS or BYTES. // this is either CHARS or BYTES // Deal with the 4 cases ( in fact 3, one is simetric) if( mb.type == T_CHARS && type==T_CHARS ) { return charC.equals( mb.charC ); } if( mb.type==T_BYTES && type== T_BYTES ) { return byteC.equals( mb.byteC ); } if( mb.type== T_CHARS && type== T_BYTES ) { return byteC.equals( mb.charC ); } if( mb.type== T_BYTES && type== T_CHARS ) { return mb.byteC.equals( charC ); } // can't happen return true; }
Returns true if the message bytes starts with the specified string.
Params:
  • s – the string
/** * Returns true if the message bytes starts with the specified string. * @param s the string */
public boolean startsWith(String s) { switch (type) { case T_STR: return strValue.startsWith( s ); case T_CHARS: return charC.startsWith( s ); case T_BYTES: return byteC.startsWith( s ); default: return false; } }
Returns true if the message bytes starts with the specified string.
Params:
  • s – the string
  • pos – The start position
/** * Returns true if the message bytes starts with the specified string. * @param s the string * @param pos The start position */
public boolean startsWithIgnoreCase(String s, int pos) { switch (type) { case T_STR: if( strValue==null ) return false; if( strValue.length() < pos + s.length() ) return false; for( int i=0; i<s.length(); i++ ) { if( Ascii.toLower( s.charAt( i ) ) != Ascii.toLower( strValue.charAt( pos + i ))) { return false; } } return true; case T_CHARS: return charC.startsWithIgnoreCase( s, pos ); case T_BYTES: return byteC.startsWithIgnoreCase( s, pos ); default: return false; } } // -------------------- Hash code -------------------- @Override public int hashCode() { if( hasHashCode ) return hashCode; int code; if( caseSensitive ) code=hash(); else code=hashIgnoreCase(); hashCode=code; hasHashCode=true; return code; } // normal hash. private int hash() { int code=0; switch (type) { case T_STR: // We need to use the same hash function for (int i = 0; i < strValue.length(); i++) { code = code * 37 + strValue.charAt( i ); } return code; case T_CHARS: return charC.hash(); case T_BYTES: return byteC.hash(); default: return 0; } } // hash ignoring case private int hashIgnoreCase() { int code=0; switch (type) { case T_STR: for (int i = 0; i < strValue.length(); i++) { code = code * 37 + Ascii.toLower(strValue.charAt( i )); } return code; case T_CHARS: return charC.hashIgnoreCase(); case T_BYTES: return byteC.hashIgnoreCase(); default: return 0; } } public int indexOf(char c) { return indexOf( c, 0); } // Inefficient initial implementation. Will be replaced on the next // round of tune-up public int indexOf(String s, int starting) { toString(); return strValue.indexOf( s, starting ); } // Inefficient initial implementation. Will be replaced on the next // round of tune-up public int indexOf(String s) { return indexOf( s, 0 ); } public int indexOfIgnoreCase(String s, int starting) { toString(); String upper=strValue.toUpperCase(); String sU=s.toUpperCase(); return upper.indexOf( sU, starting ); }
Returns true if the message bytes starts with the specified string.
Params:
  • c – the character
  • starting – The start position
/** * Returns true if the message bytes starts with the specified string. * @param c the character * @param starting The start position */
public int indexOf(char c, int starting) { switch (type) { case T_STR: return strValue.indexOf( c, starting ); case T_CHARS: return charC.indexOf( c, starting); case T_BYTES: return byteC.indexOf( c, starting ); default: return -1; } }
Copy the src into this MessageBytes, allocating more space if needed
/** Copy the src into this MessageBytes, allocating more space if * needed */
public void duplicate( MessageBytes src ) throws IOException { // START CR 6309514 // Discard previous state before duplicating recycle(); // END CR 6309514 switch( src.getType() ) { case MessageBytes.T_BYTES: type=T_BYTES; ByteChunk bc=src.getByteChunk(); byteC.allocate( 2 * bc.getLength(), -1 ); byteC.append( bc ); break; case MessageBytes.T_CHARS: type=T_CHARS; CharChunk cc=src.getCharChunk(); charC.allocate( 2 * cc.getLength(), -1 ); charC.append( cc ); break; case MessageBytes.T_STR: type=T_STR; String sc=src.getString(); this.setString( sc ); break; } } // -------------------- Deprecated code -------------------- // efficient int, long and date // XXX used only for headers - shouldn't be // stored here. private int intValue; private boolean hasIntValue=false; private long longValue; private boolean hasLongValue=false;
Set the buffer to the representation of an int
/** Set the buffer to the representation of an int */
public void setInt(int i) { byteC.allocate(16, 32); int current = i; byte[] buf = byteC.getBuffer(); int start = 0; int end = 0; if (i == 0) { buf[end++] = (byte) '0'; } if (i < 0) { current = -i; buf[end++] = (byte) '-'; } while (current > 0) { int digit = current % 10; current = current / 10; buf[end++] = HexUtils.HEX[digit]; } byteC.setStart(0); byteC.setEnd(end); // Inverting buffer end--; if (i < 0) { start++; } while (end > start) { byte temp = buf[start]; buf[start] = buf[end]; buf[end] = temp; start++; end--; } intValue=i; hasStrValue=false; hasHashCode=false; hasIntValue=true; hasLongValue=false; type=T_BYTES; }
Set the buffer to the representation of an long
/** Set the buffer to the representation of an long */
public void setLong(long l) { byteC.allocate(32, 64); long current = l; byte[] buf = byteC.getBuffer(); int start = 0; int end = 0; if (l == 0) { buf[end++] = (byte) '0'; } if (l < 0) { current = -l; buf[end++] = (byte) '-'; } while (current > 0) { int digit = (int) (current % 10); current = current / 10; buf[end++] = HexUtils.HEX[digit]; } byteC.setStart(0); byteC.setEnd(end); // Inverting buffer end--; if (l < 0) { start++; } while (end > start) { byte temp = buf[start]; buf[start] = buf[end]; buf[end] = temp; start++; end--; } longValue=l; hasStrValue=false; hasHashCode=false; hasIntValue=false; hasLongValue=true; type=T_BYTES; } // Used for headers conversion
Convert the buffer to an int, cache the value
/** Convert the buffer to an int, cache the value */
public int getInt() { if( hasIntValue ) return intValue; switch (type) { case T_BYTES: intValue=byteC.getInt(); break; default: intValue=Integer.parseInt(toString()); } hasIntValue=true; return intValue; } // Used for headers conversion
Convert the buffer to an long, cache the value
/** Convert the buffer to an long, cache the value */
public long getLong() { if( hasLongValue ) return longValue; switch (type) { case T_BYTES: longValue=byteC.getLong(); break; default: longValue=Long.parseLong(toString()); } hasLongValue=true; return longValue; } // -------------------- Future may be different -------------------- private static MessageBytesFactory factory=new MessageBytesFactory(); public static void setFactory( MessageBytesFactory mbf ) { factory=mbf; } public static class MessageBytesFactory { protected MessageBytesFactory() { } @SuppressWarnings({"deprecation"}) public MessageBytes newInstance() { return new MessageBytes(); } } }