package org.glassfish.grizzly.http.util;
import java.io.CharConversionException;
import java.io.IOException;
import java.nio.charset.Charset;
import org.glassfish.grizzly.Buffer;
public class DataChunk implements Chunk {
public enum Type {
None, Bytes, Buffer, Chars, String
}
public static DataChunk newInstance() {
return newInstance(new ByteChunk(), new BufferChunk(), new CharChunk(), null);
}
public static DataChunk newInstance(final ByteChunk byteChunk, final BufferChunk bufferChunk, final CharChunk charChunk, final String stringValue) {
return new DataChunk(byteChunk, bufferChunk, charChunk, stringValue);
}
Type type = Type.None;
final ByteChunk byteChunk;
final BufferChunk bufferChunk;
final CharChunk charChunk;
String stringValue;
protected DataChunk() {
this(new ByteChunk(), new BufferChunk(), new CharChunk(), null);
}
protected DataChunk(final ByteChunk byteChunk, final BufferChunk bufferChunk, final CharChunk charChunk, final String stringValue) {
this.byteChunk = byteChunk;
this.bufferChunk = bufferChunk;
this.charChunk = charChunk;
this.stringValue = stringValue;
}
public DataChunk toImmutable() {
return new Immutable(this);
}
public Type getType() {
return type;
}
public void set(final DataChunk value) {
if (value == null) {
return;
}
switch (value.getType()) {
case Bytes: {
final ByteChunk anotherByteChunk = value.byteChunk;
setBytesInternal(anotherByteChunk.getBytes(), anotherByteChunk.getStart(), anotherByteChunk.getEnd());
break;
}
case Buffer: {
final BufferChunk anotherBufferChunk = value.bufferChunk;
setBufferInternal(anotherBufferChunk.getBuffer(), anotherBufferChunk.getStart(), anotherBufferChunk.getEnd());
break;
}
case String: {
setStringInternal(value.stringValue);
break;
}
case Chars: {
final CharChunk anotherCharChunk = value.charChunk;
setCharsInternal(anotherCharChunk.getChars(), anotherCharChunk.getStart(), anotherCharChunk.getLimit());
break;
}
}
}
public void set(final DataChunk value, final int start, final int end) {
switch (value.getType()) {
case Bytes: {
final ByteChunk anotherByteChunk = value.byteChunk;
setBytesInternal(anotherByteChunk.getBytes(), start, end);
break;
}
case Buffer: {
final BufferChunk anotherBufferChunk = value.bufferChunk;
setBufferInternal(anotherBufferChunk.getBuffer(), start, end);
break;
}
case String: {
setStringInternal(value.stringValue.substring(start, end));
break;
}
case Chars: {
final CharChunk anotherCharChunk = value.charChunk;
setCharsInternal(anotherCharChunk.getChars(), start, end);
break;
}
}
}
public void notifyDirectUpdate() {
switch (type) {
case Bytes:
byteChunk.notifyDirectUpdate();
return;
case Buffer:
bufferChunk.notifyDirectUpdate();
return;
case Chars:
charChunk.notifyDirectUpdate();
}
}
public BufferChunk getBufferChunk() {
return bufferChunk;
}
public void setBuffer(final Buffer buffer, final int position, final int limit) {
setBufferInternal(buffer, position, limit);
}
public void setBuffer(final Buffer buffer) {
setBufferInternal(buffer, buffer.position(), buffer.limit());
}
public CharChunk getCharChunk() {
return charChunk;
}
public void setChars(final char[] chars, final int position, final int limit) {
setCharsInternal(chars, position, limit);
}
public ByteChunk getByteChunk() {
return byteChunk;
}
public void setBytes(final byte[] bytes) {
setBytesInternal(bytes, 0, bytes.length);
}
public void setBytes(final byte[] bytes, final int position, final int limit) {
setBytesInternal(bytes, position, limit);
}
public void setString(String string) {
setStringInternal(string);
}
public void trimLeft() {
switch (getType()) {
case Bytes:
getByteChunk().trimLeft();
break;
case Buffer:
getBufferChunk().trimLeft();
break;
case Chars:
getCharChunk().trimLeft();
}
}
public void duplicate(final DataChunk src) {
switch (src.getType()) {
case Bytes: {
final ByteChunk bc = src.getByteChunk();
byteChunk.allocate(2 * bc.getLength(), -1);
try {
byteChunk.append(bc);
} catch (IOException ignored) {
}
switchToByteChunk();
break;
}
case Buffer: {
final BufferChunk bc = src.getBufferChunk();
bufferChunk.allocate(2 * bc.getLength());
bufferChunk.append(bc);
switchToBufferChunk();
break;
}
case Chars: {
final CharChunk cc = src.getCharChunk();
charChunk.allocate(2 * cc.getLength(), -1);
try {
charChunk.append(cc);
} catch (IOException ignored) {
}
switchToCharChunk();
break;
}
case String: {
setString(src.toString());
break;
}
default: {
recycle();
}
}
}
public void toChars(final Charset charset) throws CharConversionException {
switch (type) {
case Bytes:
charChunk.set(byteChunk, charset);
setChars(charChunk.getChars(), charChunk.getStart(), charChunk.getEnd());
return;
case Buffer:
charChunk.set(bufferChunk, charset);
setChars(charChunk.getChars(), charChunk.getStart(), charChunk.getEnd());
return;
case String:
charChunk.recycle();
try {
charChunk.append(stringValue);
} catch (IOException e) {
throw new IllegalStateException("Unexpected exception");
}
setChars(charChunk.getChars(), charChunk.getStart(), charChunk.getEnd());
return;
case Chars:
return;
default:
charChunk.recycle();
}
}
@Override
public String toString() {
return toString(null);
}
public String toString(Charset charset) {
switch (type) {
case Bytes:
return byteChunk.toString(charset);
case Buffer:
return bufferChunk.toString(charset);
case String:
return stringValue;
case Chars:
return charChunk.toString();
default:
return null;
}
}
@Override
public int getLength() {
switch (type) {
case Bytes:
return byteChunk.getLength();
case Buffer:
return bufferChunk.getLength();
case String:
return stringValue.length();
case Chars:
return charChunk.getLength();
default:
return 0;
}
}
@Override
public int getStart() {
switch (type) {
case Bytes:
return byteChunk.getStart();
case Buffer:
return bufferChunk.getStart();
case Chars:
return charChunk.getStart();
default:
return 0;
}
}
@Override
public void setStart(int start) {
switch (type) {
case Bytes:
byteChunk.setStart(start);
break;
case Buffer:
bufferChunk.setStart(start);
break;
case Chars:
charChunk.setStart(start);
break;
default:
break;
}
}
@Override
public int getEnd() {
switch (type) {
case Bytes:
return byteChunk.getEnd();
case Buffer:
return bufferChunk.getEnd();
case Chars:
return charChunk.getEnd();
default:
return stringValue.length();
}
}
@Override
public void setEnd(int end) {
switch (type) {
case Bytes:
byteChunk.setEnd(end);
break;
case Buffer:
bufferChunk.setEnd(end);
break;
case Chars:
charChunk.setEnd(end);
break;
default:
break;
}
}
@Override
public final int indexOf(final char c, final int fromIndex) {
switch (type) {
case Bytes:
return byteChunk.indexOf(c, fromIndex);
case Buffer:
return bufferChunk.indexOf(c, fromIndex);
case String:
return stringValue.indexOf(c, fromIndex);
case Chars:
return charChunk.indexOf(c, fromIndex);
default:
return -1;
}
}
@Override
public final int indexOf(final String s, final int fromIndex) {
switch (type) {
case Bytes:
return byteChunk.indexOf(s, fromIndex);
case Buffer:
return bufferChunk.indexOf(s, fromIndex);
case String:
return stringValue.indexOf(s, fromIndex);
case Chars:
return charChunk.indexOf(s, fromIndex);
default:
return -1;
}
}
@Override
public final void delete(final int from, final int to) {
switch (type) {
case Bytes:
byteChunk.delete(from, to);
return;
case Buffer:
bufferChunk.delete(from, to);
return;
case String:
stringValue = stringValue.substring(0, from) + stringValue.substring(to, stringValue.length());
return;
case Chars:
charChunk.delete(from, to);
}
}
@Override
public java.lang.String toString(final int start, final int end) {
switch (type) {
case Bytes:
return byteChunk.toString(start, end);
case Buffer:
return bufferChunk.toString(start, end);
case String:
return start == 0 && end == stringValue.length() ? stringValue : stringValue.substring(start, end);
case Chars:
return charChunk.toString(start, end);
default:
return null;
}
}
@Override
public boolean equals(final Object object) {
if (!(object instanceof DataChunk)) {
return false;
}
final DataChunk anotherChunk = (DataChunk) object;
if (isNull() || anotherChunk.isNull()) {
return isNull() == anotherChunk.isNull();
}
switch (type) {
case Bytes:
return anotherChunk.equals(byteChunk);
case Buffer:
return anotherChunk.equals(bufferChunk);
case String:
return anotherChunk.equals(stringValue);
case Chars:
return anotherChunk.equals(charChunk);
default:
return false;
}
}
public boolean equals(final String s) {
switch (type) {
case Bytes:
return byteChunk.equals(s);
case Buffer:
return bufferChunk.equals(s);
case String:
return stringValue.equals(s);
case Chars:
return charChunk.equals(s);
default:
return false;
}
}
public boolean equals(final ByteChunk byteChunkToCheck) {
return equals(byteChunkToCheck.getBuffer(), byteChunkToCheck.getStart(), byteChunkToCheck.getLength());
}
public boolean equals(final BufferChunk bufferChunkToCheck) {
switch (type) {
case Bytes:
return bufferChunkToCheck.equals(byteChunk.getBuffer(), byteChunk.getStart(), byteChunk.getLength());
case Buffer:
return bufferChunkToCheck.equals(bufferChunk);
case String:
return bufferChunkToCheck.equals(stringValue);
case Chars:
return bufferChunkToCheck.equals(charChunk.getBuffer(), charChunk.getStart(), charChunk.getLength());
default:
return false;
}
}
public boolean equals(final CharChunk charChunkToCheck) {
switch (type) {
case Bytes:
return charChunkToCheck.equals(byteChunk.getBuffer(), byteChunk.getStart(), byteChunk.getLength());
case Buffer:
return bufferChunk.equals(charChunkToCheck.getBuffer(), charChunkToCheck.getStart(), charChunkToCheck.getLength());
case String:
return charChunkToCheck.equals(stringValue);
case Chars:
return charChunk.equals(charChunkToCheck.getBuffer(), charChunkToCheck.getStart(), charChunkToCheck.getLength());
default:
return false;
}
}
public boolean equals(final byte[] bytes) {
return equals(bytes, 0, bytes.length);
}
public boolean equals(final byte[] bytes, final int start, final int len) {
switch (type) {
case Bytes:
return byteChunk.equals(bytes, start, len);
case Buffer:
return bufferChunk.equals(bytes, start, len);
case String:
return ByteChunk.equals(bytes, start, len, stringValue);
case Chars:
return charChunk.equals(bytes, start, len);
default:
return false;
}
}
public boolean equalsIgnoreCase(final Object object) {
if (!(object instanceof DataChunk)) {
return false;
}
final DataChunk anotherChunk = (DataChunk) object;
if (isNull() || anotherChunk.isNull()) {
return isNull() == anotherChunk.isNull();
}
switch (type) {
case Bytes:
return anotherChunk.equalsIgnoreCase(byteChunk);
case Buffer:
return anotherChunk.equalsIgnoreCase(bufferChunk);
case String:
return anotherChunk.equalsIgnoreCase(stringValue);
case Chars:
return anotherChunk.equalsIgnoreCase(charChunk);
default:
return false;
}
}
public boolean equalsIgnoreCase(final String s) {
switch (type) {
case Bytes:
return byteChunk.equalsIgnoreCase(s);
case Buffer:
return bufferChunk.equalsIgnoreCase(s);
case String:
return stringValue.equalsIgnoreCase(s);
case Chars:
return charChunk.equalsIgnoreCase(s);
default:
return false;
}
}
public boolean equalsIgnoreCase(final ByteChunk byteChunkToCheck) {
return equalsIgnoreCase(byteChunkToCheck.getBuffer(), byteChunkToCheck.getStart(), byteChunkToCheck.getLength());
}
public boolean equalsIgnoreCase(final BufferChunk bufferChunkToCheck) {
switch (type) {
case Bytes:
return bufferChunkToCheck.equalsIgnoreCase(byteChunk.getBuffer(), byteChunk.getStart(), byteChunk.getLength());
case Buffer:
return bufferChunkToCheck.equalsIgnoreCase(bufferChunk);
case String:
return bufferChunkToCheck.equalsIgnoreCase(stringValue);
case Chars:
return bufferChunkToCheck.equalsIgnoreCase(charChunk.getBuffer(), charChunk.getStart(), charChunk.getLength());
default:
return false;
}
}
public boolean equalsIgnoreCase(final CharChunk charChunkToCheck) {
switch (type) {
case Bytes:
return charChunkToCheck.equalsIgnoreCase(byteChunk.getBuffer(), byteChunk.getStart(), byteChunk.getLength());
case Buffer:
return bufferChunk.equalsIgnoreCase(charChunkToCheck.getBuffer(), charChunkToCheck.getStart(), charChunkToCheck.getLength());
case String:
return charChunkToCheck.equalsIgnoreCase(stringValue);
case Chars:
return charChunk.equalsIgnoreCase(charChunkToCheck.getBuffer(), charChunkToCheck.getStart(), charChunkToCheck.getLength());
default:
return false;
}
}
public boolean equalsIgnoreCase(final byte[] bytes) {
return equalsIgnoreCase(bytes, 0, bytes.length);
}
public boolean equalsIgnoreCase(final byte[] bytes, final int start, final int len) {
switch (type) {
case Bytes:
return byteChunk.equalsIgnoreCase(bytes, start, len);
case Buffer:
return bufferChunk.equalsIgnoreCase(bytes, start, len);
case String:
return ByteChunk.equalsIgnoreCase(bytes, start, len, stringValue);
case Chars:
return charChunk.equalsIgnoreCase(bytes, start, len);
default:
return false;
}
}
@Override
public int hashCode() {
switch (type) {
case Bytes:
return byteChunk.hash();
case Buffer:
return bufferChunk.hash();
case String:
return stringValue.hashCode();
case Chars:
return charChunk.hash();
default:
return 0;
}
}
public final boolean equalsIgnoreCaseLowerCase(final byte[] b) {
switch (type) {
case Bytes:
return byteChunk.equalsIgnoreCaseLowerCase(b);
case Buffer:
return bufferChunk.equalsIgnoreCaseLowerCase(b);
case String:
return equalsIgnoreCaseLowerCase(stringValue, b);
case Chars:
return charChunk.equalsIgnoreCaseLowerCase(b);
default:
return false;
}
}
public final boolean startsWith(final String s, final int pos) {
switch (type) {
case Bytes:
return byteChunk.startsWith(s, pos);
case Buffer:
return bufferChunk.startsWith(s, pos);
case String:
if (stringValue.length() < pos + s.length()) {
return false;
}
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) != stringValue.charAt(pos + i)) {
return false;
}
}
return true;
case Chars:
return charChunk.startsWith(s, pos);
default:
return false;
}
}
public final boolean startsWithIgnoreCase(final String s, final int pos) {
switch (type) {
case Bytes:
return byteChunk.startsWithIgnoreCase(s, pos);
case Buffer:
return bufferChunk.startsWithIgnoreCase(s, pos);
case String:
if (stringValue.length() < pos + s.length()) {
return false;
}
for (int i = 0; i < s.length(); i++) {
if (Ascii.toLower(s.charAt(i)) != Ascii.toLower(stringValue.charAt(pos + i))) {
return false;
}
}
return true;
case Chars:
return charChunk.startsWithIgnoreCase(s, pos);
default:
return false;
}
}
public final boolean isNull() {
return type == Type.None || byteChunk.isNull() && bufferChunk.isNull() && stringValue == null && charChunk.isNull();
}
protected void resetBuffer() {
bufferChunk.recycle();
}
protected void resetCharChunk() {
charChunk.recycle();
}
protected void resetByteChunk() {
byteChunk.recycleAndReset();
}
protected void resetString() {
stringValue = null;
}
protected void reset() {
stringValue = null;
if (type == Type.Bytes) {
byteChunk.recycleAndReset();
} else if (type == Type.Buffer) {
bufferChunk.recycle();
} else if (type == Type.Chars) {
charChunk.recycle();
}
type = Type.None;
}
public void recycle() {
reset();
}
private static boolean equalsIgnoreCase(String s, byte[] b) {
final int len = b.length;
if (s.length() != len) {
return false;
}
for (int i = 0; i < len; i++) {
if (Ascii.toLower(s.charAt(i)) != Ascii.toLower(b[i])) {
return false;
}
}
return true;
}
private static boolean equalsIgnoreCaseLowerCase(final String s, final byte[] b) {
final int len = b.length;
if (s.length() != len) {
return false;
}
for (int i = 0; i < len; i++) {
if (Ascii.toLower(s.charAt(i)) != b[i]) {
return false;
}
}
return true;
}
private void setBytesInternal(final byte[] array, final int position, final int limit) {
byteChunk.setBytes(array, position, limit - position);
switchToByteChunk();
}
private void setBufferInternal(final Buffer buffer, final int position, final int limit) {
bufferChunk.setBufferChunk(buffer, position, limit, limit);
switchToBufferChunk();
}
private void setCharsInternal(final char[] chars, final int position, final int limit) {
charChunk.setChars(chars, position, limit - position);
switchToCharChunk();
}
private void setStringInternal(String string) {
stringValue = string;
switchToString();
}
private void switchToByteChunk() {
if (type == Type.Buffer) {
resetBuffer();
} else if (type == Type.Chars) {
resetCharChunk();
}
resetString();
type = Type.Bytes;
}
private void switchToBufferChunk() {
if (type == Type.Bytes) {
resetByteChunk();
} else if (type == Type.Chars) {
resetCharChunk();
}
resetString();
type = Type.Buffer;
}
private void switchToCharChunk() {
if (type == Type.Bytes) {
resetByteChunk();
} else if (type == Type.Buffer) {
resetBuffer();
}
resetString();
type = Type.Chars;
}
private void switchToString() {
if (type == Type.Bytes) {
resetByteChunk();
} else if (type == Type.Chars) {
resetCharChunk();
} else if (type == Type.Buffer) {
resetBuffer();
}
type = Type.String;
}
final static class Immutable extends DataChunk {
public Immutable(DataChunk original) {
super.set(original);
}
@Override
public DataChunk toImmutable() {
return this;
}
@Override
public void set(DataChunk value) {
}
@Override
public void setBuffer(Buffer buffer, int start, int end) {
}
@Override
public void setString(String string) {
}
@Override
public void setChars(char[] chars, int position, int limit) {
}
@Override
protected void resetBuffer() {
}
@Override
protected void resetString() {
}
@Override
protected void resetCharChunk() {
}
@Override
protected void reset() {
}
@Override
public void recycle() {
}
}
}