Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
package org.codehaus.jackson.util;
import java.io.*;
import java.math.BigDecimal;
import java.util.ArrayList;
/**
* TextBuffer is a class similar to {@link StringBuffer}, with
* following differences:
*
*
TextBuffer uses segments character arrays, to avoid having
* to do additional array copies when array is not big enough.
* This means that only reallocating that is necessary is done only once:
* if and when caller
* wants to access contents in a linear array (char[], String).
*
*
TextBuffer can also be initialized in "shared mode", in which
* it will just act as a wrapper to a single char array managed
* by another object (like parser that owns it)
*
*
TextBuffer is not synchronized.
*
*
*/
public final class TextBuffer
{
final static char[] NO_CHARS = new char[0];
// // // Configuration:
private final BufferRecycler mAllocator;
// // // Shared read-only input buffer:
/**
* Shared input buffer; stored here in case some input can be returned
* as is, without being copied to collector's own buffers. Note that
* this is read-only for this Objet.
*/
private char[] mInputBuffer;
/**
* Character offset of first char in input buffer; -1 to indicate
* that input buffer currently does not contain any useful char data
*/
private int mInputStart;
private int mInputLen;
// // // Internal non-shared collector buffers:
/**
* List of segments prior to currently active segment.
*/
private ArrayList mSegments;
/**
* Flag that indicates whether mSegments is non-empty
*/
private boolean mHasSegments = false;
// // // Currently used segment; not (yet) contained in mSegments
/**
* Amount of characters in segments in {@link mSegments}
*/
private int mSegmentSize;
private char[] mCurrentSegment;
/**
* Number of characters in currently active (last) segment
*/
private int mCurrentSize;
// // // Temporary caching for Objects to return
/**
* String that will be constructed when the whole contents are
* needed; will be temporarily stored in case asked for again.
*/
private String mResultString;
private char[] mResultArray;
/*
//////////////////////////////////////////////
// Life-cycle
//////////////////////////////////////////////
*/
public TextBuffer(BufferRecycler allocator)
{
mAllocator = allocator;
}
/**
* Method called to indicate that the underlying buffers should now
* be recycled if they haven't yet been recycled. Although caller
* can still use this text buffer, it is not advisable to call this
* method if that is likely, since next time a buffer is needed,
* buffers need to reallocated.
* Note: calling this method automatically also clears contents
* of the buffer.
*/
public void releaseBuffers()
{
if (mAllocator != null && mCurrentSegment != null) {
// First, let's get rid of all but the largest char array
resetWithEmpty();
// And then return that array
char[] buf = mCurrentSegment;
mCurrentSegment = null;
mAllocator.releaseCharBuffer(BufferRecycler.CharBufferType.TEXT_BUFFER, buf);
}
}
/**
* Method called to clear out any content text buffer may have, and
* initializes buffer to use non-shared data.
*/
public void resetWithEmpty()
{
mInputBuffer = null;
mInputStart = -1; // indicates shared buffer not used
mInputLen = 0;
mResultString = null;
mResultArray = null;
// And then reset internal input buffers, if necessary:
if (mHasSegments) {
clearSegments();
}
mCurrentSize = 0;
}
/**
* Method called to initialize the buffer with a shared copy of data;
* this means that buffer will just have pointers to actual data. It
* also means that if anything is to be appended to the buffer, it
* will first have to unshare it (make a local copy).
*/
public void resetWithShared(char[] buf, int start, int len)
{
// First, let's clear intermediate values, if any:
mResultString = null;
mResultArray = null;
// Then let's mark things we need about input buffer
mInputBuffer = buf;
mInputStart = start;
mInputLen = len;
// And then reset internal input buffers, if necessary:
if (mHasSegments) {
clearSegments();
}
}
public void resetWithCopy(char[] buf, int start, int len)
{
mInputBuffer = null;
mInputStart = -1; // indicates shared buffer not used
mInputLen = 0;
mResultString = null;
mResultArray = null;
// And then reset internal input buffers, if necessary:
if (mHasSegments) {
clearSegments();
}
mCurrentSize = mSegmentSize = 0;
append(buf, start, len);
}
public void resetWithString(String str)
{
// First things first, let's reset the buffer
mInputBuffer = null;
mInputStart = -1; // indicates shared buffer not used
mInputLen = 0;
mResultString = str;
mResultArray = null;
int len = str.length();
if (mHasSegments) {
mCurrentSegment = mSegments.get(mSegments.size() - 1);
mSegments.clear();
} else if (mCurrentSegment == null) {
mCurrentSegment = allocBuffer(len);
}
// Ok, but does the String fit? If not, need to realloc
if (mCurrentSegment.length < len) {
mCurrentSegment = new char[len];
}
str.getChars(0, len, mCurrentSegment, 0);
mCurrentSize = len;
}
private final char[] allocBuffer(int needed)
{
return mAllocator.allocCharBuffer(BufferRecycler.CharBufferType.TEXT_BUFFER, needed);
}
private final void clearSegments()
{
mHasSegments = false;
/* Let's start using _last_ segment from list; for one, it's
* the biggest one, and it's also most likely to be cached
*/
mCurrentSegment = mSegments.get(mSegments.size() - 1);
mSegments.clear();
mCurrentSize = mSegmentSize = 0;
}
/*
//////////////////////////////////////////////
// Accessors for implementing StAX interface:
//////////////////////////////////////////////
*/
/**
* @return Number of characters currently stored by this collector
*/
public int size() {
if (mInputStart >= 0) { // shared copy from input buf
return mInputLen;
}
// local segmented buffers
return mSegmentSize + mCurrentSize;
}
public int getTextOffset()
{
/* Only shared input buffer can have non-zero offset; buffer
* segments start at 0, and if we have to create a combo buffer,
* that too will start from beginning of the buffer
*/
return (mInputStart >= 0) ? mInputStart : 0;
}
public char[] getTextBuffer()
{
// Are we just using shared input buffer?
if (mInputStart >= 0) {
return mInputBuffer;
}
// Nope; but does it fit in just one segment?
if (!mHasSegments) {
return mCurrentSegment;
}
// Nope, need to have/create a non-segmented array and return it
return contentsAsArray();
}
/*
//////////////////////////////////////////////
// Accessors:
//////////////////////////////////////////////
*/
public String contentsAsString()
{
if (mResultString == null) {
// Has array been requested? Can make a shortcut, if so:
if (mResultArray != null) {
mResultString = new String(mResultArray);
} else {
// Do we use shared array?
if (mInputStart >= 0) {
if (mInputLen < 1) {
return (mResultString = "");
}
mResultString = new String(mInputBuffer, mInputStart, mInputLen);
} else { // nope... need to copy
// But first, let's see if we have just one buffer
int segLen = mSegmentSize;
int currLen = mCurrentSize;
if (segLen == 0) { // yup
mResultString = (currLen == 0) ? "" : new String(mCurrentSegment, 0, currLen);
} else { // no, need to combine
StringBuilder sb = new StringBuilder(segLen + currLen);
// First stored segments
if (mSegments != null) {
for (int i = 0, len = mSegments.size(); i < len; ++i) {
char[] curr = mSegments.get(i);
sb.append(curr, 0, curr.length);
}
}
// And finally, current segment:
sb.append(mCurrentSegment, 0, mCurrentSize);
mResultString = sb.toString();
}
}
}
}
return mResultString;
}
public char[] contentsAsArray()
{
char[] result = mResultArray;
if (result == null) {
mResultArray = result = buildResultArray();
}
return result;
}
public int contentsToArray(int srcStart, char[] dst, int dstStart, int len) {
// Easy to copy from shared buffer:
if (mInputStart >= 0) {
int amount = mInputLen - srcStart;
if (amount > len) {
amount = len;
} else if (amount < 0) {
amount = 0;
}
if (amount > 0) {
System.arraycopy(mInputBuffer, mInputStart+srcStart,
dst, dstStart, amount);
}
return amount;
}
/* Could also check if we have array, but that'd only help with
* braindead clients that get full array first, then segments...
* which hopefully aren't that common
*/
// Copying from segmented array is bit more involved:
int totalAmount = 0;
if (mSegments != null) {
for (int i = 0, segc = mSegments.size(); i < segc; ++i) {
char[] segment = mSegments.get(i);
int segLen = segment.length;
int amount = segLen - srcStart;
if (amount < 1) { // nothing from this segment?
srcStart -= segLen;
continue;
}
if (amount >= len) { // can get rest from this segment?
System.arraycopy(segment, srcStart, dst, dstStart, len);
return (totalAmount + len);
}
// Can get some from this segment, offset becomes zero:
System.arraycopy(segment, srcStart, dst, dstStart, amount);
totalAmount += amount;
dstStart += amount;
len -= amount;
srcStart = 0;
}
}
// Need to copy anything from last segment?
if (len > 0) {
int maxAmount = mCurrentSize - srcStart;
if (len > maxAmount) {
len = maxAmount;
}
if (len > 0) { // should always be true
System.arraycopy(mCurrentSegment, srcStart, dst, dstStart, len);
totalAmount += len;
}
}
return totalAmount;
}
/**
* Method that will stream contents of this buffer into specified
* Writer.
*/
public int rawContentsTo(Writer w)
throws IOException
{
// Let's first see if we have created helper objects:
if (mResultArray != null) {
w.write(mResultArray);
return mResultArray.length;
}
if (mResultString != null) {
w.write(mResultString);
return mResultString.length();
}
// Do we use shared array?
if (mInputStart >= 0) {
if (mInputLen > 0) {
w.write(mInputBuffer, mInputStart, mInputLen);
}
return mInputLen;
}
// Nope, need to do full segmented output
int rlen = 0;
if (mSegments != null) {
for (int i = 0, len = mSegments.size(); i < len; ++i) {
char[] ch = mSegments.get(i);
w.write(ch);
rlen += ch.length;
}
}
if (mCurrentSize > 0) {
w.write(mCurrentSegment, 0, mCurrentSize);
rlen += mCurrentSize;
}
return rlen;
}
/**
* Convenience method for converting contents of the buffer
* into a {@link BigDecimal}.
*/
public BigDecimal contentsAsDecimal()
throws NumberFormatException
{
// Already got a pre-cut array?
if (mResultArray != null) {
return new BigDecimal(mResultArray);
}
// Or a shared buffer?
if (mInputStart >= 0) {
return new BigDecimal(mInputBuffer, mInputStart, mInputLen);
}
// Or if not, just a single buffer (the usual case)
if (mSegmentSize == 0) {
return new BigDecimal(mCurrentSegment, 0, mCurrentSize);
}
// If not, let's just get it aggregated...
return new BigDecimal(contentsAsArray());
}
/**
* Convenience method for converting contents of the buffer
* into a Double value.
*/
public double contentsAsDouble()
throws NumberFormatException
{
return Double.parseDouble(contentsAsString());
}
/*
//////////////////////////////////////////////
// Public mutators:
//////////////////////////////////////////////
*/
/**
* Method called to make sure that buffer is not using shared input
* buffer; if it is, it will copy such contents to private buffer.
*/
public void ensureNotShared() {
if (mInputStart >= 0) {
unshare(16);
}
}
public void append(char[] c, int start, int len)
{
// Can't append to shared buf (sanity check)
if (mInputStart >= 0) {
unshare(len);
}
mResultString = null;
mResultArray = null;
// Room in current segment?
char[] curr = mCurrentSegment;
int max = curr.length - mCurrentSize;
if (max >= len) {
System.arraycopy(c, start, curr, mCurrentSize, len);
mCurrentSize += len;
} else {
// No room for all, need to copy part(s):
if (max > 0) {
System.arraycopy(c, start, curr, mCurrentSize, max);
start += max;
len -= max;
}
// And then allocate new segment; we are guaranteed to now
// have enough room in segment.
expand(len); // note: curr != mCurrentSegment after this
System.arraycopy(c, start, mCurrentSegment, 0, len);
mCurrentSize = len;
}
}
/*
//////////////////////////////////////////////
// Raw access, for high-performance use:
//////////////////////////////////////////////
*/
public char[] getCurrentSegment()
{
/* Since the intention of the caller is to directly add stuff into
* buffers, we should NOT have anything in shared buffer... ie. may
* need to unshare contents.
*/
if (mInputStart >= 0) {
unshare(1);
} else {
char[] curr = mCurrentSegment;
if (curr == null) {
mCurrentSegment = allocBuffer(0);
} else if (mCurrentSize >= curr.length) {
// Plus, we better have room for at least one more char
expand(1);
}
}
return mCurrentSegment;
}
public char[] emptyAndGetCurrentSegment()
{
resetWithEmpty();
char[] curr = mCurrentSegment;
if (curr == null) {
mCurrentSegment = curr = allocBuffer(0);
}
return curr;
}
public int getCurrentSegmentSize() {
return mCurrentSize;
}
public void setCurrentLength(int len) {
mCurrentSize = len;
}
public char[] finishCurrentSegment()
{
if (mSegments == null) {
mSegments = new ArrayList();
}
mHasSegments = true;
mSegments.add(mCurrentSegment);
int oldLen = mCurrentSegment.length;
mSegmentSize += oldLen;
// Let's grow segments by 50%
char[] curr = new char[oldLen + (oldLen >> 1)];
mCurrentSize = 0;
mCurrentSegment = curr;
return curr;
}
/**
* Method called to expand size of the current segment, to
* accomodate for more contiguous content. Usually only
* used when parsing tokens like names.
*/
public char[] expandCurrentSegment()
{
char[] curr = mCurrentSegment;
// Let's just double right away
int len = curr.length;
mCurrentSegment = new char[len + len];
System.arraycopy(curr, 0, mCurrentSegment, 0, len);
return mCurrentSegment;
}
/*
//////////////////////////////////////////////
// Standard methods:
//////////////////////////////////////////////
*/
/**
* Note: calling this method may not be as efficient as calling
* {@link #contentsAsString}, since it's not guaranteed that resulting
* String is cached.
*/
public String toString() {
return contentsAsString();
}
/*
//////////////////////////////////////////////
// Internal methods:
//////////////////////////////////////////////
*/
/**
* Method called if/when we need to append content when we have been
* initialized to use shared buffer.
*/
private void unshare(int needExtra)
{
int sharedLen = mInputLen;
mInputLen = 0;
char[] inputBuf = mInputBuffer;
mInputBuffer = null;
int start = mInputStart;
mInputStart = -1;
// Is buffer big enough, or do we need to reallocate?
int needed = sharedLen+needExtra;
if (mCurrentSegment == null || needed > mCurrentSegment.length) {
mCurrentSegment = allocBuffer(needed);
}
if (sharedLen > 0) {
System.arraycopy(inputBuf, start, mCurrentSegment, 0, sharedLen);
}
mSegmentSize = 0;
mCurrentSize = sharedLen;
}
/**
* Method called when current segment is full, to allocate new
* segment.
*/
private void expand(int minNewSegmentSize)
{
// First, let's move current segment to segment list:
if (mSegments == null) {
mSegments = new ArrayList();
}
char[] curr = mCurrentSegment;
mHasSegments = true;
mSegments.add(curr);
mSegmentSize += curr.length;
int oldLen = curr.length;
// Let's grow segments by 50% minimum
int sizeAddition = oldLen >> 1;
if (sizeAddition < minNewSegmentSize) {
sizeAddition = minNewSegmentSize;
}
curr = new char[oldLen + sizeAddition];
mCurrentSize = 0;
mCurrentSegment = curr;
}
private char[] buildResultArray()
{
if (mResultString != null) { // Can take a shortcut...
return mResultString.toCharArray();
}
char[] result;
// Do we use shared array?
if (mInputStart >= 0) {
if (mInputLen < 1) {
return NO_CHARS;
}
result = new char[mInputLen];
System.arraycopy(mInputBuffer, mInputStart, result, 0,
mInputLen);
} else { // nope
int size = size();
if (size < 1) {
return NO_CHARS;
}
int offset = 0;
result = new char[size];
if (mSegments != null) {
for (int i = 0, len = mSegments.size(); i < len; ++i) {
char[] curr = (char[]) mSegments.get(i);
int currLen = curr.length;
System.arraycopy(curr, 0, result, offset, currLen);
offset += currLen;
}
}
System.arraycopy(mCurrentSegment, 0, result, offset, mCurrentSize);
}
return result;
}
}