/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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;
/** * 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. * <p> * 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
*/ publicfinalclass MessageBytes implements Cloneable, Serializable {
privatestaticfinallong serialVersionUID = 1L;
privatestaticfinal StringManager sm = StringManager.getManager(MessageBytes.class);
// primary type ( whatever is set as original value ) privateint type = T_NULL;
publicstaticfinalint T_NULL = 0; /** * getType() is T_STR if the the object used to create the MessageBytes was a String.
*/ publicstaticfinalint T_STR = 1; /** * getType() is T_BYTES if the the object used to create the MessageBytes was a byte[].
*/ publicstaticfinalint T_BYTES = 2; /** * getType() is T_CHARS if the the object used to create the MessageBytes was a char[].
*/ publicstaticfinalint T_CHARS = 3;
privateint hashCode = 0; // did we compute the hashcode ? privateboolean hasHashCode = false;
// Internal objects to represent array + offset, and specific methods privatefinal ByteChunk byteC = new ByteChunk(); privatefinal CharChunk charC = new CharChunk();
// String private String strValue;
/** * Creates a new, uninitialized MessageBytes object. Use static newInstance() in order to allow future hooks.
*/ private MessageBytes() {
}
/** * Construct a new MessageBytes instance. * * @return the instance
*/ publicstatic MessageBytes newInstance() { return factory.newInstance();
}
@Override public Object clone() throws CloneNotSupportedException { returnsuper.clone();
}
publicboolean isNull() { return type == T_NULL;
}
/** * Resets the message bytes to an uninitialized (NULL) state.
*/ publicvoid recycle() {
type = T_NULL;
byteC.recycle();
charC.recycle();
strValue = null;
hasHashCode = false;
hasLongValue = 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
*/ publicvoid setBytes(byte[] b, int off, int len) {
byteC.setBytes(b, off, len);
type = T_BYTES;
hasHashCode = false;
hasLongValue = false;
}
/** * Sets the content to be a char[] * * @param c the chars * @param off the start offset of the chars * @param len the length of the chars
*/ publicvoid setChars(char[] c, int off, int len) {
charC.setChars(c, off, len);
type = T_CHARS;
hasHashCode = false;
hasLongValue = false;
}
/** * Set the content to be a string * * @param s The string
*/ publicvoid setString(String s) {
strValue = s;
hasHashCode = false;
hasLongValue = false; if (s == null) {
type = T_NULL;
} else {
type = T_STR;
}
}
// -------------------- Conversion and getters --------------------
/** * Compute the string value. * * @return the string
*/
@Override public String toString() { switch (type) { case T_NULL: case T_STR: // No conversion required break; case T_BYTES:
strValue = byteC.toString(); break; case T_CHARS:
strValue = charC.toString(); break;
}
return strValue;
}
/** * Convert to String (if not already of the String type) and then return the String value. * * @return The current value as a String
*/ public String toStringType() { switch (type) { case T_NULL: case T_STR: // No conversion required break; case T_BYTES:
setString(byteC.toString()); break; case T_CHARS:
setString(charC.toString()); break;
}
return strValue;
}
// ---------------------------------------- /** * Return the type of the original content. Can be T_STR, T_BYTES, T_CHARS or T_NULL * * @return the type
*/ publicint getType() { return type;
}
/** * Returns the byte chunk, representing the byte[] and offset/length. Valid only if T_BYTES or after a conversion * was made. * * @return the byte chunk
*/ 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. * * @return the char chunk
*/ public CharChunk getCharChunk() { return charC;
}
/** * Returns the string value. Valid only if T_STR or after a conversion was made. * * @return the string
*/ public String getString() { return strValue;
}
/** * @return the Charset used for string<->byte conversions.
*/ public Charset getCharset() { return byteC.getCharset();
}
/** * Set the Charset used for string<->byte conversions. * * @param charset The charset
*/ publicvoid setCharset(Charset charset) {
byteC.setCharset(charset);
}
/** * Convert to bytes and fill the ByteChunk with the converted value.
*/ publicvoid toBytes() { if (type == T_NULL) {
byteC.recycle(); return;
}
if (type == T_BYTES) { // No conversion required return;
}
if (getCharset() == ByteChunk.DEFAULT_CHARSET) { if (type == T_CHARS) {
toBytesSimple(charC.getChars(), charC.getStart(), charC.getLength());
} else { // Must be T_STR char[] chars = strValue.toCharArray();
toBytesSimple(chars, 0, chars.length);
} return;
}
ByteBuffer bb; if (type == T_CHARS) {
bb = getCharset().encode(CharBuffer.wrap(charC));
} else { // Must be T_STR
bb = getCharset().encode(strValue);
}
/** * Simple conversion of chars to bytes. * * @throws IllegalArgumentException if any of the characters to convert are above code point 0xFF.
*/ privatevoid toBytesSimple(char[] chars, int start, int len) { byte[] bytes = newbyte[len];
for (int i = 0; i < len; i++) { if (chars[i + start] > 255) { thrownew IllegalArgumentException(sm.getString("messageBytes.illegalCharacter",
Character.toString(chars[i + start]), Integer.valueOf(chars[i + start])));
} else {
bytes[i] = (byte) chars[i + start];
}
}
byteC.setBytes(bytes, 0, len);
type = T_BYTES;
}
/** * Convert to char[] and fill the CharChunk. * <p> * Note: The conversion from bytes is not optimised - it converts to String first. However, Tomcat doesn't call this * method to convert from bytes so there is no benefit from optimising that path.
*/ publicvoid toChars() { switch (type) { case T_NULL:
charC.recycle(); //$FALL-THROUGH$ case T_CHARS: // No conversion required return; case T_BYTES:
toString(); //$FALL-THROUGH$ case T_STR: { char cc[] = strValue.toCharArray();
charC.setChars(cc, 0, cc.length);
}
}
}
/** * Returns the length of the original buffer. * <p> * Note: The length in bytes may be different from the length in chars. * * @return the length
*/ publicint 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();
}
/** * Compares the message bytes to the specified String object. * * @param s the String to compare * * @return <code>true</code> if the comparison succeeded, <code>false</code> otherwise
*/ publicboolean equals(String s) { switch (type) { case T_STR: if (strValue == null) { return s == null;
} return strValue.equals(s); case T_CHARS: return charC.equals(s); case T_BYTES: return byteC.equals(s); default: returnfalse;
}
}
/** * Compares the message bytes to the specified String object. * * @param s the String to compare * * @return <code>true</code> if the comparison succeeded, <code>false</code> otherwise
*/ publicboolean equalsIgnoreCase(String s) { switch (type) { case T_STR: if (strValue == null) { return s == null;
} return strValue.equalsIgnoreCase(s); case T_CHARS: return charC.equalsIgnoreCase(s); case T_BYTES: return byteC.equalsIgnoreCase(s); default: returnfalse;
}
}
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 symmetric)
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 returntrue;
}
/** * @return <code>true</code> if the message bytes starts with the specified string. * * @param s the string * @param pos The start position
*/ publicboolean startsWithIgnoreCase(String s, int pos) { switch (type) { case T_STR: if (strValue == null) { returnfalse;
} if (strValue.length() < pos + s.length()) { returnfalse;
}
for (int i = 0; i < s.length(); i++) { if (Ascii.toLower(s.charAt(i)) != Ascii.toLower(strValue.charAt(pos + i))) { returnfalse;
}
} returntrue; case T_CHARS: return charC.startsWithIgnoreCase(s, pos); case T_BYTES: return byteC.startsWithIgnoreCase(s, pos); default: returnfalse;
}
}
// -------------------- Hash code --------------------
@Override publicint hashCode() { if (hasHashCode) { return hashCode;
} int code = 0;
// normal hash. privateint 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;
}
}
// Inefficient initial implementation. Will be replaced on the next // round of tune-up publicint indexOf(String s, int starting) {
toString(); return strValue.indexOf(s, starting);
}
// Inefficient initial implementation. Will be replaced on the next // round of tune-up publicint indexOf(String s) { return indexOf(s, 0);
}
publicint indexOfIgnoreCase(String s, int starting) {
toString();
String upper = strValue.toUpperCase(Locale.ENGLISH);
String sU = s.toUpperCase(Locale.ENGLISH); return upper.indexOf(sU, starting);
}
/** * Copy the src into this MessageBytes, allocating more space if needed. * * @param src The source * * @throws IOException Writing overflow data to the output channel failed
*/ publicvoid duplicate(MessageBytes src) throws IOException { switch (src.getType()) { case T_BYTES:
type = T_BYTES;
ByteChunk bc = src.getByteChunk();
byteC.allocate(2 * bc.getLength(), -1);
byteC.append(bc); break; case T_CHARS:
type = T_CHARS;
CharChunk cc = src.getCharChunk();
charC.allocate(2 * cc.getLength(), -1);
charC.append(cc); break; case T_STR:
type = T_STR;
String sc = src.getString(); this.setString(sc); break;
}
setCharset(src.getCharset());
}
// efficient long privatelong longValue; privateboolean hasLongValue = false;
/** * Set the buffer to the representation of a long. * * @param l The long
*/ publicvoid 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.getHex(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;
hasHashCode = false;
hasLongValue = true;
type = T_BYTES;
}
/** * Convert the buffer to a long, cache the value. Used for headers conversion. * * @return the long value
*/ publiclong getLong() { if (hasLongValue) { return longValue;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.