org.apache.tomcat.util.buf.MessageBytes Maven / Gradle / Ivy
The newest version!
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 1997-2010 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* 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.apache.tomcat.util.buf;
import java.io.Serializable;
import java.io.IOException;
import java.nio.charset.Charset;
import java.text.DateFormat;
import java.util.Date;
/**
* 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 [email protected]
* @author James Todd [[email protected]]
* @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 */
public static final int T_STR = 1;
/** 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[] */
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 ByteChunk byteC=new ByteChunk();
private 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.
*/
public MessageBytes() {
}
/** Construct a new MessageBytes instance
*/
public static MessageBytes newInstance() {
return factory.newInstance();
}
/** 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.
*/
public void recycle() {
type=T_NULL;
byteC.recycle();
charC.recycle();
strValue=null;
caseSensitive=true;
hasStrValue=false;
hasHashCode=false;
hasIntValue=false;
hasLongValue=false;
hasDateValue=false;
}
/**
* 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;
hasDateValue=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.
*/
public void setCharset( Charset charset ) {
if( !byteC.isNull() ) {
// if the encoding changes we need to reset the converion results
charC.recycle();
hasStrValue=false;
}
byteC.setCharset(charset);
}
/**
* 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;
hasDateValue=false;
}
/** 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
*/
public void setString( String s ) {
strValue=s;
hasHashCode=false;
hasIntValue=false;
hasLongValue=false;
hasDateValue=false;
if (s == null) {
hasStrValue=false;
type=T_NULL;
} else {
hasStrValue=true;
type=T_STR;
}
}
// -------------------- Conversion and getters --------------------
/** Compute the string value
*/
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 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.
*/
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.
*/
public CharChunk getCharChunk() {
return charC;
}
/**
* 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.
*/
public void toBytes() {
if( ! byteC.isNull() ) {
type=T_BYTES;
return;
}
toString();
type=T_BYTES;
byte bb[] = strValue.getBytes();
byteC.setBytes(bb, 0, bb.length);
}
/** 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.
*/
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.
* @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:
if( strValue==null && s!=null) return false;
return 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.
* @param s the String to compare
* @return true if the comparison succeeded, false otherwise
*/
public boolean equalsIgnoreCase(String s) {
switch (type) {
case T_STR:
if( strValue==null && s!=null) return false;
return strValue.equalsIgnoreCase( s );
case T_CHARS:
return charC.equalsIgnoreCase( s );
case T_BYTES:
return byteC.equalsIgnoreCase( s );
default:
return false;
}
}
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.
* @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.
* @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 0) {
int digit = current % 10;
current = current / 10;
buf[end++] = HexUtils.HEX[digit];
}
byteC.setOffset(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;
hasDateValue=false;
type=T_BYTES;
}
/** 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.setOffset(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;
hasDateValue=false;
type=T_BYTES;
}
/**
* @deprecated The buffer are general purpose, caching for headers should
* be done in headers
*/
public long getTime() {
if( hasDateValue ) {
if( dateValue==null) return -1;
return dateValue.getTime();
}
long l=DateTool.parseDate( this );
if( dateValue==null)
dateValue=new Date(l);
else
dateValue.setTime(l);
hasDateValue=true;
return l;
}
// Used for headers conversion
/** 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
*/
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() {
}
public MessageBytes newInstance() {
return new MessageBytes();
}
}
}