blob: f59cf7ec5089463722fb6a03145033e715fb6387 [file] [log] [blame]
/* 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.xmlbeans.impl.newstore2;
import java.io.PrintStream;
public final class CharUtil
{
public static CharIterator getThreadLocalCharIterator ( Object src, int off, int cch )
{
CharIterator charIter = (CharIterator) tl_charIter.get();
charIter.init( src, off, cch );
return charIter;
}
public static CharUtil getThreadLocalCharUtil ( )
{
return (CharUtil) tl_charUtil.get();
}
public static void getString ( StringBuffer sb, Object src, int off, int cch )
{
assert isValid( src, off, cch );
if (cch == 0)
return;
if (src instanceof char[])
sb.append( (char[]) src, off, cch );
else if (src instanceof String)
{
String s = (String) src;
if (off == 0 && cch == s.length())
sb.append( (String) src );
else
sb.append( s.substring( off, off + cch ) );
}
else
((CharJoin) src).getString( sb, off, cch );
}
public static void getChars ( char[] chars, int start, Object src, int off, int cch )
{
assert isValid( src, off, cch );
assert chars != null && start >= 0 && start <= chars.length;
if (cch == 0)
return;
if (src instanceof char[])
System.arraycopy( (char[]) src, off, chars, start, cch );
else if (src instanceof String)
((String) src).getChars( off, off + cch, chars, start );
else
((CharJoin) src).getChars( chars, start, off, cch );
}
public static String getString ( Object src, int off, int cch )
{
assert isValid( src, off, cch );
if (cch == 0)
return "";
if (src instanceof char[])
return new String( (char[]) src, off, cch );
if (src instanceof String)
{
String s = (String) src;
if (off == 0 && cch == s.length())
return s;
return s.substring( off, off + cch );
}
StringBuffer sb = new StringBuffer();
((CharJoin) src).getString( sb, off, cch );
return sb.toString();
}
public static final boolean isWhiteSpace ( char ch )
{
switch ( ch )
{
case ' ': case '\t': case '\n': case '\r': return true;
default : return false;
}
}
public static final boolean isWhiteSpace ( Object src, int off, int cch )
{
assert isValid( src, off, cch );
if (cch > 0)
{
if (src instanceof char[])
{
for ( char[] chars = (char[]) src ; cch > 0 ; cch-- )
if (!isWhiteSpace( chars[ off++ ] ))
return false;
}
else if (src instanceof String)
{
for ( String s = (String) src ; cch > 0 ; cch-- )
if (!isWhiteSpace( s.charAt( off++ ) ))
return false;
}
else
{
CharIterator charIter = getThreadLocalCharIterator( src, off, cch );
while ( isWhiteSpace( charIter.currChar() ) && --cch > 0 )
charIter.next();
if (cch != 0)
return false;
}
}
return true;
}
public Object stripLeft ( Object src, int off, int cch )
{
assert isValid( src, off, cch );
if (cch > 0)
{
if (src instanceof char[])
{
char[] chars = (char[]) src;
while ( cch > 0 && isWhiteSpace( chars[ off ] ) )
{ cch--; off++; }
}
else if (src instanceof String)
{
String s = (String) src;
while ( cch > 0 && isWhiteSpace( s.charAt( off ) ) )
{ cch--; off++; }
}
else
{
int oldCch = cch;
CharIterator charIter = getThreadLocalCharIterator( src, off, cch );
while ( isWhiteSpace( charIter.currChar() ) && --cch > 0 )
charIter.next();
off += oldCch - cch;
}
}
if (cch == 0)
{
_offSrc = 0;
_cchSrc = 0;
return null;
}
_offSrc = off;
_cchSrc = cch;
return src;
}
public Object stripRight ( Object src, int off, int cch )
{
assert isValid( src, off, cch );
if (cch > 0)
{
int oldCch = cch;
CharIterator charIter = getThreadLocalCharIterator( src, off, cch );
charIter.setPos( cch - 1 );
while ( isWhiteSpace( charIter.currChar() ) && --cch > 0 )
charIter.prev();
}
if (cch == 0)
{
_offSrc = 0;
_cchSrc = 0;
return null;
}
_offSrc = off;
_cchSrc = cch;
return src;
}
public Object insertChars (
int posInsert,
Object src, int off, int cch,
Object srcInsert, int offInsert, int cchInsert )
{
assert isValid( src, off, cch );
assert isValid( srcInsert, offInsert, cchInsert );
assert posInsert >= 0 && posInsert <= cch;
// TODO - at some point, instead of creating joins, I should
// normalize all the text into a single buffer to stop large
// tree;s from being built when many modifications happen...
// TODO - actually, I should see if the size of the new char
// sequence is small enough to simply allocate a new contigous
// sequence, either in a common char[] managed by the master,
// or just create a new string ... this goes for remove chars
// as well.
if (cchInsert == 0)
{
_cchSrc = cch;
_offSrc = off;
return src;
}
if (cch == 0)
{
_cchSrc = cchInsert;
_offSrc = offInsert;
return srcInsert;
}
_cchSrc = cch + cchInsert;
Object newSrc;
if (_cchSrc <= MAX_COPY && canAllocate( _cchSrc ))
{
char[] chars = allocate( _cchSrc );
getChars( chars, _offSrc, src, off, posInsert );
getChars( chars, _offSrc + posInsert, srcInsert, offInsert, cchInsert );
getChars(
chars, _offSrc + posInsert + cchInsert, src, off + posInsert, cch - posInsert );
newSrc = chars;
}
else
{
_offSrc = 0;
CharJoin newJoin;
if (posInsert == 0)
newJoin = new CharJoin( srcInsert, offInsert, cchInsert, src, off, cch );
else if (posInsert == cch)
newJoin = new CharJoin( src, off, cch, srcInsert, offInsert, cchInsert );
else
{
newJoin =
new CharJoin(
new CharJoin( src, off, posInsert, srcInsert, offInsert, cchInsert ),
0, posInsert + cchInsert,
src, off + posInsert, cch - posInsert );
}
if (newJoin._depth > CharJoin.MAX_DEPTH)
newSrc = saveChars( newJoin, _offSrc, _cchSrc, null, 0, 0 );
else
newSrc = newJoin;
}
assert isValid( newSrc, _offSrc, _cchSrc );
return newSrc;
}
public Object removeChars ( int posRemove, int cchRemove, Object src, int off, int cch )
{
assert isValid( src, off, cch );
assert posRemove >= 0 && posRemove <= cch;
assert cchRemove >= 0 && posRemove + cchRemove <= cch;
Object newSrc;
_cchSrc = cch - cchRemove;
if (_cchSrc == 0)
{
newSrc = null;
_offSrc = 0;
}
else if (posRemove == 0)
{
newSrc = src;
_offSrc = off + cchRemove;
}
else if (posRemove + cchRemove == cch)
{
newSrc = src;
_offSrc = off;
}
else
{
int cchAfter = cch - cchRemove;
if (cchAfter <= MAX_COPY && canAllocate( cchAfter ))
{
char[] chars = allocate( cchAfter );
getChars( chars, _offSrc, src, off, posRemove );
getChars(
chars, _offSrc + posRemove,
src, off + posRemove + cchRemove, cch - posRemove - cchRemove );
newSrc = chars;
_offSrc = _offSrc;
}
else
{
CharJoin newJoin =
new CharJoin(
src, off, posRemove,
src, off + posRemove + cchRemove, cch - posRemove - cchRemove );
if (newJoin._depth > CharJoin.MAX_DEPTH)
newSrc = saveChars( newJoin, 0, _cchSrc, null, 0, 0 );
else
{
newSrc = newJoin;
_offSrc = 0;
}
}
}
assert isValid( newSrc, _offSrc, _cchSrc );
return newSrc;
}
private boolean canAllocate ( int cch )
{
return _currentBuffer == null || _currentBuffer.length - _currentOffset >= cch;
}
private char[] allocate ( int cch )
{
assert _currentBuffer == null || _currentBuffer.length - _currentOffset > 0;
if (_currentBuffer == null)
{
_currentBuffer = new char [ Math.max( cch, _charBufSize ) ];
_currentOffset = 0;
}
_offSrc = _currentOffset;
_cchSrc = Math.min( _currentBuffer.length - _currentOffset, cch );
char[] retBuf = _currentBuffer;
if ((_currentOffset += _cchSrc) == _currentBuffer.length)
{
_currentBuffer = null;
_currentOffset = 0;
}
return retBuf;
}
public Object saveChars (
Object src, int off, int cch,
Object srcPrev, int offPrev, int cchPrev )
{
assert isValid( src, off, cch );
assert isValid( srcPrev, offPrev, cchPrev );
char[] srcAlloc = allocate( cch );
getChars( srcAlloc, _offSrc, src, off, _cchSrc );
Object srcNew;
int offNew;
if (cchPrev == 0)
{
srcNew = srcAlloc;
offNew = _offSrc;
}
else if (srcPrev == srcAlloc && offPrev + cchPrev == _offSrc)
{
srcNew = srcPrev;
offNew = offPrev;
}
else
{
srcNew = new CharJoin( srcPrev, offPrev, cchPrev, srcAlloc, _offSrc, _cchSrc );
offNew = 0;
}
int cchNew = _cchSrc + cchPrev;
int cchR = cch - _cchSrc;
if (cchR > 0)
{
int cchL = _cchSrc;
srcAlloc = allocate( cchR );
assert _cchSrc == cchR;
assert _offSrc == 0;
getChars( srcAlloc, _offSrc, src, off + cchL, _cchSrc );
srcNew = new CharJoin( srcNew, offNew, cchNew, srcAlloc, _offSrc, cchR );
offNew = 0;
cchNew += cchR;
}
_offSrc = offNew;
_cchSrc = cchNew;
assert isValid( srcNew, _offSrc, _cchSrc );
return srcNew;
}
private static void dumpText ( PrintStream o, String s )
{
o.print( "\"" );
for ( int i = 0 ; i < s.length() ; i++ )
{
char ch = s.charAt( i );
if (i == 36)
{
o.print( "..." );
break;
}
if (ch == '\n') o.print( "\\n" );
else if (ch == '\r') o.print( "\\r" );
else if (ch == '\t') o.print( "\\t" );
else if (ch == '\f') o.print( "\\f" );
else if (ch == '\f') o.print( "\\f" );
else if (ch == '"' ) o.print( "\\\"" );
else o.print( ch );
}
o.print( "\"" );
}
public static void dump ( Object src, int off, int cch )
{
dumpChars( System.out, src, off, cch );
System.out.println();
}
public static void dumpChars ( PrintStream p, Object src, int off, int cch )
{
p.print( "cch=" + cch + ", off=" + off + ": " );
if (src == null)
p.print( "<null-src>" );
else if (src instanceof String)
{
String s = (String) src;
p.print( "String" );
if (off != 0 || cch != s.length())
{
p.print( " offf: " + off + ", cch: " + cch );
if (off < 0 || off > s.length() || off + cch < 0 || off + cch > s.length())
{
p.print( " (Error)" );
return;
}
}
p.print( ": " );
dumpText( p, s.substring( off, off + cch ) );
}
else if (src instanceof char[])
{
char[] chars = (char[]) src;
p.print( "char[]" );
if (off != 0 || cch != chars.length)
{
p.print( " off: " + off + ", cch: " + cch );
if (off < 0 || off > chars.length || off + cch < 0 || off + cch > chars.length)
{
p.print( " (Error)" );
return;
}
}
p.print( ": " );
dumpText( p, new String( chars, off, cch ) );
}
else if (src instanceof CharJoin)
{
p.print( "CharJoin" );
((CharJoin) src).dumpChars( p, off, cch );
}
else
{
p.print( "Unknown text source" );
}
}
public static boolean isValid ( Object src, int off, int cch )
{
if (cch < 0 || off < 0)
return false;
if (src == null)
return off == 0 && cch == 0;
if (src instanceof char[])
{
char[] c = (char[]) src;
return off <= c.length && off + cch <= c.length;
}
if (src instanceof String)
{
String s = (String) src;
return off <= s.length() && off + cch <= s.length();
}
if (src instanceof CharJoin)
{
return ((CharJoin) src).isValid( off, cch );
}
return false;
}
//
// Private stuff
//
private static final class CharJoin
{
public CharJoin (
Object srcLeft, int offLeft, int cchLeft,
Object srcRight, int offRight, int cchRight )
{
_srcLeft = srcLeft; _offLeft = offLeft; _cchLeft = cchLeft;
_srcRight = srcRight; _offRight = offRight; _cchRight = cchRight;
int depth = 0;
if (srcLeft instanceof CharJoin)
depth = ((CharJoin) srcLeft)._depth;
if (srcRight instanceof CharJoin)
{
int rightDepth = ((CharJoin) srcRight)._depth;
if (rightDepth > depth)
depth = rightDepth;
}
_depth = depth;
assert _depth <= MAX_DEPTH + 1;
}
final Object _srcLeft;
final int _offLeft;
final int _cchLeft;
final Object _srcRight;
final int _offRight;
final int _cchRight;
final int _depth;
static final int MAX_DEPTH = 64;
public int length ( ) { return _cchLeft + _cchRight; }
public boolean isValid ( int off, int cch )
{
if (off < 0 || cch < 0 || off > length() || off + cch > length())
return false;
if (!CharUtil.isValid( _srcRight, _offRight, _cchRight ))
return false;
if (!CharUtil.isValid( _srcLeft, _offLeft, _cchLeft ))
return false;
return true;
}
private void getString ( StringBuffer sb, int off, int cch )
{
assert cch > 0;
if (off < _cchLeft)
{
int cchL = _cchLeft - off;
if (cchL > cch)
cchL = cch;
CharUtil.getString( sb, _srcLeft, _offLeft + off, cchL );
cch -= cchL;
if (cch > 0)
CharUtil.getString( sb, _srcRight, _offRight, cch );
}
else
CharUtil.getString( sb, _srcRight, _offRight + off - _cchLeft, cch );
}
private void getChars ( char[] chars, int start, int off, int cch )
{
assert cch > 0;
if (off < _cchLeft)
{
int cchL = _cchLeft - off;
if (cchL > cch)
cchL = cch;
CharUtil.getChars( chars, start, _srcLeft, _offLeft + off, cchL );
start += cchL;
cch -= cchL;
if (cch > 0)
CharUtil.getChars( chars, start, _srcRight, _offRight, cch );
}
else
CharUtil.getChars( chars, start, _srcRight, _offRight + off - _cchLeft, cch );
}
private void dumpChars( int off, int cch )
{
dumpChars( System.out, off, cch );
}
private void dumpChars( PrintStream p, int off, int cch )
{
p.print( "( " );
CharUtil.dumpChars( p, _srcLeft, _offLeft, _cchLeft );
p.print( ", " );
CharUtil.dumpChars( p, _srcRight, _offRight, _cchRight );
p.print( " )" );
}
}
private CharUtil ( int charBufSize )
{
_charBufSize = charBufSize;
}
public final static class CharIterator
{
void init ( Object src, int off, int cch )
{
assert cch > 0;
assert isValid( src, off, cch );
_src = src;
_off = off;
_cch = cch;
_top = -1;
while ( _src instanceof CharJoin )
{
CharJoin cj = (CharJoin) _src;
_joins[ ++_top ] = cj;
_poses[ _top ] = 0;
if (cj._cchLeft > 0)
{
_src = cj._srcLeft;
_off = cj._offLeft;
_cch = cj._cchLeft;
}
else
{
_src = cj._srcRight;
_off = cj._offRight;
_cch = cj._cchRight;
}
}
_currPos = 0;
_startPos = 0;
cacheLeaf();
}
char currChar ( )
{
assert _src instanceof String || _src instanceof char[];
int index = _off + _currPos - _startPos;
return _chars == null ? _string.charAt( index ) : _chars[ index ];
}
void prev ( )
{
setPos( _currPos - 1 );
}
void next ( )
{
setPos( _currPos + 1 );
}
void setPos ( final int newPos )
{
assert newPos >= 0;
assert !(_src instanceof CharJoin);
if (newPos < _startPos || newPos >= _startPos + _cch)
{
_startPos = _poses[ _top ];
while ( newPos < _startPos || newPos >= _startPos + _joins[ _top ].length() )
{
_joins[ _top-- ] = null;
_startPos = _poses[ _top ];
}
CharJoin cj = _joins[ _top ];
for ( ; ; )
{
if (newPos < _startPos + cj._cchLeft)
{
_src = cj._srcLeft;
_off = cj._offLeft;
_cch = cj._offLeft;
}
else
{
_src = cj._srcRight;
_off = cj._offRight;
_cch = cj._offRight;
_startPos += cj._cchLeft;
}
if (!(_src instanceof CharJoin))
break;
cj = (CharJoin) _src;
_joins[ ++_top ] = cj;
_poses[ _top ] = _startPos;
}
cacheLeaf();
}
assert newPos >= _startPos && newPos < _startPos + _cch;
_currPos = newPos;
}
private void cacheLeaf ( )
{
assert _src instanceof String || _src instanceof char[];
if (_src instanceof char[])
{
_chars = (char[]) _src;
_string = null;
}
else
{
_string = (String) _src;
_chars = null;
}
}
private Object _src;
private int _off;
private int _cch;
private int _startPos;
private int _currPos;
private String _string;
private char[] _chars;
private CharJoin[] _joins = new CharJoin[ CharJoin.MAX_DEPTH + 1 ];
private int[] _poses = new int [ CharJoin.MAX_DEPTH + 1 ];
private int _top;
}
private static ThreadLocal tl_charUtil =
new ThreadLocal() { protected Object initialValue() { return new CharUtil( 1024 * 32 ); } };
private static ThreadLocal tl_charIter =
new ThreadLocal() { protected Object initialValue() { return new CharIterator(); } };
private static final int MAX_COPY = 8;
// private static final int MAX_COPY = 64;
// Current char buffer we're allcoating new chars to
private int _charBufSize;
private int _currentOffset;
private char[] _currentBuffer;
// These members are used to communicate offset and character count
// information back to a caller of various methods on CharUtil.
// Usually, the methods returns the src Object, and these two hold
// the offset and the char count.
public int _offSrc;
public int _cchSrc;
}