mirror of
https://github.com/unicode-org/icu.git
synced 2025-04-13 08:53:20 +00:00
RichEdit control classes - styled text storage.
X-SVN-Rev: 1185
This commit is contained in:
parent
7dd3e16ec6
commit
0622187d5a
19 changed files with 4781 additions and 0 deletions
379
icu4j/src/com/ibm/richtext/styledtext/CharBuffer.java
Executable file
379
icu4j/src/com/ibm/richtext/styledtext/CharBuffer.java
Executable file
|
@ -0,0 +1,379 @@
|
|||
/*
|
||||
* @(#)$RCSfile: CharBuffer.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:45:10 $
|
||||
*
|
||||
* (C) Copyright IBM Corp. 1998-1999. All Rights Reserved.
|
||||
*
|
||||
* The program is provided "as is" without any warranty express or
|
||||
* implied, including the warranty of non-infringement and the implied
|
||||
* warranties of merchantibility and fitness for a particular purpose.
|
||||
* IBM will not be liable for any damages suffered by you as a result
|
||||
* of using the Program. In no event will IBM be liable for any
|
||||
* special, indirect or consequential damages or lost profits even if
|
||||
* IBM has been advised of the possibility of their occurrence. IBM
|
||||
* will not be liable for any third party claims against you.
|
||||
*/
|
||||
/** An implementation of MCharBuffer that stores chars in an array with an insertion gap. */
|
||||
/*
|
||||
Change history
|
||||
072396 jf - fixed a bug in replace(int, int, char[], int, int) so that it correctly
|
||||
inserted into the middle of the buffer.
|
||||
080296 jf - added timestamp. This is strictly a debugging device to help catch
|
||||
stale iterators.
|
||||
|
||||
082296 jbr added check for 0-length iterator in replace
|
||||
*/
|
||||
|
||||
package com.ibm.richtext.styledtext;
|
||||
|
||||
import java.io.Externalizable;
|
||||
import java.io.ObjectInput;
|
||||
import java.io.ObjectOutput;
|
||||
import java.io.IOException;
|
||||
|
||||
import java.text.CharacterIterator;
|
||||
|
||||
final class CharBuffer
|
||||
extends MCharBuffer implements Externalizable
|
||||
{
|
||||
|
||||
static final String COPYRIGHT =
|
||||
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
|
||||
private static final int kGrowSize = 0x80; // small size for testing
|
||||
private static final int CURRENT_VERSION = 1; // version code for streaming
|
||||
private static final long serialVersionUID = 563174;
|
||||
|
||||
transient Validation fValidation = null;
|
||||
private char[] fArray;
|
||||
transient private int fArraySize;
|
||||
transient private int fGap;
|
||||
|
||||
/** Create an empty char buffer. */
|
||||
public CharBuffer()
|
||||
{
|
||||
}
|
||||
|
||||
/** Create a char buffer that can hold at least capacity chars. */
|
||||
|
||||
public CharBuffer(int capacity)
|
||||
{
|
||||
fArray = allocate(capacity);
|
||||
}
|
||||
|
||||
public void readExternal(ObjectInput in) throws IOException,
|
||||
ClassNotFoundException {
|
||||
|
||||
if (in.readInt() != CURRENT_VERSION) {
|
||||
throw new IOException("Invalid version of CharBuffer");
|
||||
}
|
||||
|
||||
fArray = (char[]) in.readObject();
|
||||
if (fArray != null) {
|
||||
fArraySize = fArray.length;
|
||||
fGap = fArraySize;
|
||||
}
|
||||
else {
|
||||
fArraySize = 0;
|
||||
fGap = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void writeExternal(ObjectOutput out) throws IOException {
|
||||
|
||||
compress();
|
||||
out.writeInt(CURRENT_VERSION);
|
||||
out.writeObject(fArray);
|
||||
}
|
||||
|
||||
private void invalidate() {
|
||||
|
||||
if (fValidation != null) {
|
||||
fValidation.invalidate();
|
||||
fValidation = null;
|
||||
}
|
||||
}
|
||||
|
||||
// not ThreadSafe - could end up with two Validations
|
||||
// being generated
|
||||
private Validation getValidation() {
|
||||
|
||||
if (fValidation == null) {
|
||||
fValidation = new Validation();
|
||||
}
|
||||
return fValidation;
|
||||
}
|
||||
|
||||
/** Replace the chars from start to limit with the chars from srcStart to srcLimit in srcBuffer. */
|
||||
|
||||
/** Replace the chars from start to limit with the chars from srcStart to srcLimit in srcChars.
|
||||
* This is the core routine for manipulating the buffer.
|
||||
*/
|
||||
public void replace(int start, int limit, char[] srcChars, int srcStart, int srcLimit)
|
||||
{
|
||||
invalidate();
|
||||
int dstLength = limit - start;
|
||||
int srcLength = srcLimit - srcStart;
|
||||
|
||||
if (dstLength < 0 || srcLength < 0) {
|
||||
throw new IllegalArgumentException("replace(int start, int limit, char[] srcChars, int srcStart, int srcLimit)");
|
||||
}
|
||||
|
||||
int gapAlloc = 0;
|
||||
if (srcChars == null) {
|
||||
gapAlloc = srcLength;
|
||||
srcLength = 0;
|
||||
}
|
||||
|
||||
int newSize = fArraySize - dstLength + srcLength;
|
||||
|
||||
if (fArray == null) {
|
||||
if (start != 0 || limit != 0) {
|
||||
throw new IllegalArgumentException("replace(int start, int limit, char[] srcChars, int srcStart, int srcLimit)");
|
||||
}
|
||||
if (newSize + gapAlloc > 0) {
|
||||
fArray = allocate(newSize + gapAlloc);
|
||||
if (srcLength > 0) {
|
||||
System.arraycopy(srcChars, srcStart, fArray, 0, srcLength);
|
||||
fArraySize = srcLength;
|
||||
fGap = srcLength;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int newGap = start + srcLength;
|
||||
int gapLimit = fArray.length - fArraySize + fGap;
|
||||
|
||||
if (newSize + gapAlloc > fArray.length) {
|
||||
char[] temp = allocate(newSize + gapAlloc);
|
||||
|
||||
//move stuff at beginning that we aren't writing over
|
||||
if (start > 0) {
|
||||
at(0, start, temp, 0);
|
||||
}
|
||||
//move stuff from src array that we are copying
|
||||
if (srcLength > 0) {
|
||||
System.arraycopy(srcChars, srcStart, temp, start, srcLength);
|
||||
}
|
||||
//move stuff at end that we aren't copying over
|
||||
if (limit < fArraySize) {
|
||||
at(limit, fArraySize, temp, temp.length - newSize + newGap);
|
||||
//change 7-23-96
|
||||
// at(limit, fArraySize - limit, temp, temp.length - newSize + newGap);
|
||||
}
|
||||
|
||||
fArray = temp;
|
||||
} else {
|
||||
if (start > fGap) {
|
||||
System.arraycopy(fArray, gapLimit, fArray, fGap, start - fGap);
|
||||
}
|
||||
if (limit < fGap) {
|
||||
System.arraycopy(fArray, limit, fArray, fArray.length - newSize + newGap, fGap - limit);
|
||||
}
|
||||
if (srcLength > 0) {
|
||||
System.arraycopy(srcChars, srcStart, fArray, start, srcLength);
|
||||
}
|
||||
}
|
||||
|
||||
fArraySize = newSize;
|
||||
fGap = newGap;
|
||||
}
|
||||
}
|
||||
|
||||
/** Replace the chars from start to limit with the chars from srcStart to srcLimit in srcString. */
|
||||
|
||||
/* This implements optimizations for null text or inserting text that fits at the gap,
|
||||
and defaults to call the core replace routine if these optimizations fail. */
|
||||
|
||||
public void replace(int start, int limit, String srcString, int srcStart, int srcLimit)
|
||||
{
|
||||
invalidate();
|
||||
int length = limit - start;
|
||||
int srcLength = srcLimit - srcStart;
|
||||
|
||||
if (fArray == null) {
|
||||
if (start != 0 || limit != 0) {
|
||||
throw new IllegalArgumentException("replace(int start, int limit, String srcString, int srcStart, int srcLimit)");
|
||||
}
|
||||
if (srcLength > 0) {
|
||||
fArray = allocate(srcLength);
|
||||
srcString.getChars(srcStart, srcLimit, fArray, 0);
|
||||
fArraySize = srcLength;
|
||||
fGap = srcLength;
|
||||
}
|
||||
} else {
|
||||
if (start == fGap && fArray.length >= fArraySize - length + srcLength) {
|
||||
if (srcLimit > 0) {
|
||||
srcString.getChars(srcStart, srcLimit, fArray, fGap);
|
||||
fGap += srcLength;
|
||||
}
|
||||
fArraySize += srcLength - length;
|
||||
} else {
|
||||
replace(start, limit, srcString != null ? srcString.toCharArray() : null, srcStart, srcLimit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void replace(int start, int limit, MConstText srcText, int srcStart, int srcLimit)
|
||||
{
|
||||
invalidate();
|
||||
int length = limit - start;
|
||||
int srcLength = srcLimit - srcStart;
|
||||
|
||||
if (fArray == null) {
|
||||
if (start != 0 || limit != 0) {
|
||||
throw new IllegalArgumentException("replace(int start, int limit, String srcString, int srcStart, int srcLimit)");
|
||||
}
|
||||
if (srcLength > 0) {
|
||||
fArray = allocate(srcLength);
|
||||
srcText.extractChars(srcStart, srcLimit, fArray, 0);
|
||||
fArraySize = srcLength;
|
||||
fGap = srcLength;
|
||||
}
|
||||
} else {
|
||||
if (start == fGap && fArray.length >= fArraySize - length + srcLength) {
|
||||
if (srcLimit > 0) {
|
||||
srcText.extractChars(srcStart, srcLimit, fArray, fGap);
|
||||
fGap += srcLength;
|
||||
}
|
||||
fArraySize += srcLength - length;
|
||||
} else {
|
||||
char[] temp = srcLength == 0? null : new char[srcLength];
|
||||
if (temp != null) {
|
||||
srcText.extractChars(srcStart, srcLimit, temp, 0);
|
||||
}
|
||||
replace(start, limit, temp, 0, srcLimit - srcStart);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Replace the chars from start to limit with srcChar. */
|
||||
|
||||
/* This implements optimizations for null text or replacing a character that fits into the gap,
|
||||
and defaults to call the core replace routine if these optimizations fail. */
|
||||
|
||||
public void replace(int start, int limit, char srcChar)
|
||||
{
|
||||
invalidate();
|
||||
if (fArray == null) {
|
||||
if (start != 0 || limit != 0) {
|
||||
throw new IllegalArgumentException("replace(int start, int limit, char srcChar)");
|
||||
}
|
||||
fArray = allocate(1);
|
||||
fArray[0] = srcChar;
|
||||
fArraySize = 1;
|
||||
fGap = 1;
|
||||
} else {
|
||||
int length = limit - start;
|
||||
if (start == fGap && fArray.length > fArraySize - length) {
|
||||
fArray[fGap] = srcChar;
|
||||
fGap += 1;
|
||||
fArraySize += 1 - length;
|
||||
} else {
|
||||
replace(start, limit, new char[] { srcChar} , 0, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Return the char at pos. */
|
||||
|
||||
public char at(int pos)
|
||||
{
|
||||
if (pos < 0 || pos >= fArraySize) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
return pos < fGap ? fArray[pos] : fArray[fArray.length - fArraySize + pos];
|
||||
}
|
||||
|
||||
/** Copy the chars from start to limit to dst starting at dstStart. */
|
||||
|
||||
public void at(int start, int limit, char[] dst, int dstStart)
|
||||
{
|
||||
int length = limit - start;
|
||||
|
||||
if (start < 0 || limit < start || limit > fArraySize) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
if (limit <= fGap) {
|
||||
System.arraycopy(fArray, start, dst, dstStart, length);
|
||||
} else if (start >= fGap) {
|
||||
System.arraycopy(fArray, fArray.length - fArraySize + start, dst, dstStart, length);
|
||||
} else {
|
||||
System.arraycopy(fArray, start, dst, dstStart, fGap - start);
|
||||
System.arraycopy(fArray, fArray.length - fArraySize + fGap, dst, dstStart + fGap - start, limit - fGap);
|
||||
}
|
||||
}
|
||||
|
||||
/** Return the number of chars in the buffer. */
|
||||
|
||||
public final int length()
|
||||
{
|
||||
return fArraySize;
|
||||
}
|
||||
|
||||
/** Return the number of chars the buffer can hold before it must reallocate. */
|
||||
|
||||
public final int capacity()
|
||||
{
|
||||
return fArray != null ? fArray.length : 0;
|
||||
}
|
||||
|
||||
/** Reserve capacity chars at start. Utility to optimize a sequence of operations at start. */
|
||||
|
||||
public void reserveCapacity(int start, int capacity)
|
||||
{
|
||||
replace(start, start, (char[])null, 0, capacity);
|
||||
}
|
||||
|
||||
/** Minimize the storage used by the buffer. */
|
||||
|
||||
public void compress()
|
||||
{
|
||||
invalidate();
|
||||
if (fArraySize == 0) {
|
||||
fArray = null;
|
||||
fGap = 0;
|
||||
} else if (fArraySize != fArray.length) {
|
||||
char[] temp = new char[fArraySize];
|
||||
at(0, fArraySize, temp, 0);
|
||||
fArray = temp;
|
||||
fGap = fArraySize;
|
||||
}
|
||||
}
|
||||
|
||||
/** Display the buffer. */
|
||||
|
||||
public String toString()
|
||||
{
|
||||
if (fArray != null) {
|
||||
return new StringBuffer()
|
||||
.append("limit: ").append(fArray.length)
|
||||
.append(", size: ").append(fArraySize)
|
||||
.append(", gap: ").append(fGap)
|
||||
.append(", ").append(fArray, 0, fGap)
|
||||
.append(fArray, fArray.length - fArraySize + fGap, fArraySize - fGap)
|
||||
.toString();
|
||||
} else {
|
||||
return new String("The buffer is empty.");
|
||||
}
|
||||
}
|
||||
|
||||
public CharacterIterator createCharacterIterator(int start, int limit) {
|
||||
|
||||
Validation val = getValidation();
|
||||
return new CharBufferIterator(start, limit, fArray, fArraySize, fGap, val);
|
||||
}
|
||||
|
||||
/** The resizing algorithm. Return a value >= minSize. */
|
||||
|
||||
protected int allocation(int minSize)
|
||||
{
|
||||
// return (minSize + kGrowSize) & ~(kGrowSize - 1);
|
||||
return minSize < kGrowSize ? kGrowSize : (minSize * 2 + kGrowSize) & ~(kGrowSize - 1);
|
||||
}
|
||||
|
||||
/** Allocate a new character array of limit >= minSize. */
|
||||
|
||||
protected char[] allocate(int minSize)
|
||||
{
|
||||
return new char[allocation(minSize)];
|
||||
}
|
||||
}
|
131
icu4j/src/com/ibm/richtext/styledtext/CharBufferIterator.java
Executable file
131
icu4j/src/com/ibm/richtext/styledtext/CharBufferIterator.java
Executable file
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* @(#)$RCSfile: CharBufferIterator.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:45:10 $
|
||||
*
|
||||
* (C) Copyright IBM Corp. 1998-1999. All Rights Reserved.
|
||||
*
|
||||
* The program is provided "as is" without any warranty express or
|
||||
* implied, including the warranty of non-infringement and the implied
|
||||
* warranties of merchantibility and fitness for a particular purpose.
|
||||
* IBM will not be liable for any damages suffered by you as a result
|
||||
* of using the Program. In no event will IBM be liable for any
|
||||
* special, indirect or consequential damages or lost profits even if
|
||||
* IBM has been advised of the possibility of their occurrence. IBM
|
||||
* will not be liable for any third party claims against you.
|
||||
*/
|
||||
package com.ibm.richtext.styledtext;
|
||||
|
||||
import java.text.CharacterIterator;
|
||||
|
||||
final class CharBufferIterator implements CharacterIterator,
|
||||
Cloneable
|
||||
{
|
||||
static final String COPYRIGHT =
|
||||
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
|
||||
private int fRangeStart;
|
||||
private int fRangeLimit;
|
||||
private int fCurrentIndex;
|
||||
private char fStorage[];
|
||||
private int fGap;
|
||||
private int fGapLength;
|
||||
private Validation fValidation;
|
||||
|
||||
CharBufferIterator(int start,
|
||||
int limit,
|
||||
char[] storage,
|
||||
int length,
|
||||
int gap,
|
||||
Validation validation) {
|
||||
|
||||
if (start > limit) {
|
||||
throw new IllegalArgumentException("start > limit");
|
||||
}
|
||||
fRangeStart = start;
|
||||
fRangeLimit = limit;
|
||||
fCurrentIndex = fRangeStart;
|
||||
fStorage = storage;
|
||||
fGap = gap;
|
||||
fGapLength = (storage==null? 0 : storage.length) - length;
|
||||
fValidation = validation;
|
||||
}
|
||||
|
||||
private void checkValidation() {
|
||||
|
||||
if (!fValidation.isValid()) {
|
||||
throw new Error("Iterator is no longer valid");
|
||||
}
|
||||
}
|
||||
|
||||
public char first()
|
||||
{
|
||||
return setIndex(fRangeStart);
|
||||
}
|
||||
|
||||
public char last()
|
||||
{
|
||||
return setIndex(fRangeLimit - 1);
|
||||
}
|
||||
|
||||
public char current()
|
||||
{
|
||||
checkValidation();
|
||||
if (fCurrentIndex < fRangeStart || fCurrentIndex >= fRangeLimit)
|
||||
return DONE;
|
||||
int i = (fCurrentIndex < fGap) ? fCurrentIndex : (fCurrentIndex + fGapLength);
|
||||
return fStorage[i];
|
||||
}
|
||||
|
||||
public char next()
|
||||
{
|
||||
checkValidation();
|
||||
fCurrentIndex++;
|
||||
if (fCurrentIndex >= fRangeLimit)
|
||||
{
|
||||
fCurrentIndex = fRangeLimit;
|
||||
return DONE;
|
||||
}
|
||||
int i = (fCurrentIndex < fGap) ? fCurrentIndex : (fCurrentIndex + fGapLength);
|
||||
return fStorage[i];
|
||||
}
|
||||
|
||||
public char previous()
|
||||
{
|
||||
fCurrentIndex--;
|
||||
if (fCurrentIndex >= fRangeStart)
|
||||
return current();
|
||||
fCurrentIndex = fRangeStart;
|
||||
return DONE;
|
||||
}
|
||||
|
||||
public char setIndex(int i)
|
||||
{
|
||||
if (i < fRangeStart || i > fRangeLimit)
|
||||
throw new IllegalArgumentException("Invalid position");
|
||||
fCurrentIndex = i;
|
||||
return current();
|
||||
}
|
||||
|
||||
public int getBeginIndex()
|
||||
{
|
||||
return fRangeStart;
|
||||
}
|
||||
|
||||
public int getEndIndex()
|
||||
{
|
||||
return fRangeLimit;
|
||||
}
|
||||
|
||||
public int getIndex()
|
||||
{
|
||||
return fCurrentIndex;
|
||||
}
|
||||
|
||||
public Object clone()
|
||||
{
|
||||
try {
|
||||
return super.clone();
|
||||
}
|
||||
catch (CloneNotSupportedException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
136
icu4j/src/com/ibm/richtext/styledtext/FastIntBinarySearch.java
Executable file
136
icu4j/src/com/ibm/richtext/styledtext/FastIntBinarySearch.java
Executable file
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
* @(#)$RCSfile: FastIntBinarySearch.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:45:10 $
|
||||
*
|
||||
* (C) Copyright IBM Corp. 1998-1999. All Rights Reserved.
|
||||
*
|
||||
* The program is provided "as is" without any warranty express or
|
||||
* implied, including the warranty of non-infringement and the implied
|
||||
* warranties of merchantibility and fitness for a particular purpose.
|
||||
* IBM will not be liable for any damages suffered by you as a result
|
||||
* of using the Program. In no event will IBM be liable for any
|
||||
* special, indirect or consequential damages or lost profits even if
|
||||
* IBM has been advised of the possibility of their occurrence. IBM
|
||||
* will not be liable for any third party claims against you.
|
||||
*/
|
||||
/*
|
||||
(C) Copyright Taligent, Inc. 1996 - All Rights Reserved
|
||||
(C) Copyright IBM Corp. 1996 - All Rights Reserved
|
||||
|
||||
The original version of this source code and documentation is copyrighted and
|
||||
owned by Taligent, Inc., a wholly-owned subsidiary of IBM. These materials are
|
||||
provided under terms of a License Agreement between Taligent and Sun. This
|
||||
technology is protected by multiple US and International patents. This notice and
|
||||
attribution to Taligent may not be removed.
|
||||
Taligent is a registered trademark of Taligent, Inc.
|
||||
*/
|
||||
|
||||
/*
|
||||
7/29/96
|
||||
Modified to search portions of an integer array. Should be retested.
|
||||
*/
|
||||
|
||||
package com.ibm.richtext.styledtext;
|
||||
|
||||
/**
|
||||
* This class searches a segment of an array of integers. The segment
|
||||
* must be sorted in ascending order (but this class does not verify this).
|
||||
* Also, this class aliases the array; if the array is modified later the
|
||||
* search results are undefined.
|
||||
*/
|
||||
final class FastIntBinarySearch
|
||||
{
|
||||
static final String COPYRIGHT =
|
||||
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
|
||||
private int dataArray[];
|
||||
private int auxStart;
|
||||
private int power;
|
||||
|
||||
private int fFirstIndex;
|
||||
|
||||
private static final int exp2[] = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072 };
|
||||
|
||||
public FastIntBinarySearch(int data[])
|
||||
{
|
||||
this(data, 0, data.length);
|
||||
}
|
||||
|
||||
public FastIntBinarySearch(int data[], int firstValidIndex, int validLength)
|
||||
{
|
||||
setData(data, firstValidIndex, validLength);
|
||||
}
|
||||
|
||||
public void setData(int data[]) {
|
||||
|
||||
setData(data, 0, data.length);
|
||||
}
|
||||
|
||||
public void setData(int data[], int firstValidIndex, int validLength) {
|
||||
|
||||
if (data.length < 1) throw new IllegalArgumentException();
|
||||
if (data.length >= exp2[exp2.length-1]) throw new IllegalArgumentException();
|
||||
|
||||
dataArray = data;
|
||||
fFirstIndex = firstValidIndex;
|
||||
|
||||
for (power = exp2.length-1; power > 0 && validLength < exp2[power]; power--) {}
|
||||
|
||||
// at this point, array.length >= 2^power
|
||||
|
||||
auxStart = validLength - exp2[power];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the index in the array of the first element which is at least
|
||||
* as large as <tt>value</tt>. If value is larger than the largest
|
||||
* element in the array the last valid index in the array is returned.
|
||||
*/
|
||||
public int findIndex(int value)
|
||||
{
|
||||
int index = exp2[power]-1 + fFirstIndex;
|
||||
if (value >= dataArray[auxStart + fFirstIndex]) {
|
||||
index += auxStart;
|
||||
}
|
||||
|
||||
// at this point, index is the "upper limit" of the search
|
||||
|
||||
switch (power) {
|
||||
case 17:
|
||||
if (value < dataArray[index-65536]) index -= 65536;
|
||||
case 16:
|
||||
if (value < dataArray[index-32768]) index -= 32768;
|
||||
case 15:
|
||||
if (value < dataArray[index-16384]) index -= 16384;
|
||||
case 14:
|
||||
if (value < dataArray[index-8192]) index -= 8192;
|
||||
case 13:
|
||||
if (value < dataArray[index-4096]) index -= 4096;
|
||||
case 12:
|
||||
if (value < dataArray[index-2048]) index -= 2048;
|
||||
case 11:
|
||||
if (value < dataArray[index-1024]) index -= 1024;
|
||||
case 10:
|
||||
if (value < dataArray[index-512]) index -= 512;
|
||||
case 9:
|
||||
if (value < dataArray[index-256]) index -= 256;
|
||||
case 8:
|
||||
if (value < dataArray[index-128]) index -= 128;
|
||||
case 7:
|
||||
if (value < dataArray[index-64]) index -= 64;
|
||||
case 6:
|
||||
if (value < dataArray[index-32]) index -= 32;
|
||||
case 5:
|
||||
if (value < dataArray[index-16]) index -= 16;
|
||||
case 4:
|
||||
if (value < dataArray[index-8]) index -= 8;
|
||||
case 3:
|
||||
if (value < dataArray[index-4]) index -= 4;
|
||||
case 2:
|
||||
if (value < dataArray[index-2]) index -= 2;
|
||||
case 1:
|
||||
if (value < dataArray[index-1]) index -= 1;
|
||||
case 0:
|
||||
if (value < dataArray[index]) index -= 1;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
}
|
38
icu4j/src/com/ibm/richtext/styledtext/MCharBuffer.java
Executable file
38
icu4j/src/com/ibm/richtext/styledtext/MCharBuffer.java
Executable file
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* @(#)$RCSfile: MCharBuffer.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:45:10 $
|
||||
*
|
||||
* (C) Copyright IBM Corp. 1998-1999. All Rights Reserved.
|
||||
*
|
||||
* The program is provided "as is" without any warranty express or
|
||||
* implied, including the warranty of non-infringement and the implied
|
||||
* warranties of merchantibility and fitness for a particular purpose.
|
||||
* IBM will not be liable for any damages suffered by you as a result
|
||||
* of using the Program. In no event will IBM be liable for any
|
||||
* special, indirect or consequential damages or lost profits even if
|
||||
* IBM has been advised of the possibility of their occurrence. IBM
|
||||
* will not be liable for any third party claims against you.
|
||||
*/
|
||||
package com.ibm.richtext.styledtext;
|
||||
import java.text.CharacterIterator;
|
||||
|
||||
/** A dynamic character array optimized for sequences of insert
|
||||
or delete operations in a local region. */
|
||||
|
||||
abstract class MCharBuffer
|
||||
{
|
||||
static final String COPYRIGHT =
|
||||
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
|
||||
abstract void replace(int start, int limit, MConstText text, int srcStart, int srcLimit);
|
||||
abstract void replace(int start, int limit, char[] srcChars, int srcStart, int srcLimit);
|
||||
abstract void replace(int start, int limit, String srcString, int srcStart, int srcLimit);
|
||||
abstract void replace(int start, int limit, char srcChar);
|
||||
abstract CharacterIterator createCharacterIterator(int start, int limit);
|
||||
abstract char at(int pos);
|
||||
abstract void at(int start, int limit, char[] dst, int dstStart);
|
||||
|
||||
abstract int length();
|
||||
|
||||
abstract int capacity();
|
||||
abstract void reserveCapacity(int pos, int length);
|
||||
abstract void compress();
|
||||
}
|
325
icu4j/src/com/ibm/richtext/styledtext/MConstText.java
Executable file
325
icu4j/src/com/ibm/richtext/styledtext/MConstText.java
Executable file
|
@ -0,0 +1,325 @@
|
|||
/*
|
||||
* @(#)$RCSfile: MConstText.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:45:10 $
|
||||
*
|
||||
* (C) Copyright IBM Corp. 1998-1999. All Rights Reserved.
|
||||
*
|
||||
* The program is provided "as is" without any warranty express or
|
||||
* implied, including the warranty of non-infringement and the implied
|
||||
* warranties of merchantibility and fitness for a particular purpose.
|
||||
* IBM will not be liable for any damages suffered by you as a result
|
||||
* of using the Program. In no event will IBM be liable for any
|
||||
* special, indirect or consequential damages or lost profits even if
|
||||
* IBM has been advised of the possibility of their occurrence. IBM
|
||||
* will not be liable for any third party claims against you.
|
||||
*/
|
||||
package com.ibm.richtext.styledtext;
|
||||
|
||||
import com.ibm.textlayout.attributes.AttributeMap;
|
||||
import java.text.CharacterIterator;
|
||||
import java.awt.datatransfer.DataFlavor;
|
||||
|
||||
/**
|
||||
* MConstText is a base class for text with multiple character and
|
||||
* paragraph styles. The text is a sequence of Unicode characters,
|
||||
* represented by <code>char</code>. Character and paragraph
|
||||
* styles are represented by the <code>AttributeMap</code> class.
|
||||
* <p>
|
||||
* Characters in the text are accessed with an integer index using the
|
||||
* <code>at</code> method.
|
||||
* Valid indices are between 0 and (length-1), where length is the number
|
||||
* of characters in the text. Additionally, the
|
||||
* characters in the text may be accessed through a
|
||||
* <code>java.text.CharacterIterator</code>.
|
||||
* <p>
|
||||
* Every character in the text has a character style associated with it,
|
||||
* represented by the <code>AttributeMap</code> class. The character
|
||||
* style for a particular character can be obtained using the
|
||||
* <code>characterStyleAt</code> method.
|
||||
* <p>
|
||||
* Each character in the text is contained in a paragraph. A paragraph
|
||||
* is a range of text including and terminated by a
|
||||
* paragraph separator (either <code>\n</code> or <code>U+2029</code>).
|
||||
* Every
|
||||
* paragraph has a paragraph style associated with it, represented
|
||||
* by the <code>AttributeMap</code> class. Paragraph boundaries and
|
||||
* styles can be obtained from the MConstText.
|
||||
* <p>
|
||||
* This class does not have methods for modifying the text or styles.
|
||||
* However, subclasses may add this capability, so it is not safe to
|
||||
* assume that an MConstText instance is immutable. In particular,
|
||||
* the MText class adds modification protocol to this class. Clients
|
||||
* can detect whether an MConstText has changed by keeping track of its
|
||||
* timestamp.
|
||||
* <p>
|
||||
* A DataFlavor for clipboard content is defined in this class. Using
|
||||
* this DataFlavor insures that all clients will recognize MConstText
|
||||
* content on the clipboard.
|
||||
* @see MText
|
||||
* @see AttributeMap
|
||||
* @see java.text.CharacterIterator
|
||||
* @see java.awt.datatransfer.DataFlavor
|
||||
*/
|
||||
public abstract class MConstText {
|
||||
|
||||
static final String COPYRIGHT =
|
||||
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
|
||||
/**
|
||||
* The DataFlavor for MConstText clipboard content. Used to
|
||||
* indicate that clipboard data has an MConstText representation.
|
||||
*/
|
||||
public static final DataFlavor styledTextFlavor =
|
||||
new DataFlavor(MConstText.class, "Styled Text");
|
||||
|
||||
protected MConstText() {
|
||||
}
|
||||
|
||||
//========================================================
|
||||
// CHARACTER ACCESS
|
||||
//========================================================
|
||||
/**
|
||||
* Return the character at offset <code>pos</code>.
|
||||
* @param pos a valid offset into the text
|
||||
* @returns the character at offset <code>pos</code>
|
||||
*/
|
||||
public abstract char at(int pos);
|
||||
|
||||
/**
|
||||
* Copy the characters in the range [<code>start</code>, <code>limit</code>)
|
||||
* into the array <code>dst</code>, beginning at <code>dstStart</code>.
|
||||
* @param start offset of first character which will be copied into the array
|
||||
* @param limit offset immediately after the last character which will be copied into the array
|
||||
* @param dst array in which to copy characters. The length of <code>dst</code> must be at least
|
||||
* (<code>dstStart + limit - start</code>).
|
||||
*/
|
||||
public abstract void extractChars(int start, int limit, char[] dst, int dstStart);
|
||||
|
||||
/**
|
||||
* Create an MConstText containing the characters and styles in the range
|
||||
* [<code>start</code>, <code>limit</code>).
|
||||
* @param start offset of first character in the new text
|
||||
* @param limit offset immediately after the last character in the new text
|
||||
* @return an MConstText object containing the characters and styles in the given range
|
||||
*/
|
||||
public abstract MConstText extract(int start, int limit);
|
||||
|
||||
/**
|
||||
* Create a <code>java.text.CharacterIterator</code> over all
|
||||
* of the characters in the text. Default implementation calls
|
||||
* <code>createCharacterIterator(0, length())</code>
|
||||
* @return a <code>java.text.CharacterIterator</code> over all
|
||||
* of the characters in the text
|
||||
*/
|
||||
public CharacterIterator createCharacterIterator() {
|
||||
|
||||
return createCharacterIterator(0, length());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a <code>java.text.CharacterIterator</code> over the
|
||||
* given range of characters in the text.
|
||||
* @param start the first index in the iteration range
|
||||
* @param limit the index after the last character in the iteration range
|
||||
* @return a <code>java.text.CharacterIterator</code> over the
|
||||
* given range
|
||||
*/
|
||||
public abstract CharacterIterator createCharacterIterator(int start,
|
||||
int limit);
|
||||
|
||||
|
||||
//========================================================
|
||||
// SIZE/CAPACITY
|
||||
//========================================================
|
||||
/**
|
||||
* Return the length of the MConstText object. The length is the number of characters in the text.
|
||||
* @return the length of the MConstText object
|
||||
*/
|
||||
public abstract int length();
|
||||
|
||||
//========================================================
|
||||
// Character styles
|
||||
//========================================================
|
||||
|
||||
/**
|
||||
* Return the index of the first character in the character style run
|
||||
* containing pos. All characters in a style run have the same character
|
||||
* style.
|
||||
* @returns the style at offset <code>pos</code>
|
||||
*/
|
||||
public abstract int characterStyleStart(int pos);
|
||||
|
||||
/**
|
||||
* Return the index after the last character in the character style run
|
||||
* containing pos. All characters in a style run have the same character
|
||||
* style.
|
||||
* @returns the style at offset <code>pos</code>
|
||||
*/
|
||||
public abstract int characterStyleLimit(int pos);
|
||||
|
||||
/**
|
||||
* Return the style applied to the character at offset <code>pos</code>.
|
||||
* @param pos a valid offset into the text
|
||||
* @returns the style at offset <code>pos</code>
|
||||
*/
|
||||
public abstract AttributeMap characterStyleAt(int pos);
|
||||
|
||||
//========================================================
|
||||
// PARAGRAPH BOUNDARIES
|
||||
//========================================================
|
||||
/**
|
||||
* Return the start of the paragraph containing the character at offset <code>pos</code>.
|
||||
* @param pos a valid offset into the text
|
||||
* @returns the start of the paragraph containing the character at offset <code>pos</code>
|
||||
*/
|
||||
public abstract int paragraphStart(int pos);
|
||||
|
||||
/**
|
||||
* Return the limit of the paragraph containing the character at offset <code>pos</code>.
|
||||
* @param pos a valid offset into the text
|
||||
* @returns the limit of the paragraph containing the character at offset <code>pos</code>
|
||||
*/
|
||||
public abstract int paragraphLimit(int pos);
|
||||
|
||||
/**
|
||||
* Return the paragraph style applied to the paragraph containing offset <code>pos</code>.
|
||||
* @param pos a valid offset into the text
|
||||
* @returns the paragraph style in effect at <code>pos</code>
|
||||
*/
|
||||
public abstract AttributeMap paragraphStyleAt(int pos);
|
||||
|
||||
/**
|
||||
* Return the current time stamp. The time stamp is
|
||||
* incremented whenever the contents of the MConstText changes.
|
||||
* @return the current paragraph style time stamp
|
||||
*/
|
||||
public abstract int getTimeStamp();
|
||||
|
||||
/**
|
||||
* Return the start of the damaged range. If the start is not less
|
||||
* than the the limit of the damaged range, then the damaged range
|
||||
* is empty.
|
||||
* @return the start of the damaged range
|
||||
* @see #damagedRangeLimit
|
||||
* @see MText#resetDamagedRange
|
||||
*/
|
||||
public abstract int damagedRangeStart();
|
||||
|
||||
/**
|
||||
* Return the limit of the damaged range. If the start is not less
|
||||
* than the the limit of the damaged range, then the damaged range
|
||||
* is empty.
|
||||
* @return the start of the damaged range
|
||||
* @see #damagedRangeStart
|
||||
* @see MText#resetDamagedRange
|
||||
*/
|
||||
public abstract int damagedRangeLimit();
|
||||
|
||||
//========================================================
|
||||
// Equality and hashCode
|
||||
//========================================================
|
||||
/**
|
||||
* Compare this to another Object for equality. This is
|
||||
* equal to rhs if rhs is an MConstText which is equal
|
||||
* to this.
|
||||
* @param rhs Object to compare to
|
||||
* @return true if this equals <code>rhs</code>
|
||||
*/
|
||||
public final boolean equals(Object rhs) {
|
||||
|
||||
MConstText otherText;
|
||||
|
||||
try {
|
||||
otherText = (MConstText) rhs;
|
||||
}
|
||||
catch(ClassCastException e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return equals(otherText);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare this to another MConstText for equality. This is
|
||||
* equal to rhs if the characters and styles in rhs are the
|
||||
* same as this. Subclasses may override this implementation
|
||||
* for efficiency, but they should preserve these semantics.
|
||||
* Determining that two MConstText instances are equal may be
|
||||
* an expensive operation, since every character and style must
|
||||
* be compared.
|
||||
* @param rhs Object to compare to
|
||||
* @return true if this equals <code>rhs</code>
|
||||
*/
|
||||
public boolean equals(MConstText rhs) {
|
||||
|
||||
if (rhs == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (rhs == this) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (hashCode() != rhs.hashCode()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int length = length();
|
||||
if (length != rhs.length()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i=0; i < length; i++) {
|
||||
if (i < length && at(i) != rhs.at(i)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (int start = 0; start < length;) {
|
||||
if (!characterStyleAt(start).equals(rhs.characterStyleAt(start))) {
|
||||
return false;
|
||||
}
|
||||
int limit = characterStyleLimit(start);
|
||||
if (limit != rhs.characterStyleLimit(start)) {
|
||||
return false;
|
||||
}
|
||||
start = limit;
|
||||
}
|
||||
|
||||
for (int start = 0; start < length;) {
|
||||
|
||||
if (!paragraphStyleAt(start).equals(rhs.paragraphStyleAt(start))) {
|
||||
return false;
|
||||
}
|
||||
start = paragraphLimit(start);
|
||||
}
|
||||
|
||||
return paragraphStyleAt(length).equals(rhs.paragraphStyleAt(length));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the hashCode for this MConstText. An empty MConstText
|
||||
* has hashCode 0; a nonempty MConstText's hashCode is
|
||||
* <blockquote><pre>
|
||||
* at(0) +
|
||||
* at(length/2)*31^1 +
|
||||
* at(length-1)*31^2 +
|
||||
* characterStyleAt(0).hashCode()*31^3 +
|
||||
* paragraphStyleAt(length-1).hashCode()*31^4
|
||||
* </pre></blockquote>
|
||||
* where <code>^</code> is exponentiation (not bitwise XOR).
|
||||
*/
|
||||
public final int hashCode() {
|
||||
|
||||
int hashCode = 0;
|
||||
int length = length();
|
||||
|
||||
if (length > 0) {
|
||||
hashCode = paragraphStyleAt(length-1).hashCode();
|
||||
hashCode = hashCode*31 + characterStyleAt(0).hashCode();
|
||||
hashCode = hashCode*31 + at(length-1);
|
||||
hashCode = hashCode*31 + at(length/2);
|
||||
hashCode = hashCode*31 + at(0);
|
||||
}
|
||||
|
||||
return hashCode;
|
||||
}
|
||||
}
|
94
icu4j/src/com/ibm/richtext/styledtext/MParagraphBuffer.java
Executable file
94
icu4j/src/com/ibm/richtext/styledtext/MParagraphBuffer.java
Executable file
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* @(#)$RCSfile: MParagraphBuffer.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:45:10 $
|
||||
*
|
||||
* (C) Copyright IBM Corp. 1998-1999. All Rights Reserved.
|
||||
*
|
||||
* The program is provided "as is" without any warranty express or
|
||||
* implied, including the warranty of non-infringement and the implied
|
||||
* warranties of merchantibility and fitness for a particular purpose.
|
||||
* IBM will not be liable for any damages suffered by you as a result
|
||||
* of using the Program. In no event will IBM be liable for any
|
||||
* special, indirect or consequential damages or lost profits even if
|
||||
* IBM has been advised of the possibility of their occurrence. IBM
|
||||
* will not be liable for any third party claims against you.
|
||||
*/
|
||||
package com.ibm.richtext.styledtext;
|
||||
|
||||
import java.io.Serializable;
|
||||
import com.ibm.textlayout.attributes.AttributeMap;
|
||||
|
||||
abstract class MParagraphBuffer
|
||||
{
|
||||
static final String COPYRIGHT =
|
||||
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
|
||||
|
||||
/**
|
||||
* Returns the start of the paragraph containing offset <tt>pos</tt>.
|
||||
*/
|
||||
abstract int paragraphStart(int pos);
|
||||
|
||||
/**
|
||||
* Returns the limit of the paragraph containing offset <tt>pos</tt>.
|
||||
*/
|
||||
abstract int paragraphLimit(int pos);
|
||||
|
||||
/**
|
||||
* Returns the style of the paragraph containing offset <tt>pos</tt>.
|
||||
*/
|
||||
abstract AttributeMap paragraphStyleAt(int offset);
|
||||
|
||||
/**
|
||||
* Process a character insertion at offset <tt>start</tt>.
|
||||
* If a paragraph break was inserted, propogate paragraph style at
|
||||
* <tt>start</tt> to new paragraph.
|
||||
*/
|
||||
abstract void insertText(int start, char insertedChar);
|
||||
|
||||
/**
|
||||
* Process character insertion at offset <tt>start</tt>.
|
||||
* Each new paragraph gets paragraph style at
|
||||
* <tt>start</tt>.
|
||||
*/
|
||||
abstract void insertText(int start,
|
||||
char[] srcChars,
|
||||
int srcStart,
|
||||
int srcLimit);
|
||||
|
||||
/**
|
||||
* Process deletion by removing paragraph breaks contained in
|
||||
* deleted range. Propogate paragraph styles backward, if necessary.
|
||||
*/
|
||||
abstract void deleteText(int start,
|
||||
int limit,
|
||||
int[] damagedRange);
|
||||
|
||||
/*
|
||||
* Replace paragraph breaks/styles between start and limit with paragraph breaks/styles
|
||||
* from <tt>srcText</tt>.
|
||||
* @param start an offset into the text
|
||||
* @param limit the index after the last character to replace
|
||||
* @param srcText the text from which new paragraphs are taken
|
||||
* @param srcStart the start of the range in <code>srcText</code> to copy
|
||||
* @param srcLimit the first index after the range in <code>srcText</code> to copy
|
||||
*/
|
||||
abstract void replace(int start,
|
||||
int limit,
|
||||
MConstText srcText,
|
||||
int srcStart,
|
||||
int srcLimit,
|
||||
int[] damagedRange);
|
||||
|
||||
/**
|
||||
* Set the style of all paragraphs containing offsets in the range [start, limit) to
|
||||
* <tt>style</tt>.
|
||||
*/
|
||||
abstract boolean modifyParagraphStyles(int start,
|
||||
int limit,
|
||||
StyleModifier modifier,
|
||||
int[] damagedRange);
|
||||
|
||||
/**
|
||||
* Minimize the amount of memory used by this object.
|
||||
*/
|
||||
abstract void compress();
|
||||
}
|
112
icu4j/src/com/ibm/richtext/styledtext/MStyleBuffer.java
Executable file
112
icu4j/src/com/ibm/richtext/styledtext/MStyleBuffer.java
Executable file
|
@ -0,0 +1,112 @@
|
|||
/*
|
||||
* @(#)$RCSfile: MStyleBuffer.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:45:10 $
|
||||
*
|
||||
* (C) Copyright IBM Corp. 1998-1999. All Rights Reserved.
|
||||
*
|
||||
* The program is provided "as is" without any warranty express or
|
||||
* implied, including the warranty of non-infringement and the implied
|
||||
* warranties of merchantibility and fitness for a particular purpose.
|
||||
* IBM will not be liable for any damages suffered by you as a result
|
||||
* of using the Program. In no event will IBM be liable for any
|
||||
* special, indirect or consequential damages or lost profits even if
|
||||
* IBM has been advised of the possibility of their occurrence. IBM
|
||||
* will not be liable for any third party claims against you.
|
||||
*/
|
||||
package com.ibm.richtext.styledtext;
|
||||
import com.ibm.textlayout.attributes.AttributeMap;
|
||||
|
||||
/*
|
||||
8/1/96
|
||||
Style -> ResolvedStyle
|
||||
8/7/96 jf
|
||||
added countStyles and getStyles protocol
|
||||
8/13/96
|
||||
ResolvedStyle->Style
|
||||
8/22/96 jf
|
||||
Removed the setIterator methods.
|
||||
*/
|
||||
/*
|
||||
* MStyleBuffer is the abstract interface for a class which maintains
|
||||
* style runs in an <tt>MText</tt>. A "style run" consists of a
|
||||
* style and the interval on which the style applies.
|
||||
* <p>
|
||||
* MStyleBuffer includes methods to call when text is inserted into
|
||||
* or deleted from the <tt>MText</tt>. These methods update the
|
||||
* style runs in accordance with the commonly accepted behavior for
|
||||
* style runs.
|
||||
* <p>
|
||||
* Additionally, MStyleBuffer provides methods for replacing the style runs on a
|
||||
* text range with another set of style runs. MStyleBuffer does not do style "combining" (for
|
||||
* example, adding the bold attribute to text which is italicized); clients are
|
||||
* responsible for computing the combined styles, and passing these styles into
|
||||
* MStyleBuffer.
|
||||
* <p>
|
||||
* MStyleBuffer supplies a method for replacing the style runs on a text range with the runs
|
||||
* represented in an <tt>MStyleRunIterator</tt>. This is useful for implementing paste
|
||||
* operations, in which the style runs on a range of text are replaced by style runs
|
||||
* from an external source.
|
||||
* <p>
|
||||
*
|
||||
* @author John Raley
|
||||
*
|
||||
* @see AttributeMap
|
||||
* @see MText
|
||||
*/
|
||||
abstract class MStyleBuffer
|
||||
{
|
||||
|
||||
static final String COPYRIGHT =
|
||||
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
|
||||
/**
|
||||
* Respond to an insertion in the text. The length of the last style run which
|
||||
* begins before <tt>start</tt> is increased by <tt>limit-start</tt>.
|
||||
* @param start the offset where the insertion began
|
||||
* @param limit the offset where the insertion ended
|
||||
*/
|
||||
abstract void insertText(int start, int limit);
|
||||
|
||||
/**
|
||||
* Respond to a deletion in the text. The last style run before
|
||||
* <tt>start</tt> is truncated to end at <tt>start</tt>. The
|
||||
* style run containing (<tt>start</tt>+<tt>length</tt>) is set to begin
|
||||
* at (<tt>start</tt>+<tt>length</tt>). Runs in between are deleted.
|
||||
* If the deletion occurs entirely within one style run, the length of the style
|
||||
* run is reduced by <tt>length</tt>.
|
||||
* @param start the offset where the deletion began
|
||||
* @param length the offset where the deletion ended
|
||||
*/
|
||||
abstract void deleteText(int start, int limit);
|
||||
|
||||
/*
|
||||
* Replace style runs between offsets <tt>start</tt> and <tt>limit</tt> with styles in
|
||||
* <tt>iter</tt>. This method can be used to perform a "paste" operation.
|
||||
* @param start the offset where the replacement begins
|
||||
* @param limit the offset where the replacement ends
|
||||
* @param iter an <tt>MStyleRunIterator</tt> containing style runs which will replace old
|
||||
* style runs.
|
||||
*/
|
||||
abstract void replace(int start, int limit, MConstText srcText, int srcStart, int srcLimit);
|
||||
|
||||
abstract int styleStart(int pos);
|
||||
abstract int styleLimit(int pos);
|
||||
|
||||
/**
|
||||
* Return style at location <tt>pos</tt>.
|
||||
* @param pos an offset into the text
|
||||
* @returns the style of the character at <tt>offset</tt>
|
||||
*/
|
||||
abstract AttributeMap styleAt(int pos);
|
||||
|
||||
/**
|
||||
* Return true if styles were modified.
|
||||
*/
|
||||
abstract boolean modifyStyles(int start,
|
||||
int limit,
|
||||
StyleModifier modifier,
|
||||
int[] damagedRange);
|
||||
|
||||
/**
|
||||
* Minimize the amount of memory used by this object.
|
||||
*/
|
||||
abstract void compress();
|
||||
}
|
113
icu4j/src/com/ibm/richtext/styledtext/MTabRuler.java
Executable file
113
icu4j/src/com/ibm/richtext/styledtext/MTabRuler.java
Executable file
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* @(#)$RCSfile: MTabRuler.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:45:10 $
|
||||
*
|
||||
* (C) Copyright IBM Corp. 1998-1999. All Rights Reserved.
|
||||
*
|
||||
* The program is provided "as is" without any warranty express or
|
||||
* implied, including the warranty of non-infringement and the implied
|
||||
* warranties of merchantibility and fitness for a particular purpose.
|
||||
* IBM will not be liable for any damages suffered by you as a result
|
||||
* of using the Program. In no event will IBM be liable for any
|
||||
* special, indirect or consequential damages or lost profits even if
|
||||
* IBM has been advised of the possibility of their occurrence. IBM
|
||||
* will not be liable for any third party claims against you.
|
||||
*/
|
||||
package com.ibm.richtext.styledtext;
|
||||
|
||||
/**
|
||||
* This interface represents a sequence of TabStops, ordered by position.
|
||||
* The first
|
||||
* TabStop in the ruler can be obtained with the <code>firstTab</code>
|
||||
* method; subsequent TabStops are obtained with the <code>nextTab</code>
|
||||
* method.
|
||||
* <p>
|
||||
* If a TabStop with type <code>TabStop.kAuto</code> is returned, all tabs
|
||||
* after that TabStop will also have type <code>TabStop.kAuto</code>, and
|
||||
* their positions will be multiples of <code>autoSpacing</code>.
|
||||
* @see TabStop
|
||||
*/
|
||||
public abstract class MTabRuler
|
||||
{
|
||||
static final String COPYRIGHT =
|
||||
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
|
||||
/**
|
||||
* Return first tab in the ruler. If an autoTab, it is at position zero, and
|
||||
* all subsequent tabs will be autotabs at autoSpacing intervals.
|
||||
*/
|
||||
public abstract TabStop firstTab();
|
||||
|
||||
/**
|
||||
* Return the first tab in the ruler with fPosition > position. If it is an
|
||||
* autotab, it is at an increment of autoSpacing, and all subsequent tabs will be
|
||||
* autotabs at autoSpacing intervals.
|
||||
*/
|
||||
public abstract TabStop nextTab(int position);
|
||||
|
||||
/**
|
||||
* Return the interval for autotabs.
|
||||
*/
|
||||
public abstract int autoSpacing();
|
||||
|
||||
/**
|
||||
* Compute the hashCode for this ruler. The hashCode is the
|
||||
* hashCode of the first tab multiplied by the autoSpacing
|
||||
* interval.
|
||||
*/
|
||||
public final int hashCode() {
|
||||
|
||||
return firstTab().hashCode() * autoSpacing();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this tab ruler contains the given tab.
|
||||
* @param tabToTest the tab to search for
|
||||
* @return true if this tab ruler contains <code>tabToTest</code>
|
||||
*/
|
||||
public boolean containsTab(TabStop tabToTest) {
|
||||
|
||||
for (TabStop tab = firstTab();
|
||||
tab.getType() != TabStop.kAuto;
|
||||
tab = nextTab(tab.getPosition())) {
|
||||
if (tab.getPosition() >= tabToTest.getPosition()) {
|
||||
return tabToTest.equals(tab);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a tab ruler identical to this ruler, except with the
|
||||
* given tab added. This ruler is not modified.
|
||||
* @param tabToAdd the tab to add to the new tab ruler
|
||||
* @return an MTabRuler resulting from this operation
|
||||
*/
|
||||
public MTabRuler addTab(TabStop tabToAdd) {
|
||||
|
||||
return StandardTabRuler.addTabToRuler(this, tabToAdd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a tab ruler identical to the given ruler, except with the
|
||||
* tab at the given position removed. This ruler is not modified.
|
||||
* @param position the position of the tab to remove from the new tab ruler
|
||||
* @return an MTabRuler resulting from this operation
|
||||
*/
|
||||
public MTabRuler removeTab(int position) {
|
||||
|
||||
return StandardTabRuler.removeTabFromRuler(this, position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a tab ruler identical to this ruler, except with the
|
||||
* tab at position <code>fromPosition</code> moved to position
|
||||
* <code>toPosition</code>. This ruler is not modified.
|
||||
* @param fromPosition the position of the tab to move
|
||||
* @param toPosition the new position of the tab
|
||||
* @return an MTabRuler resulting from this operation
|
||||
*/
|
||||
public MTabRuler moveTab(int fromPosition, int toPosition) {
|
||||
|
||||
return StandardTabRuler.moveTabOnRuler(this, fromPosition, toPosition);
|
||||
}
|
||||
}
|
243
icu4j/src/com/ibm/richtext/styledtext/MText.java
Executable file
243
icu4j/src/com/ibm/richtext/styledtext/MText.java
Executable file
|
@ -0,0 +1,243 @@
|
|||
/*
|
||||
* @(#)$RCSfile: MText.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:45:10 $
|
||||
*
|
||||
* (C) Copyright IBM Corp. 1998-1999. All Rights Reserved.
|
||||
*
|
||||
* The program is provided "as is" without any warranty express or
|
||||
* implied, including the warranty of non-infringement and the implied
|
||||
* warranties of merchantibility and fitness for a particular purpose.
|
||||
* IBM will not be liable for any damages suffered by you as a result
|
||||
* of using the Program. In no event will IBM be liable for any
|
||||
* special, indirect or consequential damages or lost profits even if
|
||||
* IBM has been advised of the possibility of their occurrence. IBM
|
||||
* will not be liable for any third party claims against you.
|
||||
*/
|
||||
package com.ibm.richtext.styledtext;
|
||||
|
||||
import com.ibm.textlayout.attributes.AttributeMap;
|
||||
|
||||
/*
|
||||
Change history:
|
||||
|
||||
10/29/96 jef split the character and paragraph style access functions
|
||||
8/14/96 sfb eliminated StyleSheetIterator
|
||||
8/21/96 jef completed abstract interface (changed iterator classes etc.)
|
||||
1/30/97 rtg cleaned up interface, brought in functions from SimpleTextView
|
||||
7/31/98 jbr switched from Style to AttributeMap
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* This class is a mutable extension of MConstText. It has methods for
|
||||
* inserting, appending, replacing, and removing styled text. Additionally,
|
||||
* it has methods for modifying paragraph and character styles.
|
||||
* <p>
|
||||
* Styled characters (from another <code>MConstText</code> instance) added
|
||||
* to the text retain their original character styles. The style of plain characters
|
||||
* (specified as a <code>char</code> or <code>char[]</code>) is always
|
||||
* specified explicitly when they are added to the text. MText does not do
|
||||
* character style "propagation", where unstyled characters take on the
|
||||
* style of previous characters. Clients can implement this behavior by
|
||||
* specifying the styles to propagate.
|
||||
* <p>
|
||||
* When unstyled characters are added to the text, their paragraph style
|
||||
* is the paragraph style in effect immediately after the last new character.
|
||||
* If the characters contain paragraph separators, then every new paragraph
|
||||
* will have the same paragraph style. When styled characters are added
|
||||
* to the text, their resulting paragraph style is determined by the
|
||||
* following rule:
|
||||
* <blockquote>
|
||||
* The paragraph styles in the new text
|
||||
* become the paragraph styles in the target text, with the exception of the
|
||||
* last paragraph in the new text, which takes on the paragraph style in
|
||||
* effect immediately after the inserted text.
|
||||
* If the new text is added at the end of the target text, the new text's
|
||||
* paragraph styles take effect in any paragraph affected by the addition.
|
||||
* </blockquote>
|
||||
* For example, suppose there is a single paragraph of text with style 'A',
|
||||
* delimited with a paragraph separator 'P':
|
||||
* <blockquote>
|
||||
* AAAAAAP
|
||||
* </blockquote>
|
||||
* Suppose the following styled paragraphs are inserted into the above text
|
||||
* after the fourth character:
|
||||
* <blockquote>
|
||||
* BBBBPCCCPDDD
|
||||
* </blockquote>
|
||||
* Then the original paragraph style of each character is:
|
||||
* <blockquote>
|
||||
* AAAABBBBPCCCPDDDAAP
|
||||
* </blockquote>
|
||||
* The resulting paragraph styles are:
|
||||
* <blockquote>
|
||||
* BBBBBBBBPCCCPAAAAAP
|
||||
* </blockquote>
|
||||
* Similarly, if characters are deleted, the paragraph style immediately
|
||||
* after the deletion takes effect on the paragraph containing the deletion.
|
||||
* So, if characters 4-16 were deleted in the example above, the paragraph
|
||||
* styles would be:
|
||||
* <blockquote>
|
||||
* AAAAAAP
|
||||
* </blockquote>
|
||||
* This paragraph-style propagation policy is sometimes referred to as <strong>
|
||||
* following styles win</strong>, since styles at the end of the paragraph
|
||||
* become the style for the entire paragraph.
|
||||
* <p>
|
||||
* This class can accumulate a <strong>damaged range</strong> - an interval in
|
||||
* which characters, character styles, or paragraph styles have changed. This is
|
||||
* useful for clients such as text editors which reformat and draw text after
|
||||
* changes. Usually the damaged range is exactly the range of characters
|
||||
* operated upon; however, larger ranges may be damaged if paragraph styles
|
||||
* change.
|
||||
* @see StyleModifier
|
||||
*/
|
||||
|
||||
public abstract class MText extends MConstText
|
||||
{
|
||||
static final String COPYRIGHT =
|
||||
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
|
||||
protected MText() {
|
||||
}
|
||||
|
||||
//==================================================
|
||||
// MAIN CHARACTER MODIFICATION FUNCTIONS
|
||||
//==================================================
|
||||
/**
|
||||
* Replace the characters and styles in the range [<code>start</code>, <code>limit</code>) with the characters
|
||||
* and styles in <code>srcText</code> in the range [<code>srcStart</code>, <code>srcLimit</code>). <code>srcText</code> is not
|
||||
* modified.
|
||||
* @param start the offset at which the replace operation begins
|
||||
* @param limit the offset at which the replace operation ends. The character and style at
|
||||
* <code>limit</code> is not modified.
|
||||
* @param srcText the source for the new characters and styles
|
||||
* @param srcStart the offset into <code>srcText</code> where new characters and styles will be obtained
|
||||
* @param srcLimit the offset into <code>srcText</code> where the new characters and styles end
|
||||
*/
|
||||
public abstract void replace(int start, int limit, MConstText srcText, int srcStart, int srcLimit);
|
||||
|
||||
/**
|
||||
* Replace the characters and styles in the range [<code>start</code>, <code>limit</code>) with the characters
|
||||
* and styles in <code>srcText</code>. <code>srcText</code> is not
|
||||
* modified.
|
||||
* @param start the offset at which the replace operation begins
|
||||
* @param limit the offset at which the replace operation ends. The character and style at
|
||||
* <code>limit</code> is not modified.
|
||||
* @param srcText the source for the new characters and styles
|
||||
*/
|
||||
public abstract void replace(int start, int limit, MConstText text);
|
||||
|
||||
/**
|
||||
* Replace the characters in the range [<code>start</code>, <code>limit</code>) with the characters
|
||||
* in <code>srcChars</code> in the range [<code>srcStart</code>, <code>srcLimit</code>). New characters take on the style
|
||||
* <code>charsStyle</code>.
|
||||
* <code>srcChars</code> is not modified.
|
||||
* @param start the offset at which the replace operation begins
|
||||
* @param limit the offset at which the replace operation ends. The character at
|
||||
* <code>limit</code> is not modified.
|
||||
* @param srcChars the source for the new characters
|
||||
* @param srcStart the offset into <code>srcChars</code> where new characters will be obtained
|
||||
* @param srcLimit the offset into <code>srcChars</code> where the new characters end
|
||||
* @param charsStyle the style of the new characters
|
||||
*/
|
||||
public abstract void replace(int start, int limit, char[] srcChars, int srcStart, int srcLimit, AttributeMap charsStyle);
|
||||
|
||||
/**
|
||||
* Replace the characters in the range [<code>start</code>, <code>limit</code>) with the character <code>srcChar</code>.
|
||||
* The new character takes on the style <code>charStyle</code>
|
||||
* @param start the offset at which the replace operation begins
|
||||
* @param limit the offset at which the replace operation ends. The character at
|
||||
* <code>limit</code> is not modified.
|
||||
* @param srcChar the new character
|
||||
* @param charsStyle the style of the new character
|
||||
*/
|
||||
public abstract void replace(int start, int limit, char srcChar, AttributeMap charStyle);
|
||||
|
||||
/**
|
||||
* Replace the entire contents of this MText (both characters and styles) with
|
||||
* the contents of <code>srcText</code>.
|
||||
* @param srcText the source for the new characters and styles
|
||||
*/
|
||||
public abstract void replaceAll(MConstText srcText);
|
||||
|
||||
/**
|
||||
* Insert the contents of <code>srcText</code> (both characters and styles) into this
|
||||
* MText at the position specified by <code>pos</code>.
|
||||
* @param pos The character offset where the new text is to be inserted.
|
||||
* @param srcText The text to insert. */
|
||||
public abstract void insert(int pos, MConstText srcText);
|
||||
|
||||
/**
|
||||
* Append the contents of <code>srcText</code> (both characters and styles) to the
|
||||
* end of this MText.
|
||||
* @param srcText The text to append. */
|
||||
public abstract void append(MConstText srcText);
|
||||
|
||||
/**
|
||||
* Delete the specified range of characters (and styles).
|
||||
* @param start Offset of the first character to delete.
|
||||
* @param limit Offset of the first character after the range to delete. */
|
||||
public abstract void remove(int start, int limit);
|
||||
|
||||
/**
|
||||
* Delete all characters and styles.
|
||||
*/
|
||||
public abstract void remove();
|
||||
|
||||
/**
|
||||
* Create an MText containing the characters and styles in the range
|
||||
* [<code>start</code>, <code>limit</code>).
|
||||
* @param start offset of first character in the new text
|
||||
* @param limit offset immediately after the last character in the new text
|
||||
* @return an MConstText object containing the characters and styles in the given range
|
||||
*/
|
||||
public abstract MText extractWritable(int start, int limit);
|
||||
|
||||
|
||||
//==================================================
|
||||
// STORAGE MANAGEMENT
|
||||
//==================================================
|
||||
|
||||
/**
|
||||
* Minimize the amount of memory used by the MText object.
|
||||
*/
|
||||
public abstract void compress();
|
||||
|
||||
//==================================================
|
||||
// STYLE MODIFICATION
|
||||
//==================================================
|
||||
|
||||
/**
|
||||
* Set the character style of all characters in the MText object to
|
||||
* <code>AttributeMap.EMPTY_ATTRIBUTE_MAP</code>.
|
||||
*/
|
||||
public abstract void removeCharacterStyles();
|
||||
|
||||
/**
|
||||
* Invoke the given modifier on all character styles from start to limit.
|
||||
* @param modifier the modifier to apply to the range.
|
||||
* @param start the start of the range of text to modify.
|
||||
* @param limit the limit of the range of text to modify.
|
||||
*/
|
||||
public abstract void modifyCharacterStyles(int start, int limit, StyleModifier modifier);
|
||||
|
||||
/**
|
||||
* Invoke the given modifier on all paragraph styles in paragraphs
|
||||
* containing characters in the range [start, limit).
|
||||
* @param modifier the modifier to apply to the range.
|
||||
* @param start the start of the range of text to modify.
|
||||
* @param limit the limit of the range of text to modify.
|
||||
*/
|
||||
public abstract void modifyParagraphStyles(int start, int limit, StyleModifier modifier);
|
||||
|
||||
//==================================================
|
||||
// DAMAGED RANGE
|
||||
//==================================================
|
||||
/**
|
||||
* Reset the damaged range to an empty interval, and begin accumulating the damaged
|
||||
* range. The damaged range includes every index where a character, character style,
|
||||
* or paragraph style has changed.
|
||||
* @see #damagedRangeStart
|
||||
* @see #damagedRangeLimit
|
||||
*/
|
||||
public abstract void resetDamagedRange();
|
||||
}
|
716
icu4j/src/com/ibm/richtext/styledtext/ParagraphBuffer.java
Executable file
716
icu4j/src/com/ibm/richtext/styledtext/ParagraphBuffer.java
Executable file
|
@ -0,0 +1,716 @@
|
|||
/*
|
||||
* @(#)$RCSfile: ParagraphBuffer.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:45:10 $
|
||||
*
|
||||
* (C) Copyright IBM Corp. 1998-1999. All Rights Reserved.
|
||||
*
|
||||
* The program is provided "as is" without any warranty express or
|
||||
* implied, including the warranty of non-infringement and the implied
|
||||
* warranties of merchantibility and fitness for a particular purpose.
|
||||
* IBM will not be liable for any damages suffered by you as a result
|
||||
* of using the Program. In no event will IBM be liable for any
|
||||
* special, indirect or consequential damages or lost profits even if
|
||||
* IBM has been advised of the possibility of their occurrence. IBM
|
||||
* will not be liable for any third party claims against you.
|
||||
*/
|
||||
package com.ibm.richtext.styledtext;
|
||||
|
||||
import java.io.Externalizable;
|
||||
import java.io.ObjectInput;
|
||||
import java.io.ObjectOutput;
|
||||
import java.io.IOException;
|
||||
|
||||
import com.ibm.textlayout.attributes.AttributeMap;
|
||||
|
||||
/*
|
||||
Right now, you have to construct this class with a charBuffer. That's pretty ugly... */
|
||||
|
||||
/*
|
||||
8/8/96
|
||||
Added replace method, which reads styles from a ParagraphIterator.
|
||||
Also, added a constructor which takes a ParagraphIterator.
|
||||
These methods are for copy/paste support.
|
||||
|
||||
8/22/96
|
||||
Replace method (which takes an iterator as an argument) tests for a
|
||||
0-length iterator.
|
||||
|
||||
9/30/96
|
||||
{jbr} modified paragraphLimit();
|
||||
|
||||
10/23/96
|
||||
This class now maintains paragraph styles. Also has a timestamp.
|
||||
|
||||
10/25/96
|
||||
Holds on to Style instead of Style.
|
||||
|
||||
7/31/98 Switched to AttributeMap
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* This class stores offsets where paragraph breaks occur, and the style applied to
|
||||
* each paragraph.
|
||||
*
|
||||
* The offsets where paragraph breaks occur are stored in a RunArray object. This is
|
||||
* not strictly necessary, but it makes scanning the text for paragraph breaks unnecessary.
|
||||
* However, it makes determining where paragraphs start a little confusing. If there is a
|
||||
* paragraph break at offset p, then there will be a paragraph start at offset p+1.
|
||||
* If the last character in the text is a paragraph break, there will be a run array entry
|
||||
* for that character (and also a paragraph style for that paragraph, even though the
|
||||
* style does not apply to any text).
|
||||
*
|
||||
* The style of the first paragraph in the text is in the fFirstStyle member. Other
|
||||
* paragraph styles are stored in the fStyleTable array, in the following manner: the
|
||||
* paragraph with begins at offset fRunArray.fRunStart[i]+1 has style fStyleTable[i].
|
||||
* The style table's "gap range" is kept in sync with the RunArray.
|
||||
*
|
||||
* This class propogates paragraph styles in the "Microsoft Word" fashion: styles
|
||||
* propogate backward from paragraph breaks.
|
||||
*
|
||||
* This class maintains a time stamp, which changes every time extra formatting (formatting
|
||||
* on a range other than the current selection) is needed; for example, when a paragraph
|
||||
* break is removed.
|
||||
*/
|
||||
|
||||
|
||||
final class ParagraphBuffer extends MParagraphBuffer implements Externalizable {
|
||||
|
||||
static final String COPYRIGHT =
|
||||
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
|
||||
private static final int kInitialSize = 10;
|
||||
private static final int CURRENT_VERSION = 1;
|
||||
private static final long serialVersionUID = 22356934;
|
||||
|
||||
private RunArray fRunArray;
|
||||
private AttributeMap[] fStyleTable;
|
||||
private AttributeMap fFirstStyle;
|
||||
|
||||
private static final boolean isParagraphBreak(char c) {
|
||||
|
||||
return c =='\u2029' || c == '\n';
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new paragraph buffer from the characters in <tt>charBuffer</tt>.
|
||||
*/
|
||||
ParagraphBuffer(MCharBuffer charBuffer) {
|
||||
|
||||
this(charBuffer.length());
|
||||
|
||||
// scan text for paragraph boundaries
|
||||
|
||||
int textLength = fRunArray.getCurTextLength();
|
||||
|
||||
for (int pos=0; pos < textLength; pos++) {
|
||||
|
||||
if (isParagraphBreak(charBuffer.at(pos))) {
|
||||
if (fRunArray.fPosEnd+1 >= fRunArray.fNegStart)
|
||||
expandStyleTable();
|
||||
fRunArray.fRunStart[++fRunArray.fPosEnd] = pos;
|
||||
fStyleTable[fRunArray.fPosEnd] = fFirstStyle;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Private constructor.
|
||||
*/
|
||||
private ParagraphBuffer(int initialLength) {
|
||||
|
||||
fRunArray = new RunArray(kInitialSize, initialLength);
|
||||
fStyleTable = new AttributeMap[fRunArray.getArrayLength()];
|
||||
|
||||
fFirstStyle = AttributeMap.EMPTY_ATTRIBUTE_MAP;
|
||||
}
|
||||
|
||||
/**
|
||||
* Note: this constructor is ONLY for use by the Serialization
|
||||
* mechanism. It does not leave this object in a valid state!
|
||||
*/
|
||||
public ParagraphBuffer() {
|
||||
}
|
||||
|
||||
public void writeExternal(ObjectOutput out) throws IOException {
|
||||
|
||||
compress();
|
||||
out.writeInt(CURRENT_VERSION);
|
||||
out.writeObject(fRunArray);
|
||||
out.writeObject(fStyleTable);
|
||||
out.writeObject(fFirstStyle);
|
||||
}
|
||||
|
||||
public void readExternal(ObjectInput in) throws IOException,
|
||||
ClassNotFoundException {
|
||||
|
||||
if (in.readInt() != CURRENT_VERSION) {
|
||||
throw new IOException("Invalid version of ParagraphBuffer");
|
||||
}
|
||||
fRunArray = (RunArray) in.readObject();
|
||||
fStyleTable = (AttributeMap[]) in.readObject();
|
||||
fFirstStyle = (AttributeMap) in.readObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shift table such that the last positive run starts before pos.
|
||||
*/
|
||||
private void shiftTableTo(int pos) {
|
||||
|
||||
int oldNegStart = fRunArray.fNegStart;
|
||||
int oldPosEnd = fRunArray.fPosEnd;
|
||||
|
||||
fRunArray.shiftTableTo(pos);
|
||||
|
||||
if (oldPosEnd > fRunArray.fPosEnd)
|
||||
System.arraycopy(fStyleTable, fRunArray.fPosEnd+1,
|
||||
fStyleTable, fRunArray.fNegStart,
|
||||
oldPosEnd-fRunArray.fPosEnd);
|
||||
else if (oldNegStart < fRunArray.fNegStart)
|
||||
System.arraycopy(fStyleTable, oldNegStart,
|
||||
fStyleTable, oldPosEnd+1,
|
||||
fRunArray.fNegStart-oldNegStart);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the style table to reflect a change in the RunArray's size.
|
||||
*/
|
||||
private void handleArrayResize(int oldNegStart) {
|
||||
|
||||
AttributeMap newStyleTable[] = new AttributeMap[fRunArray.getArrayLength()];
|
||||
System.arraycopy(fStyleTable, 0, newStyleTable, 0, fRunArray.fPosEnd+1);
|
||||
System.arraycopy(fStyleTable, oldNegStart, newStyleTable, fRunArray.fNegStart, (fRunArray.getArrayLength()-fRunArray.fNegStart));
|
||||
fStyleTable = newStyleTable;
|
||||
}
|
||||
|
||||
void compress() {
|
||||
|
||||
int oldNegStart = fRunArray.fNegStart;
|
||||
fRunArray.compress();
|
||||
if (fRunArray.fNegStart != oldNegStart) {
|
||||
handleArrayResize(oldNegStart);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Make more room in run/style tables.
|
||||
*/
|
||||
private void expandStyleTable() {
|
||||
|
||||
int oldNegStart = fRunArray.fNegStart;
|
||||
fRunArray.expandRunTable();
|
||||
handleArrayResize(oldNegStart);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a character insertion at offset <tt>start</tt>.
|
||||
* If a paragraph break was inserted, propogate paragraph style at
|
||||
* <tt>start</tt> to new paragraph.
|
||||
*/
|
||||
public void insertText(int start, char insertedChar) {
|
||||
|
||||
shiftTableTo(start);
|
||||
if (isParagraphBreak(insertedChar)) {
|
||||
if (fRunArray.fPosEnd+1 >= fRunArray.fNegStart)
|
||||
expandStyleTable();
|
||||
fRunArray.fRunStart[++fRunArray.fPosEnd] = start;
|
||||
fStyleTable[fRunArray.fPosEnd] =
|
||||
(fRunArray.fPosEnd == 0)? fFirstStyle : fStyleTable[fRunArray.fPosEnd-1];
|
||||
fRunArray.runStartsChanged();
|
||||
}
|
||||
|
||||
//fRunArray.fCurTextLength++;
|
||||
fRunArray.addToCurTextLength(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process character insertion at offset <tt>start</tt>.
|
||||
* Each new paragraph gets paragraph style at
|
||||
* <tt>start</tt>.
|
||||
*/
|
||||
public void insertText(int start, char srcChars[], int srcStart, int srcLimit) {
|
||||
|
||||
shiftTableTo(start);
|
||||
|
||||
int adjust = start - srcStart;
|
||||
|
||||
for (int i=srcStart; i < srcLimit; i++)
|
||||
if (isParagraphBreak(srcChars[i])) {
|
||||
if (fRunArray.fPosEnd+1 >= fRunArray.fNegStart)
|
||||
expandStyleTable();
|
||||
fRunArray.fRunStart[++fRunArray.fPosEnd] = adjust + i;
|
||||
fStyleTable[fRunArray.fPosEnd] =
|
||||
(fRunArray.fPosEnd == 0)? fFirstStyle : fStyleTable[fRunArray.fPosEnd-1];
|
||||
fRunArray.runStartsChanged();
|
||||
}
|
||||
|
||||
//fRunArray.fCurTextLength += (srcLimit-srcStart);
|
||||
fRunArray.addToCurTextLength(srcLimit-srcStart);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process deletion by removing paragraph breaks contained in
|
||||
* deleted range. Propogate paragraph styles backward, if necessary.
|
||||
*/
|
||||
public void deleteText(int start, int limit, int[] damagedRange) {
|
||||
|
||||
int length = limit - start;
|
||||
if (length < 0) {
|
||||
throw new IllegalArgumentException("Invalid range");
|
||||
}
|
||||
|
||||
shiftTableTo(limit);
|
||||
|
||||
int newEnd = fRunArray.findRunContaining(start-1);
|
||||
|
||||
if (newEnd != fRunArray.fPosEnd) {
|
||||
|
||||
AttributeMap propStyle = fStyleTable[fRunArray.fPosEnd];
|
||||
boolean propogated;
|
||||
|
||||
if (newEnd == -1) {
|
||||
propogated = !propStyle.equals(fFirstStyle);
|
||||
fFirstStyle = propStyle;
|
||||
}
|
||||
else {
|
||||
propogated = !propStyle.equals(fStyleTable[newEnd]);
|
||||
fStyleTable[newEnd] = propStyle;
|
||||
}
|
||||
|
||||
if (propogated) {
|
||||
int pStart = (newEnd==-1)? 0 : fRunArray.fRunStart[newEnd] + 1;
|
||||
damagedRange[0] = Math.min(damagedRange[0], pStart);
|
||||
}
|
||||
|
||||
fRunArray.fPosEnd = newEnd;
|
||||
}
|
||||
|
||||
fRunArray.addToCurTextLength(-length);
|
||||
|
||||
fRunArray.runStartsChanged();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the start of the paragraph containing offset <tt>pos</tt>.
|
||||
*/
|
||||
public int paragraphStart(int pos) {
|
||||
|
||||
int run = fRunArray.findRunContaining(pos-1);
|
||||
if (run == -1) {
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
return fRunArray.getLogicalRunStart(run) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the limit of the paragraph containing offset <tt>pos</tt>.
|
||||
*/
|
||||
public int paragraphLimit(int pos) {
|
||||
|
||||
int run = fRunArray.findRunContaining(pos-1);
|
||||
|
||||
if (run == fRunArray.fPosEnd)
|
||||
run = fRunArray.fNegStart;
|
||||
else
|
||||
run++;
|
||||
|
||||
if (run == fRunArray.getArrayLength()) {
|
||||
return fRunArray.getCurTextLength();
|
||||
}
|
||||
|
||||
int start = fRunArray.getLogicalRunStart(run);
|
||||
|
||||
return start+1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the style of the paragraph containing offset <tt>pos</tt>.
|
||||
*/
|
||||
public AttributeMap paragraphStyleAt(int offset) {
|
||||
|
||||
int run = fRunArray.findRunContaining(offset-1);
|
||||
if (run < 0)
|
||||
return fFirstStyle;
|
||||
else
|
||||
return fStyleTable[run];
|
||||
}
|
||||
|
||||
/**
|
||||
* Create paragraph iterator.
|
||||
*/
|
||||
/*
|
||||
public MParagraphIterator createParagraphIterator(int start, int limit) {
|
||||
|
||||
return new ParagraphIterator(start, limit);
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Called by iterator to get run info.
|
||||
*/
|
||||
private void setIterator(int pos, ParagraphIterator iter) {
|
||||
|
||||
if ((pos < 0) || (pos >= fRunArray.getCurTextLength())) {
|
||||
iter.set(0, 0, kNoRun, null);
|
||||
return;
|
||||
}
|
||||
|
||||
int run;
|
||||
|
||||
if (pos > 0)
|
||||
run = fRunArray.findRunContaining(pos-1);
|
||||
else
|
||||
run = -1;
|
||||
|
||||
setIteratorUsingRun(run, iter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by iterator to get run info.
|
||||
*/
|
||||
private void setIteratorUsingRun(int run, ParagraphIterator iter) {
|
||||
|
||||
int lastValidRun = fRunArray.lastRun();
|
||||
|
||||
if (run < -1 || run > lastValidRun) {
|
||||
iter.set(0, 0, kNoRun, null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (run == fRunArray.fPosEnd+1)
|
||||
run = fRunArray.fNegStart;
|
||||
else if (run == fRunArray.fNegStart-1)
|
||||
run = fRunArray.fPosEnd;
|
||||
|
||||
int runStart;
|
||||
AttributeMap style;
|
||||
|
||||
if (run < 0) {
|
||||
runStart = 0;
|
||||
style = fFirstStyle;
|
||||
}
|
||||
else {
|
||||
runStart = fRunArray.fRunStart[run];
|
||||
style = fStyleTable[run];
|
||||
if (runStart < 0)
|
||||
runStart += fRunArray.getCurTextLength();
|
||||
runStart++;
|
||||
}
|
||||
|
||||
int nextRun;
|
||||
|
||||
if (run == fRunArray.fPosEnd)
|
||||
nextRun = fRunArray.fNegStart;
|
||||
else
|
||||
nextRun = run + 1;
|
||||
|
||||
int runLimit;
|
||||
|
||||
if (nextRun >= fRunArray.getArrayLength())
|
||||
runLimit = fRunArray.getCurTextLength();
|
||||
else {
|
||||
runLimit = fRunArray.fRunStart[nextRun];
|
||||
if (runLimit < 0)
|
||||
runLimit += fRunArray.getCurTextLength();
|
||||
runLimit++;
|
||||
}
|
||||
|
||||
iter.set(runStart, runLimit, run, style);
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace paragraph breaks/styles between start and length with paragraph breaks/styles
|
||||
* from <tt>srcText</tt>.
|
||||
* @param start an offset into the text
|
||||
* @param limit the index after the last character to replace
|
||||
* @param srcText the text from which new paragraphs are taken
|
||||
* @param srcStart the start of the range in <code>srcText</code> to copy
|
||||
* @param srcLimit the first index after the range in <code>srcText</code> to copy
|
||||
*/
|
||||
public void replace(int start,
|
||||
int limit,
|
||||
MConstText srcText,
|
||||
int srcStart,
|
||||
int srcLimit,
|
||||
int[] damagedRange) {
|
||||
|
||||
final int insLength = srcLimit - srcStart;
|
||||
if (insLength < 0) {
|
||||
throw new Error("invalid range");
|
||||
}
|
||||
final int origLength = fRunArray.getCurTextLength();
|
||||
deleteText(start, limit, damagedRange);
|
||||
|
||||
if (insLength == 0)
|
||||
return;
|
||||
|
||||
final int oldPosEnd = fRunArray.fPosEnd;
|
||||
AttributeMap origStyle;
|
||||
if (limit < origLength) {
|
||||
origStyle = (fRunArray.fPosEnd>=0)? fStyleTable[fRunArray.fPosEnd] : fFirstStyle;
|
||||
}
|
||||
else {
|
||||
origStyle = srcText.paragraphStyleAt(srcLimit);
|
||||
}
|
||||
|
||||
int paragraphStart = srcStart;
|
||||
int lastPLimit = srcText.paragraphStart(srcLimit);
|
||||
boolean separatorAtEnd = lastPLimit > srcStart && isParagraphBreak(srcText.at(lastPLimit-1));
|
||||
|
||||
if (limit == origLength && lastPLimit == paragraphStart) {
|
||||
if (fRunArray.fPosEnd > 0) {
|
||||
fStyleTable[fRunArray.fPosEnd] = origStyle;
|
||||
}
|
||||
else {
|
||||
fFirstStyle = origStyle;
|
||||
}
|
||||
}
|
||||
else {
|
||||
boolean firstPass = true;
|
||||
while (paragraphStart < lastPLimit) {
|
||||
|
||||
AttributeMap style = srcText.paragraphStyleAt(paragraphStart);
|
||||
int paragraphLimit = srcText.paragraphLimit(paragraphStart);
|
||||
|
||||
if (fRunArray.fPosEnd+1 >= fRunArray.fNegStart)
|
||||
expandStyleTable();
|
||||
|
||||
if (fRunArray.fPosEnd >= 0) {
|
||||
if (!style.equals(fStyleTable[fRunArray.fPosEnd])) {
|
||||
fStyleTable[fRunArray.fPosEnd] = style;
|
||||
if (firstPass) {
|
||||
int pStart = fRunArray.fRunStart[fRunArray.fPosEnd]+1;
|
||||
damagedRange[0] = Math.min(damagedRange[0], pStart);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!style.equals(fFirstStyle)) {
|
||||
fFirstStyle = style;
|
||||
damagedRange[0] = 0;
|
||||
}
|
||||
|
||||
firstPass = false;
|
||||
|
||||
if (paragraphLimit < lastPLimit || separatorAtEnd) {
|
||||
fRunArray.fRunStart[++fRunArray.fPosEnd] = paragraphLimit - 1 + start - srcStart;
|
||||
}
|
||||
paragraphStart = paragraphLimit;
|
||||
}
|
||||
if (fRunArray.fPosEnd != oldPosEnd) {
|
||||
fStyleTable[fRunArray.fPosEnd] = origStyle;
|
||||
}
|
||||
}
|
||||
|
||||
fRunArray.addToCurTextLength(insLength);
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify the style of all paragraphs containing offsets in the range [start, limit) to
|
||||
* <tt>style</tt>.
|
||||
*/
|
||||
public boolean modifyParagraphStyles(int start,
|
||||
int limit,
|
||||
StyleModifier modifier,
|
||||
int[] damagedRange) {
|
||||
|
||||
int run = fRunArray.findRunContaining(start-1);
|
||||
int currentPStart;
|
||||
if (run == -1) {
|
||||
currentPStart = 0;
|
||||
}
|
||||
else {
|
||||
currentPStart = fRunArray.getLogicalRunStart(run) + 1;
|
||||
}
|
||||
|
||||
boolean modifiedAnywhere = false;
|
||||
|
||||
for (;;) {
|
||||
|
||||
boolean modified = false;
|
||||
|
||||
if (run < 0) {
|
||||
|
||||
AttributeMap newStyle = modifier.modifyStyle(fFirstStyle);
|
||||
|
||||
if (!newStyle.equals(fFirstStyle)) {
|
||||
fFirstStyle = newStyle;
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
AttributeMap newStyle = modifier.modifyStyle(fStyleTable[run]);
|
||||
|
||||
if (!fStyleTable[run].equals(newStyle)) {
|
||||
fStyleTable[run] = newStyle;
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (run == fRunArray.fPosEnd) {
|
||||
run = fRunArray.fNegStart;
|
||||
}
|
||||
else {
|
||||
run++;
|
||||
}
|
||||
|
||||
int nextPStart;
|
||||
if (run == fRunArray.getArrayLength()) {
|
||||
nextPStart = fRunArray.getCurTextLength();
|
||||
}
|
||||
else {
|
||||
nextPStart = fRunArray.getLogicalRunStart(run) + 1;
|
||||
}
|
||||
|
||||
if (modified) {
|
||||
modifiedAnywhere = true;
|
||||
damagedRange[0] = Math.min(damagedRange[0], currentPStart);
|
||||
damagedRange[1] = Math.max(damagedRange[1], nextPStart);
|
||||
}
|
||||
|
||||
if (limit <= nextPStart) {
|
||||
break;
|
||||
}
|
||||
else {
|
||||
currentPStart = nextPStart;
|
||||
}
|
||||
}
|
||||
|
||||
return modifiedAnywhere;
|
||||
}
|
||||
|
||||
private static void dumpParagraphStarts(ParagraphBuffer st) {
|
||||
|
||||
System.out.println("fRunArray.fPosEnd="+st.fRunArray.fPosEnd+", fRunArray.fNegStart="+st.fRunArray.fNegStart+
|
||||
", fRunArray.getArrayLength()="+st.fRunArray.getArrayLength()+", fRunArray.getCurTextLength()="+st.fRunArray.getCurTextLength());
|
||||
|
||||
int i;
|
||||
System.out.print("Positives: ");
|
||||
for (i=0; i<=st.fRunArray.fPosEnd; i++)
|
||||
System.out.print(st.fRunArray.fRunStart[i]+" ");
|
||||
|
||||
System.out.print(" Negatives: ");
|
||||
for (i=st.fRunArray.fNegStart; i<st.fRunArray.getArrayLength(); i++)
|
||||
System.out.print(st.fRunArray.fRunStart[i]+" ");
|
||||
|
||||
System.out.println(" ");
|
||||
}
|
||||
|
||||
private static final int kNoRun = -42; // iterator use
|
||||
|
||||
private final class ParagraphIterator /*implements MParagraphIterator*/
|
||||
{
|
||||
ParagraphIterator(int start, int limit)
|
||||
{
|
||||
reset(start, limit, start);
|
||||
}
|
||||
|
||||
public void reset(int start, int limit, int pos)
|
||||
{
|
||||
fStart = start;
|
||||
fLimit = limit;
|
||||
setIterator(fStart, this);
|
||||
}
|
||||
|
||||
public boolean isValid()
|
||||
{
|
||||
return fCurrentRun != kNoRun;
|
||||
}
|
||||
|
||||
public void next()
|
||||
{
|
||||
if (fRunLimit < fLimit) {
|
||||
fCurrentRun++;
|
||||
setIteratorUsingRun(fCurrentRun, this);
|
||||
}
|
||||
else
|
||||
set(0, 0, kNoRun, null);
|
||||
}
|
||||
|
||||
public void prev()
|
||||
{
|
||||
if (fRunStart > fStart) {
|
||||
fCurrentRun--;
|
||||
setIteratorUsingRun(fCurrentRun, this);
|
||||
}
|
||||
else
|
||||
set(0, 0, kNoRun, null);
|
||||
}
|
||||
|
||||
public void set(int pos)
|
||||
{
|
||||
if (pos >= fStart && pos < fLimit) {
|
||||
setIterator(pos, this);
|
||||
} else {
|
||||
set(0, 0, kNoRun, null);
|
||||
}
|
||||
}
|
||||
|
||||
// ParagraphBuffer calls back on this to set iterators
|
||||
void set(int start, int limit, int currentRun, AttributeMap style)
|
||||
{
|
||||
fRunStart = start < fStart ? fStart : start;
|
||||
fRunLimit = limit > fLimit ? fLimit : limit;
|
||||
fCurrentRun = currentRun;
|
||||
fStyle = style;
|
||||
}
|
||||
|
||||
public void reset(int start, int limit)
|
||||
{
|
||||
reset(start, limit, start);
|
||||
}
|
||||
|
||||
public void first()
|
||||
{
|
||||
set(fStart);
|
||||
}
|
||||
|
||||
public void last()
|
||||
{
|
||||
set(fLimit - 1);
|
||||
}
|
||||
|
||||
public int rangeStart()
|
||||
{
|
||||
return fStart;
|
||||
}
|
||||
|
||||
public int rangeLimit()
|
||||
{
|
||||
return fLimit;
|
||||
}
|
||||
|
||||
public int rangeLength()
|
||||
{
|
||||
return fLimit - fStart;
|
||||
}
|
||||
|
||||
public int runStart()
|
||||
{
|
||||
return fRunStart;
|
||||
}
|
||||
|
||||
public int runLimit()
|
||||
{
|
||||
return fRunLimit;
|
||||
}
|
||||
|
||||
public int runLength()
|
||||
{
|
||||
return fRunLimit - fRunStart;
|
||||
}
|
||||
|
||||
public AttributeMap style() {
|
||||
|
||||
return fStyle;
|
||||
}
|
||||
|
||||
private int fStart;
|
||||
private int fLimit;
|
||||
private int fRunStart;
|
||||
private int fRunLimit;
|
||||
private int fCurrentRun;
|
||||
private AttributeMap fStyle;
|
||||
}
|
||||
}
|
282
icu4j/src/com/ibm/richtext/styledtext/RunArray.java
Executable file
282
icu4j/src/com/ibm/richtext/styledtext/RunArray.java
Executable file
|
@ -0,0 +1,282 @@
|
|||
/*
|
||||
* @(#)$RCSfile: RunArray.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:45:10 $
|
||||
*
|
||||
* (C) Copyright IBM Corp. 1998-1999. All Rights Reserved.
|
||||
*
|
||||
* The program is provided "as is" without any warranty express or
|
||||
* implied, including the warranty of non-infringement and the implied
|
||||
* warranties of merchantibility and fitness for a particular purpose.
|
||||
* IBM will not be liable for any damages suffered by you as a result
|
||||
* of using the Program. In no event will IBM be liable for any
|
||||
* special, indirect or consequential damages or lost profits even if
|
||||
* IBM has been advised of the possibility of their occurrence. IBM
|
||||
* will not be liable for any third party claims against you.
|
||||
*/
|
||||
/**
|
||||
* This class maintains intervals within a piece of text. Interval boundaries
|
||||
* are stored in the fRunStart array. Interval boundaries may have a
|
||||
* positive or negative representation. A positive boundary is given as an offset
|
||||
* from 0. A negative boundary is given as a negative offset from the ned of the text.
|
||||
* The RunArray stores positive boundaries in the entries [0, fPosEnd], and negative
|
||||
* boundaries in the entries [fNegStart, fLength). New boundaries may be inserted into
|
||||
* the undefined middle of the RunArray. If fPosEnd < 0, there are no positive entries.
|
||||
* If fNegStart >= fRunArray.length, there are no negative netries. It's possible to have
|
||||
* a runarray with neither positive or negative entries.
|
||||
*
|
||||
* As an example of how the RunArray works, consider a piece of text with 5 intervals,
|
||||
* where each interval is 3 characters in length. The RunArray for this text could
|
||||
* look like:
|
||||
* fCurTextLength = 15, fPosEnd = 5, fNegStart = 10,
|
||||
* fRunStart = { 0, 3, 6, 9, 12, U, U, U, U, U };
|
||||
* where U is an undefined array element.
|
||||
|
||||
* An equivalent representation would be:
|
||||
* fCurTextLength = 15, fPosEnd = 3, fNegStart = 8,
|
||||
* fRunStart = { 0, 3, 6, U, U, U, U, U, -6, -3 };
|
||||
*
|
||||
* The RunArray class is used in the StyleBuffer and the ParagraphBuffer. In the StyleBuffer,
|
||||
* the entries in fRunStart give the offsets where style runs begin. In the
|
||||
* ParagraphBuffer, the fRunStart entries store offsets of paragraph breaks.
|
||||
*
|
||||
* This class provides methods for shifting the run table to a particular position, expanding the
|
||||
* run table, and returning the index of the run containing a particular offset in the text. All
|
||||
* other functionality is implemented in the RunArray clients.
|
||||
*
|
||||
* RunArray uses FastIntBinarySearch for searches. The searches are constructed on demand in
|
||||
* the findRunContaining method. The searches are invalidated when the run array is shifted;
|
||||
* however, the RunArray can be modified by other classes. Thus, if another class modifies
|
||||
* the entries in fRunArray, or modifies fPosEnd or fNegStart, it is responsible for
|
||||
* calling runStartsChanged.
|
||||
*/
|
||||
|
||||
package com.ibm.richtext.styledtext;
|
||||
|
||||
import java.io.Externalizable;
|
||||
import java.io.ObjectInput;
|
||||
import java.io.ObjectOutput;
|
||||
import java.io.IOException;
|
||||
|
||||
final class RunArray implements Externalizable {
|
||||
|
||||
static final String COPYRIGHT =
|
||||
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
|
||||
private static final long serialVersionUID = 22356934;
|
||||
|
||||
int[] fRunStart;
|
||||
private int fCurTextLength;
|
||||
int fPosEnd, fNegStart;
|
||||
|
||||
transient private FastIntBinarySearch fPosSearch;
|
||||
transient private boolean fPosSearchValid;
|
||||
transient private FastIntBinarySearch fNegSearch;
|
||||
transient private boolean fNegSearchValid;
|
||||
|
||||
private static final int CURRENT_VERSION = 1;
|
||||
|
||||
RunArray(int initialSize, int curTextLength) {
|
||||
|
||||
fRunStart = new int[initialSize];
|
||||
fCurTextLength = curTextLength;
|
||||
fPosEnd = -1;
|
||||
fNegStart = initialSize;
|
||||
|
||||
fPosSearch = new FastIntBinarySearch(fRunStart, 0, 1);
|
||||
fNegSearch = new FastIntBinarySearch(fRunStart, 0, 1);
|
||||
fPosSearchValid = fNegSearchValid = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Note: this constructor is ONLY for use by the Serialization
|
||||
* mechanism. It does not leave this object in a valid state!
|
||||
*/
|
||||
public RunArray() {
|
||||
}
|
||||
|
||||
public void writeExternal(ObjectOutput out) throws IOException {
|
||||
|
||||
out.writeInt(CURRENT_VERSION);
|
||||
out.writeObject(fRunStart);
|
||||
out.writeInt(fCurTextLength);
|
||||
out.writeInt(fPosEnd);
|
||||
out.writeInt(fNegStart);
|
||||
}
|
||||
|
||||
public void readExternal(ObjectInput in) throws IOException,
|
||||
ClassNotFoundException {
|
||||
|
||||
if (in.readInt() != CURRENT_VERSION) {
|
||||
throw new IOException("Invalid version of RunArray");
|
||||
}
|
||||
fRunStart = (int[]) in.readObject();
|
||||
fCurTextLength = in.readInt();
|
||||
fPosEnd = in.readInt();
|
||||
fNegStart = in.readInt();
|
||||
|
||||
fPosSearch = new FastIntBinarySearch(fRunStart, 0, 1);
|
||||
fNegSearch = new FastIntBinarySearch(fRunStart, 0, 1);
|
||||
fPosSearchValid = fNegSearchValid = false;
|
||||
}
|
||||
|
||||
public int getCurTextLength() {
|
||||
|
||||
return fCurTextLength;
|
||||
}
|
||||
|
||||
public void setCurTextLength(int curTextLength) {
|
||||
|
||||
fCurTextLength = curTextLength;
|
||||
}
|
||||
|
||||
public void addToCurTextLength(int delta) {
|
||||
|
||||
fCurTextLength += delta;
|
||||
}
|
||||
|
||||
public void runStartsChanged() {
|
||||
|
||||
fPosSearchValid = fNegSearchValid = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of the last valid run.
|
||||
*/
|
||||
int lastRun() {
|
||||
|
||||
return (fNegStart == fRunStart.length)? fPosEnd : fRunStart.length-1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the length of the run array. Replaces old fLength member.
|
||||
*/
|
||||
int getArrayLength() {
|
||||
|
||||
return fRunStart.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shifts style table such that the last positive run
|
||||
* starts before pos.
|
||||
*/
|
||||
void shiftTableTo(int pos) {
|
||||
|
||||
int oldPosEnd = fPosEnd;
|
||||
|
||||
while (fPosEnd >= 0 && fRunStart[fPosEnd] >= pos) {
|
||||
|
||||
fNegStart--;
|
||||
fRunStart[fNegStart] = fRunStart[fPosEnd] - fCurTextLength;
|
||||
fPosEnd--;
|
||||
|
||||
}
|
||||
|
||||
pos -= fCurTextLength;
|
||||
|
||||
while (fNegStart<fRunStart.length && fRunStart[fNegStart] < pos) {
|
||||
|
||||
fPosEnd++;
|
||||
fRunStart[fPosEnd] = fRunStart[fNegStart] + fCurTextLength;
|
||||
fNegStart++;
|
||||
}
|
||||
|
||||
if (oldPosEnd != fPosEnd) {
|
||||
fPosSearchValid = fNegSearchValid = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns index of style run containing pos. If first style run starts before
|
||||
* pos, -1 is returned. If pos is greater than text length, lastrun is returned.
|
||||
*/
|
||||
int findRunContaining(int pos) {
|
||||
|
||||
FastIntBinarySearch search;
|
||||
final int length = fRunStart.length;
|
||||
|
||||
if (fNegStart < length && (pos-fCurTextLength >= fRunStart[fNegStart])) {
|
||||
|
||||
pos -= fCurTextLength;
|
||||
|
||||
if (!fNegSearchValid) {
|
||||
fNegSearch.setData(fRunStart, fNegStart, length-fNegStart);
|
||||
}
|
||||
search = fNegSearch;
|
||||
}
|
||||
else if (fPosEnd >= 0) {
|
||||
|
||||
if (!fPosSearchValid) {
|
||||
fPosSearch.setData(fRunStart, 0, fPosEnd+1);
|
||||
}
|
||||
search = fPosSearch;
|
||||
}
|
||||
else
|
||||
return -1;
|
||||
|
||||
int run = search.findIndex(pos);
|
||||
|
||||
return run;
|
||||
}
|
||||
|
||||
int getLogicalRunStart(int run) {
|
||||
|
||||
if (run == -1) {
|
||||
return 0;
|
||||
}
|
||||
else if (run == fRunStart.length) {
|
||||
return fCurTextLength;
|
||||
}
|
||||
else {
|
||||
if (run <= fPosEnd) {
|
||||
return fRunStart[run];
|
||||
}
|
||||
else if (run >= fNegStart) {
|
||||
return fRunStart[run] + fCurTextLength;
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException("Illegal run");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Increases size of run table. Current implementation doubles the run table's size.
|
||||
*/
|
||||
void expandRunTable() {
|
||||
|
||||
resizeRunTable(fRunStart.length * 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the minimum number of elements possible in fRunStart.
|
||||
*/
|
||||
private int getMinSize() {
|
||||
|
||||
return Math.max(fPosEnd + (fRunStart.length-fNegStart) + 1, 1);
|
||||
}
|
||||
|
||||
void compress() {
|
||||
|
||||
int minSize = getMinSize();
|
||||
if (fRunStart.length > minSize) {
|
||||
resizeRunTable(minSize);
|
||||
}
|
||||
}
|
||||
|
||||
private void resizeRunTable(int newSize) {
|
||||
|
||||
if (newSize < getMinSize()) {
|
||||
throw new IllegalArgumentException("Attempt to make RunArray too small.");
|
||||
}
|
||||
|
||||
final int oldLength = fRunStart.length;
|
||||
|
||||
int newRunStart[] = new int[newSize];
|
||||
System.arraycopy(fRunStart, 0, newRunStart, 0, fPosEnd+1);
|
||||
int newNegStart = newRunStart.length - (oldLength-fNegStart);
|
||||
System.arraycopy(fRunStart, fNegStart, newRunStart, newNegStart, (oldLength-fNegStart));
|
||||
|
||||
fNegStart = newNegStart;
|
||||
fRunStart = newRunStart;
|
||||
|
||||
fPosSearchValid = fNegSearchValid = false;
|
||||
}
|
||||
}
|
374
icu4j/src/com/ibm/richtext/styledtext/StandardTabRuler.java
Executable file
374
icu4j/src/com/ibm/richtext/styledtext/StandardTabRuler.java
Executable file
|
@ -0,0 +1,374 @@
|
|||
/*
|
||||
* @(#)$RCSfile: StandardTabRuler.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:45:10 $
|
||||
*
|
||||
* (C) Copyright IBM Corp. 1998-1999. All Rights Reserved.
|
||||
*
|
||||
* The program is provided "as is" without any warranty express or
|
||||
* implied, including the warranty of non-infringement and the implied
|
||||
* warranties of merchantibility and fitness for a particular purpose.
|
||||
* IBM will not be liable for any damages suffered by you as a result
|
||||
* of using the Program. In no event will IBM be liable for any
|
||||
* special, indirect or consequential damages or lost profits even if
|
||||
* IBM has been advised of the possibility of their occurrence. IBM
|
||||
* will not be liable for any third party claims against you.
|
||||
*/
|
||||
package com.ibm.richtext.styledtext;
|
||||
|
||||
import java.util.Vector;
|
||||
|
||||
import java.io.Externalizable;
|
||||
import java.io.ObjectInput;
|
||||
import java.io.ObjectOutput;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* This class is a standard implementation of MTabRuler.
|
||||
* It can have a finite number of client-specified TabStops. After
|
||||
* the client-specified TabStops, all TabStops have type
|
||||
* <code>TabStop.kAuto</code> and are at the autospace intervals.
|
||||
* @see TabStop
|
||||
*/
|
||||
public final class StandardTabRuler extends MTabRuler
|
||||
implements Externalizable
|
||||
{
|
||||
static final String COPYRIGHT =
|
||||
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
|
||||
private static final int CURRENT_VERSION = 1;
|
||||
private static final long serialVersionUID = 22356934;
|
||||
|
||||
private static final TabStop AUTO_ZERO = new TabStop(0, TabStop.kAuto);
|
||||
|
||||
private TabStop[] fTabs = null;
|
||||
private int fAutoSpacing = 36; // every 1/2 inch.
|
||||
|
||||
/**
|
||||
* Create a StandardTabRuler with only auto tabs, with spacing of 36.
|
||||
*/
|
||||
public StandardTabRuler()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a StandardTabRuler with only auto tabs, with the
|
||||
* given autoSpacing.
|
||||
* @param autoSpacing the autoSpacing for this tab ruler
|
||||
*/
|
||||
public StandardTabRuler(int autoSpacing)
|
||||
{
|
||||
fAutoSpacing = autoSpacing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a StandardTabRuler. The first TabStops on the ruler will be
|
||||
* the TabStops in the <code>tabs</code> array. After these tabs all
|
||||
* tabs are auto tabs.
|
||||
* @param tabs an array of TabStops. The TabStops in the array must
|
||||
* be in strictly increasing order (of positions), and cannot have
|
||||
* type <code>TabStop.kAuto</code>.
|
||||
* @param autoSpacing the autoSpacing interval to use after the last
|
||||
* client-specified tab.
|
||||
*/
|
||||
public StandardTabRuler(TabStop[] tabs, int autoSpacing)
|
||||
{
|
||||
if (tabs.length > 0) {
|
||||
validateTabArray(tabs);
|
||||
fTabs = (TabStop[]) tabs.clone();
|
||||
}
|
||||
else {
|
||||
fTabs = null;
|
||||
}
|
||||
fAutoSpacing = autoSpacing;
|
||||
}
|
||||
|
||||
/** Tabs as provided, then autoSpacing after the last tab to eternity. Use this constructor when
|
||||
munging a ruler, it does no validation on the tabs in the vector. Vector may not be null. */
|
||||
|
||||
/*public*/ StandardTabRuler(Vector v, int autoSpacing)
|
||||
{
|
||||
fTabs = tabArrayFromVector(v);
|
||||
fAutoSpacing = autoSpacing;
|
||||
}
|
||||
|
||||
/** Construct from another ruler. No validation. Ruler may not be null. */
|
||||
|
||||
/*public*/ StandardTabRuler(MTabRuler ruler)
|
||||
{
|
||||
if (ruler == null) {
|
||||
throw new IllegalArgumentException("ruler may not be null");
|
||||
}
|
||||
|
||||
fTabs = tabArrayFromVector(vectorFromTabRuler(ruler));
|
||||
fAutoSpacing = ruler.autoSpacing();
|
||||
}
|
||||
|
||||
public void readExternal(ObjectInput in) throws IOException,
|
||||
ClassNotFoundException {
|
||||
|
||||
int version = in.readInt();
|
||||
if (version != CURRENT_VERSION) {
|
||||
throw new IOException("Invalid version of StyledText: " + version);
|
||||
}
|
||||
fTabs = (TabStop[]) in.readObject();
|
||||
fAutoSpacing = in.readInt();
|
||||
}
|
||||
|
||||
public void writeExternal(ObjectOutput out) throws IOException {
|
||||
|
||||
out.writeInt(CURRENT_VERSION);
|
||||
out.writeObject(fTabs);
|
||||
out.writeInt(fAutoSpacing);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return first tab in the ruler. If an autoTab, it is at position zero, and
|
||||
* all subsequent tabs will be autotabs at autoSpacing intervals.
|
||||
*/
|
||||
public TabStop firstTab()
|
||||
{
|
||||
if (fTabs != null && fTabs.length > 0) {
|
||||
return fTabs[0];
|
||||
}
|
||||
|
||||
return AUTO_ZERO;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the first tab in the ruler with fPosition > position. If it is an
|
||||
* autotab, it is at an increment of autoSpacing, and all subsequent tabs will be
|
||||
* autotabs at autoSpacing intervals.
|
||||
* @param position the position of the TabStop returned will be greater than this parameter
|
||||
*/
|
||||
public TabStop nextTab(int position)
|
||||
{
|
||||
if (fTabs != null) {
|
||||
for (int i = 0; i < fTabs.length; ++i) {
|
||||
if (position < fTabs[i].getPosition())
|
||||
return fTabs[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (position >= 4000) { // debug: sanity check
|
||||
System.out.println("auto tab past 4000");
|
||||
}
|
||||
|
||||
return new TabStop(((position / fAutoSpacing) + 1) * fAutoSpacing, TabStop.kAuto);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the interval for autotabs.
|
||||
*/
|
||||
public int autoSpacing()
|
||||
{
|
||||
return fAutoSpacing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare this to another Object. Returns true if the object
|
||||
* is an MTabRuler with the same autoSpacing and tabs.
|
||||
*/
|
||||
public boolean equals(Object o)
|
||||
{
|
||||
if (o == this) {
|
||||
return true;
|
||||
}
|
||||
else if (o == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MTabRuler rhs;
|
||||
try {
|
||||
rhs = (MTabRuler)o;
|
||||
}
|
||||
catch(ClassCastException e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fAutoSpacing != rhs.autoSpacing())
|
||||
return false;
|
||||
|
||||
TabStop rhsTab = rhs.firstTab();
|
||||
|
||||
if (fTabs != null) {
|
||||
for (int i = 0; i < fTabs.length; ++i) {
|
||||
if (!fTabs[i].equals(rhsTab))
|
||||
return false;
|
||||
|
||||
rhsTab = rhs.nextTab(rhsTab.getPosition());
|
||||
}
|
||||
}
|
||||
|
||||
return rhsTab.getType() == TabStop.kAuto;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return debug information about this tab ruler.
|
||||
*/
|
||||
public String toString()
|
||||
{
|
||||
StringBuffer buffer = new StringBuffer(super.toString());
|
||||
buffer.append(" auto: ");
|
||||
buffer.append(Integer.toString(fAutoSpacing));
|
||||
|
||||
if (fTabs != null) {
|
||||
for (int i = 0; i < fTabs.length; ++i) {
|
||||
buffer.append(fTabs[i].toString());
|
||||
}
|
||||
}
|
||||
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
/** Utility to convert a vector of tabs to an array. */
|
||||
|
||||
private static TabStop[] tabArrayFromVector(Vector v)
|
||||
{
|
||||
int count = v.size();
|
||||
TabStop[] tabs = new TabStop[count];
|
||||
for (int i = 0; i < count; ++i) {
|
||||
tabs[i] = (TabStop)v.elementAt(i);
|
||||
}
|
||||
|
||||
return tabs;
|
||||
}
|
||||
|
||||
/** Utility to convert a ruler to a vector of tabs, for munging. */
|
||||
|
||||
private static Vector vectorFromTabRuler(MTabRuler ruler)
|
||||
{
|
||||
Vector v = new Vector();
|
||||
for (TabStop tab = ruler.firstTab(); tab != null && tab.getType() != TabStop.kAuto; tab = ruler.nextTab(tab.getPosition())) {
|
||||
v.addElement(tab);
|
||||
}
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
/** Utility to validate an array of tabs. The array must not be null, must not contain null
|
||||
entries, must not be kAuto, and positions must in increasing order. */
|
||||
|
||||
private static void validateTabArray(TabStop[] tabs)
|
||||
{
|
||||
int pos = Integer.MIN_VALUE;
|
||||
for (int i = 0; i < tabs.length; ++i) {
|
||||
if (tabs[i].getType() == TabStop.kAuto) {
|
||||
throw new IllegalArgumentException("can't explicitly specify an auto tab.");
|
||||
}
|
||||
int nextpos = tabs[i].getPosition();
|
||||
if (nextpos <= pos) {
|
||||
throw new IllegalArgumentException("tab positions must be in increasing order.");
|
||||
}
|
||||
pos = nextpos;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a tab ruler identical to the given ruler, except with the
|
||||
* given tab added.
|
||||
* @param ruler the original ruler. The MTabRuler will be the same as
|
||||
* this except for the additional tab. <code>ruler</code> is not modified.
|
||||
* @param tabToAdd the tab to add to the new tab ruler
|
||||
* @return an MTabRuler resulting from this operation
|
||||
*/
|
||||
/*public*/ static MTabRuler addTabToRuler(MTabRuler ruler, TabStop tabToAdd)
|
||||
{
|
||||
if (ruler == null || tabToAdd == null)
|
||||
throw new IllegalArgumentException("ruler and tabToAdd may not be null");
|
||||
|
||||
Vector vector = new Vector();
|
||||
|
||||
int pos = 0;
|
||||
boolean added = false;
|
||||
for (TabStop tab = ruler.firstTab(); tab.getType() != TabStop.kAuto; tab = ruler.nextTab(pos)) {
|
||||
pos = tab.getPosition();
|
||||
|
||||
if (!added && pos >= tabToAdd.getPosition()) {
|
||||
if (pos == tabToAdd.getPosition())
|
||||
tab = null;
|
||||
vector.addElement(tabToAdd);
|
||||
added = true;
|
||||
}
|
||||
|
||||
if (tab != null)
|
||||
vector.addElement(tab);
|
||||
}
|
||||
if (!added)
|
||||
vector.addElement(tabToAdd);
|
||||
|
||||
return new StandardTabRuler(vector, ruler.autoSpacing());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a tab ruler identical to the given ruler, except with the
|
||||
* given tab removed.
|
||||
* @param ruler the original ruler. The MTabRuler will be the same as
|
||||
* this except for the removed tab. <code>ruler</code> is not modified.
|
||||
* @param position the position of the tab to remove from the new tab ruler
|
||||
* @return an MTabRuler resulting from this operation
|
||||
*/
|
||||
/*public*/ static MTabRuler removeTabFromRuler(MTabRuler ruler, int position)
|
||||
{
|
||||
if (ruler == null)
|
||||
throw new IllegalArgumentException("ruler may not be null");
|
||||
|
||||
Vector vector = new Vector();
|
||||
|
||||
int pos = 0;
|
||||
boolean removed = false;
|
||||
for (TabStop tab = ruler.firstTab(); tab.getType() != TabStop.kAuto; tab = ruler.nextTab(pos)) {
|
||||
pos = tab.getPosition();
|
||||
|
||||
if (!removed && pos >= position) {
|
||||
if (pos == position) {
|
||||
removed = true;
|
||||
continue; // skip this tab and continue with the remainder
|
||||
}
|
||||
break; // we didn't remove a tab, but skipped position, so don't bother with the rest
|
||||
}
|
||||
|
||||
vector.addElement(tab);
|
||||
}
|
||||
if (!removed) // no change
|
||||
return ruler;
|
||||
|
||||
if (vector.size() == 0)
|
||||
return new StandardTabRuler(ruler.autoSpacing());
|
||||
|
||||
return new StandardTabRuler(vector, ruler.autoSpacing());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a tab ruler identical to the given ruler, except with the
|
||||
* tab at position <code>fromPosition</code> moved to position
|
||||
* <code>toPosition</code>.
|
||||
* @param ruler the original ruler. The MTabRuler will be the same as
|
||||
* this except for the moved tab. <code>ruler</code> is not modified.
|
||||
* @param fromPosition the position of the tab to move
|
||||
* @param toPosition the new position of the tab
|
||||
* @return an MTabRuler resulting from this operation
|
||||
*/
|
||||
/*public*/ static MTabRuler moveTabOnRuler(MTabRuler ruler, int fromPosition, int toPosition)
|
||||
{
|
||||
if (ruler == null)
|
||||
throw new IllegalArgumentException("ruler may not be null");
|
||||
|
||||
Vector vector = new Vector();
|
||||
|
||||
int pos = 0;
|
||||
boolean moved = false;
|
||||
for (TabStop tab = ruler.firstTab(); tab.getType() != TabStop.kAuto; tab = ruler.nextTab(pos)) {
|
||||
pos = tab.getPosition();
|
||||
|
||||
if (!moved && pos == fromPosition) {
|
||||
moved = true;
|
||||
tab = new TabStop(toPosition, tab.getType()); // copy it
|
||||
}
|
||||
|
||||
vector.addElement(tab);
|
||||
}
|
||||
if (!moved) // no change
|
||||
return ruler;
|
||||
|
||||
return new StandardTabRuler(vector, ruler.autoSpacing());
|
||||
}
|
||||
|
||||
}
|
671
icu4j/src/com/ibm/richtext/styledtext/StyleBuffer.java
Executable file
671
icu4j/src/com/ibm/richtext/styledtext/StyleBuffer.java
Executable file
|
@ -0,0 +1,671 @@
|
|||
/*
|
||||
* @(#)$RCSfile: StyleBuffer.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:45:10 $
|
||||
*
|
||||
* (C) Copyright IBM Corp. 1998-1999. All Rights Reserved.
|
||||
*
|
||||
* The program is provided "as is" without any warranty express or
|
||||
* implied, including the warranty of non-infringement and the implied
|
||||
* warranties of merchantibility and fitness for a particular purpose.
|
||||
* IBM will not be liable for any damages suffered by you as a result
|
||||
* of using the Program. In no event will IBM be liable for any
|
||||
* special, indirect or consequential damages or lost profits even if
|
||||
* IBM has been advised of the possibility of their occurrence. IBM
|
||||
* will not be liable for any third party claims against you.
|
||||
*/
|
||||
package com.ibm.richtext.styledtext;
|
||||
|
||||
import java.io.Externalizable;
|
||||
import java.io.ObjectInput;
|
||||
import java.io.ObjectOutput;
|
||||
import java.io.IOException;
|
||||
|
||||
import com.ibm.textlayout.attributes.AttributeMap;
|
||||
|
||||
/*
|
||||
8/2/96
|
||||
Added setIteratorUsingRun method
|
||||
|
||||
8/5/96
|
||||
No longer has to be constructed with an MText.
|
||||
|
||||
8/8/96
|
||||
Added replace method, which reads styles from a StyleRunIterator.
|
||||
Also, added a constructor which takes a StyleRunIterator.
|
||||
These methods are for copy/paste support.
|
||||
8/16/96
|
||||
StyleBuffer now takes MConstText instead of MText where possible.
|
||||
|
||||
10/23/96
|
||||
Some old commented-out code removed for aesthetic reasons.
|
||||
|
||||
7/31/98 Switched to AttributeMap
|
||||
*/
|
||||
|
||||
/**
|
||||
* StyleBuffer implements <tt>MStyleBuffer</tt>. It maintains
|
||||
* <tt>AttributeMap</tt> objects to apply to the text in an <tt>MText</tt> object,
|
||||
* and the
|
||||
* intervals on which those styles apply.
|
||||
* <p>
|
||||
* StyleBuffer stores the intervals on which styles apply in a <tt>RunArray</tt>
|
||||
* object (see <tt>RunArray</tt> for more information). The styles are stored in
|
||||
* an array of <tt>AttributeMap</tt> objects.
|
||||
* <p>
|
||||
* <tt>RunArray</tt> maintains an array of integers which represent offsets into text.
|
||||
* The array has a "positive" region in which offsets are given as positive distances
|
||||
* from the start of the text, and a "negative" region in which offsets are given as
|
||||
* negative distances from the end of the text. Between the positive and negative regions
|
||||
* is a gap, into which new offsets may be inserted. This storage scheme allows for
|
||||
* efficient response to a series of editing operations which occur in the same area of the
|
||||
* text.
|
||||
* <p>
|
||||
* StyleBuffer uses the offsets in <tt>RunArray</tt> as the boundaries of style runs.
|
||||
* A style run begins at each offset in <tt>RunArray</tt>, and each style run continues to
|
||||
* the next offset. The style array is kept in sync with the array of offsets in <tt>RunArray</tt>;
|
||||
* that is, the style which begins at RunArray[i] is stored in StyleArray[i].
|
||||
* <p>
|
||||
* The first entry in the <tt>RunArray</tt> is always 0.
|
||||
*
|
||||
* @author John Raley
|
||||
*
|
||||
* @see AttributeMap
|
||||
* @see MText
|
||||
* @see RunArray
|
||||
*/
|
||||
|
||||
final class StyleBuffer extends MStyleBuffer implements Externalizable {
|
||||
|
||||
static final String COPYRIGHT =
|
||||
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
|
||||
/**
|
||||
* Creates a new style buffer with length equal to the length of <tt>text</tt>,
|
||||
* and with a single run of <tt>defaultStyle</tt>.
|
||||
*/
|
||||
private static final long serialVersionUID = 22356934;
|
||||
|
||||
private static final int CURRENT_VERSION = 1;
|
||||
private static final int kInitialSize = 10;
|
||||
private RunArray fRunArray;
|
||||
|
||||
private AttributeMap fStyleTable[];
|
||||
|
||||
StyleBuffer(MConstText text, AttributeMap initialStyle) {
|
||||
|
||||
this(text.length(), initialStyle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new style buffer with length <tt>initialLength</tt> and with a
|
||||
* single run of <tt>defaultStyle</tt>.
|
||||
*/
|
||||
|
||||
StyleBuffer(int initialLength, AttributeMap initialStyle) {
|
||||
|
||||
fRunArray = new RunArray(kInitialSize, initialLength);
|
||||
fRunArray.fPosEnd = 0;
|
||||
fRunArray.fRunStart[0] = 0;
|
||||
|
||||
fStyleTable = new AttributeMap[kInitialSize]; // do I really want to do this???
|
||||
|
||||
fStyleTable[0] = initialStyle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Note: this constructor is ONLY for use by the Serialization
|
||||
* mechanism. It does not leave this object in a valid state!
|
||||
*/
|
||||
public StyleBuffer() {
|
||||
}
|
||||
|
||||
public void writeExternal(ObjectOutput out) throws IOException {
|
||||
|
||||
compress();
|
||||
out.writeInt(CURRENT_VERSION);
|
||||
out.writeObject(fRunArray);
|
||||
out.writeObject(fStyleTable);
|
||||
}
|
||||
|
||||
public void readExternal(ObjectInput in) throws IOException,
|
||||
ClassNotFoundException {
|
||||
|
||||
if (in.readInt() != CURRENT_VERSION) {
|
||||
throw new IOException("Invalid version of StyleBuffer");
|
||||
}
|
||||
fRunArray = (RunArray) in.readObject();
|
||||
fStyleTable = (AttributeMap[]) in.readObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shift style and run tables such that the last positive run begins before the given position.
|
||||
* Since there is always a run start at 0, this method ensures that the first run will not be shifted.
|
||||
* This is called by: <tt>insertText</tt> and <tt>deleteText</tt>.
|
||||
* @param pos a position in the text.
|
||||
*/
|
||||
|
||||
private void shiftTableTo(int pos) {
|
||||
|
||||
if (pos == 0)
|
||||
pos = 1;
|
||||
|
||||
int oldNegStart = fRunArray.fNegStart;
|
||||
int oldPosEnd = fRunArray.fPosEnd;
|
||||
|
||||
fRunArray.shiftTableTo(pos);
|
||||
|
||||
if (oldPosEnd > fRunArray.fPosEnd)
|
||||
System.arraycopy(fStyleTable, fRunArray.fPosEnd+1,
|
||||
fStyleTable, fRunArray.fNegStart,
|
||||
oldPosEnd-fRunArray.fPosEnd);
|
||||
else if (oldNegStart < fRunArray.fNegStart)
|
||||
System.arraycopy(fStyleTable, oldNegStart,
|
||||
fStyleTable, oldPosEnd+1,
|
||||
fRunArray.fNegStart-oldNegStart);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the style table to reflect a change in the RunArray's size.
|
||||
*/
|
||||
private void handleArrayResize(int oldNegStart) {
|
||||
|
||||
AttributeMap newStyleTable[] = new AttributeMap[fRunArray.getArrayLength()];
|
||||
System.arraycopy(fStyleTable, 0, newStyleTable, 0, fRunArray.fPosEnd+1);
|
||||
System.arraycopy(fStyleTable, oldNegStart, newStyleTable, fRunArray.fNegStart, (fRunArray.getArrayLength()-fRunArray.fNegStart));
|
||||
fStyleTable = newStyleTable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Minimize the amount of storage used by this object.
|
||||
*/
|
||||
void compress() {
|
||||
|
||||
int oldNegStart = fRunArray.fNegStart;
|
||||
fRunArray.compress();
|
||||
if (fRunArray.fNegStart != oldNegStart) {
|
||||
handleArrayResize(oldNegStart);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Increase the storage capacity of the style and run tables if no room remains.
|
||||
*/
|
||||
private void expandStyleTableIfFull() {
|
||||
|
||||
if (fRunArray.fPosEnd + 1 == fRunArray.fNegStart) {
|
||||
|
||||
int oldNegStart = fRunArray.fNegStart;
|
||||
fRunArray.expandRunTable();
|
||||
handleArrayResize(oldNegStart);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
public MStyleRunIterator createStyleRunIterator(int start, int limit) {
|
||||
|
||||
return new StyleRunIterator(start, limit);
|
||||
}
|
||||
*/
|
||||
/**
|
||||
* Respond to an insertion in the text. The length of the last style run which
|
||||
* begins before <tt>start</tt> is increased by <tt>length</tt>. The run table
|
||||
* is shifted such that the run into which text was inserted is the last positive run.
|
||||
* This implementation assumes that all styles propogate.
|
||||
* @param start the offset where the insertion began
|
||||
* @param length the number of characters inserted
|
||||
*/
|
||||
public void insertText(int start, int limit) {
|
||||
|
||||
shiftTableTo(start);
|
||||
fRunArray.addToCurTextLength(limit - start);
|
||||
}
|
||||
|
||||
/**
|
||||
* Respond to a deletion in the text. The last style run before
|
||||
* <tt>start</tt> is truncated to end at <tt>start</tt>. The
|
||||
* style run containing (<tt>start</tt>+<tt>length</tt>) is set to begin
|
||||
* at (<tt>start</tt>+<tt>length</tt>). Runs in between are deleted.
|
||||
* If the deletion occurs entirely within one style run, the length of the style
|
||||
* run is reduced by <tt>length</tt>.
|
||||
* This implementation assumes that all styles propogate.
|
||||
* This method shifts the run table such that the run in which the delete began
|
||||
* is the last positive run. Other methods depend on this "side effect".
|
||||
* @param start the offset where the deletion began
|
||||
* @param length the offset where the deletion stopped
|
||||
*/
|
||||
public void deleteText(int start, int limit) {
|
||||
|
||||
int length = limit - start;
|
||||
|
||||
// An optimization - if a whole run wasn't deleted we don't
|
||||
// need to check for run merging, which could be expensive.
|
||||
boolean wholeRunDeleted = false;
|
||||
|
||||
shiftTableTo(start);
|
||||
|
||||
int firstRunLimit = fRunArray.getCurTextLength();
|
||||
if (fRunArray.fNegStart < fRunArray.getArrayLength())
|
||||
firstRunLimit += fRunArray.fRunStart[fRunArray.fNegStart];
|
||||
|
||||
if (limit == fRunArray.getCurTextLength()) {
|
||||
fRunArray.fNegStart = fRunArray.getArrayLength();
|
||||
}
|
||||
else if (limit >= firstRunLimit) {
|
||||
|
||||
int end = fRunArray.findRunContaining(limit);
|
||||
if (end != fRunArray.fPosEnd) {
|
||||
fRunArray.fRunStart[end] = limit - fRunArray.getCurTextLength();
|
||||
fRunArray.fNegStart = end;
|
||||
wholeRunDeleted = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (fRunArray.fNegStart != fRunArray.getArrayLength()) {
|
||||
if (start == 0 && limit >= firstRunLimit) {
|
||||
// the first style run was deleted; move first "negative" run into
|
||||
// first position
|
||||
fStyleTable[0] = fStyleTable[fRunArray.fNegStart++];
|
||||
}
|
||||
else if (wholeRunDeleted) {
|
||||
if (fStyleTable[fRunArray.fNegStart].equals(fStyleTable[fRunArray.fPosEnd])) {
|
||||
// merge style runs
|
||||
fRunArray.fNegStart++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fRunArray.addToCurTextLength(-length);
|
||||
|
||||
fRunArray.runStartsChanged();
|
||||
//System.out.println("In deleteText: number of style runs = " + numRuns(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Arrange style table so that old styles in the provided range are removed, and
|
||||
* new styles can be inserted into the insertion gap.
|
||||
* After calling this method, new style starts and styles may be placed
|
||||
* in the insertion gaps of fRunArray.fStyleStart and fStyleTable.
|
||||
* @param start offset in the text where insertion operation begins
|
||||
* @param limit offset in the text where previous styles resume
|
||||
*/
|
||||
private void prepareStyleInsert(int start) {
|
||||
|
||||
if (start == 0) {
|
||||
|
||||
// fRunArray.fPosEnd should be 0 if we're in this branch.
|
||||
|
||||
if (fRunArray.getCurTextLength() > 0) {
|
||||
|
||||
/* Move first existing style run to negative end of buffer.
|
||||
Don't do this if length==0; that is, if there is no real
|
||||
style run at 0.
|
||||
*/
|
||||
|
||||
fRunArray.fNegStart--;
|
||||
fStyleTable[fRunArray.fNegStart] = fStyleTable[0];
|
||||
fRunArray.fRunStart[fRunArray.fNegStart] = -fRunArray.getCurTextLength();
|
||||
}
|
||||
|
||||
fRunArray.fPosEnd = -1;
|
||||
}
|
||||
else {
|
||||
|
||||
// consistency check: start should be in current gap
|
||||
if (fRunArray.fRunStart[fRunArray.fPosEnd] >= start) {
|
||||
throw new Error("Inconsistent state! Start should be within insertion gap.");
|
||||
}
|
||||
|
||||
int endOfInsertionGap = fRunArray.getCurTextLength();
|
||||
if (fRunArray.fNegStart < fRunArray.getArrayLength()) {
|
||||
endOfInsertionGap += fRunArray.fRunStart[fRunArray.fNegStart];
|
||||
}
|
||||
|
||||
if (endOfInsertionGap < start) {
|
||||
throw new Error("Inconsistent state! Start should be within insertion gap.");
|
||||
}
|
||||
|
||||
// if no break at start (on negative end of buffer) make one
|
||||
|
||||
if (endOfInsertionGap != start) {
|
||||
|
||||
// split style run in insertion gap
|
||||
|
||||
expandStyleTableIfFull();
|
||||
|
||||
fRunArray.fNegStart--;
|
||||
fStyleTable[fRunArray.fNegStart] = fStyleTable[fRunArray.fPosEnd];
|
||||
fRunArray.fRunStart[fRunArray.fNegStart] = start - fRunArray.getCurTextLength();
|
||||
|
||||
//System.out.println("splitting run.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean modifyStyles(int start,
|
||||
int limit,
|
||||
StyleModifier modifier,
|
||||
int[] damagedRange) {
|
||||
|
||||
if (limit == start) {
|
||||
return false;
|
||||
}
|
||||
|
||||
shiftTableTo(start);
|
||||
|
||||
int currentRunStart = start;
|
||||
AttributeMap oldStyle;
|
||||
AttributeMap mergeStyle = fStyleTable[fRunArray.fPosEnd];
|
||||
|
||||
if (fRunArray.fNegStart < fRunArray.getArrayLength() &&
|
||||
fRunArray.fRunStart[fRunArray.fNegStart]+fRunArray.getCurTextLength() == start) {
|
||||
|
||||
oldStyle = fStyleTable[fRunArray.fNegStart];
|
||||
++fRunArray.fNegStart;
|
||||
}
|
||||
else {
|
||||
oldStyle = mergeStyle;
|
||||
}
|
||||
|
||||
boolean modifiedAnywhere = false;
|
||||
for(;;) {
|
||||
|
||||
boolean modified = false;
|
||||
|
||||
// push new style into gap on positive side
|
||||
AttributeMap newStyle = modifier.modifyStyle(oldStyle);
|
||||
if (damagedRange != null && !newStyle.equals(oldStyle)) {
|
||||
modified = modifiedAnywhere = true;
|
||||
damagedRange[0] = Math.min(currentRunStart, damagedRange[0]);
|
||||
}
|
||||
|
||||
if (!newStyle.equals(mergeStyle)) {
|
||||
|
||||
if (currentRunStart != 0) {
|
||||
expandStyleTableIfFull();
|
||||
++fRunArray.fPosEnd;
|
||||
}
|
||||
|
||||
fStyleTable[fRunArray.fPosEnd] = newStyle;
|
||||
fRunArray.fRunStart[fRunArray.fPosEnd] = currentRunStart;
|
||||
}
|
||||
|
||||
mergeStyle = newStyle;
|
||||
|
||||
int nextRunStart = fRunArray.getLogicalRunStart(fRunArray.fNegStart);
|
||||
|
||||
if (limit > nextRunStart) {
|
||||
oldStyle = fStyleTable[fRunArray.fNegStart];
|
||||
currentRunStart = nextRunStart;
|
||||
if (modified) {
|
||||
damagedRange[1] = Math.max(currentRunStart, damagedRange[1]);
|
||||
}
|
||||
++fRunArray.fNegStart;
|
||||
}
|
||||
else {
|
||||
if (limit < nextRunStart && !oldStyle.equals(mergeStyle)) {
|
||||
expandStyleTableIfFull();
|
||||
++fRunArray.fPosEnd;
|
||||
fStyleTable[fRunArray.fPosEnd] = oldStyle;
|
||||
fRunArray.fRunStart[fRunArray.fPosEnd] = limit;
|
||||
}
|
||||
if (modified) {
|
||||
damagedRange[1] = Math.max(limit, damagedRange[1]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// merge last run if needed
|
||||
if ((fRunArray.fNegStart < fRunArray.getArrayLength()) &&
|
||||
(fStyleTable[fRunArray.fNegStart].equals(fStyleTable[fRunArray.fPosEnd]))) {
|
||||
fRunArray.fNegStart++;
|
||||
}
|
||||
|
||||
fRunArray.runStartsChanged();
|
||||
|
||||
return modifiedAnywhere;
|
||||
}
|
||||
|
||||
public int styleStart(int pos) {
|
||||
|
||||
if (pos == fRunArray.getCurTextLength()) {
|
||||
return pos;
|
||||
}
|
||||
|
||||
return fRunArray.getLogicalRunStart(fRunArray.findRunContaining(pos));
|
||||
}
|
||||
|
||||
public int styleLimit(int pos) {
|
||||
|
||||
if (pos == fRunArray.getCurTextLength()) {
|
||||
return pos;
|
||||
}
|
||||
|
||||
int run = fRunArray.findRunContaining(pos);
|
||||
|
||||
if (run == fRunArray.fPosEnd) {
|
||||
run = fRunArray.fNegStart;
|
||||
}
|
||||
else {
|
||||
++run;
|
||||
}
|
||||
|
||||
return fRunArray.getLogicalRunStart(run);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return style at location <tt>pos</tt>.
|
||||
* @param pos an offset into the text
|
||||
* @returns the style of the character at <tt>offset</tt>
|
||||
*/
|
||||
public AttributeMap styleAt(int pos) {
|
||||
|
||||
return fStyleTable[ fRunArray.findRunContaining(pos) ];
|
||||
}
|
||||
|
||||
/*
|
||||
* Set run start, run length, and run value in an iterator. This method is
|
||||
* only called by a <tt>StyleRunIterator</tt>.
|
||||
* @param pos an offset into the text. The iterator's run start and run limit are
|
||||
* set to the run containing <tt>pos</tt>.
|
||||
* @param iter the iterator to set
|
||||
*/
|
||||
void setIterator(int pos, StyleRunIterator iter) {
|
||||
|
||||
if ((pos < 0) || (pos > fRunArray.getCurTextLength())) {
|
||||
|
||||
iter.set(null, 0, 0, kNoRun);
|
||||
return;
|
||||
}
|
||||
|
||||
int run = fRunArray.findRunContaining(pos);
|
||||
|
||||
setIteratorUsingRun(run, iter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set run start, run length, and run value in an iterator. This method is
|
||||
* only called by a <tt>StyleRunIterator</tt>.
|
||||
* @param run the index of the run to which the iterator should be set
|
||||
* @param iter the iterator to set
|
||||
*/
|
||||
private void setIteratorUsingRun(int run, StyleRunIterator iter) {
|
||||
|
||||
int lastValidRun = fRunArray.lastRun();
|
||||
|
||||
if (run < 0 || run > lastValidRun) {
|
||||
|
||||
iter.set(null, 0, 0, kNoRun);
|
||||
return;
|
||||
}
|
||||
|
||||
if (run == fRunArray.fPosEnd+1)
|
||||
run = fRunArray.fNegStart;
|
||||
else if (run == fRunArray.fNegStart-1)
|
||||
run = fRunArray.fPosEnd;
|
||||
|
||||
int runStart = fRunArray.fRunStart[run];
|
||||
if (runStart < 0)
|
||||
runStart += fRunArray.getCurTextLength();
|
||||
|
||||
AttributeMap style = fStyleTable[run];
|
||||
|
||||
int nextRun;
|
||||
|
||||
if (run == fRunArray.fPosEnd)
|
||||
nextRun = fRunArray.fNegStart;
|
||||
else
|
||||
nextRun = run + 1;
|
||||
|
||||
int runLimit;
|
||||
|
||||
if (nextRun >= fRunArray.getArrayLength())
|
||||
runLimit = fRunArray.getCurTextLength();
|
||||
else {
|
||||
runLimit = fRunArray.fRunStart[nextRun];
|
||||
if (runLimit < 0)
|
||||
runLimit += fRunArray.getCurTextLength();
|
||||
}
|
||||
|
||||
//System.out.println("setIterator: pos="+pos+", runStart="+runStart+", runLimit="+runLimit+
|
||||
// ", run="+run+", fPosEnd="+fPosEnd);
|
||||
|
||||
iter.set(style, runStart, runLimit, run);
|
||||
}
|
||||
|
||||
public void replace(int start, int limit, MConstText srcText, int srcStart, int srcLimit)
|
||||
{
|
||||
deleteText(start, limit);
|
||||
if (srcStart == srcLimit)
|
||||
return;
|
||||
prepareStyleInsert(start);
|
||||
for (int j2 = srcStart; j2 < srcLimit; j2 = srcText.characterStyleLimit(j2))
|
||||
{
|
||||
AttributeMap attributeMap = srcText.characterStyleAt(j2);
|
||||
if (fRunArray.fPosEnd < 0 || !fStyleTable[fRunArray.fPosEnd].equals(attributeMap))
|
||||
{
|
||||
expandStyleTableIfFull();
|
||||
fRunArray.fPosEnd++;
|
||||
fRunArray.fRunStart[fRunArray.fPosEnd] = j2 - srcStart + start;
|
||||
fStyleTable[fRunArray.fPosEnd] = attributeMap;
|
||||
}
|
||||
}
|
||||
fRunArray.addToCurTextLength(srcLimit - srcStart);
|
||||
if (fRunArray.fNegStart < fRunArray.getArrayLength() && fStyleTable[fRunArray.fNegStart].equals(fStyleTable[fRunArray.fPosEnd]))
|
||||
fRunArray.fNegStart++;
|
||||
}
|
||||
|
||||
private static final int kNoRun = -42; // iterator use
|
||||
|
||||
private final class StyleRunIterator /*implements MStyleRunIterator*/ {
|
||||
|
||||
StyleRunIterator(int start, int limit)
|
||||
{
|
||||
reset(start, limit, start);
|
||||
}
|
||||
|
||||
public void reset(int start, int limit, int pos)
|
||||
{
|
||||
fStart = start;
|
||||
fLimit = limit;
|
||||
setIterator(fStart, this);
|
||||
}
|
||||
|
||||
public boolean isValid()
|
||||
{
|
||||
return fStyle != null;
|
||||
}
|
||||
|
||||
public void next()
|
||||
{
|
||||
if (fRunLimit < fLimit) {
|
||||
fCurrentRun++;
|
||||
setIteratorUsingRun(fCurrentRun, this);
|
||||
}
|
||||
else
|
||||
set(null, 0, 0, kNoRun);
|
||||
}
|
||||
|
||||
public void prev()
|
||||
{
|
||||
if (fRunStart > fStart) {
|
||||
fCurrentRun--;
|
||||
setIteratorUsingRun(fCurrentRun, this);
|
||||
}
|
||||
else
|
||||
set(null, 0, 0, kNoRun);
|
||||
}
|
||||
|
||||
public void set(int pos)
|
||||
{
|
||||
if (pos >= fStart && pos < fLimit) {
|
||||
setIterator(pos, this);
|
||||
} else {
|
||||
set(null, 0, 0, kNoRun);
|
||||
}
|
||||
}
|
||||
|
||||
void set(AttributeMap style, int start, int limit, int currentRun)
|
||||
{
|
||||
fStyle = style;
|
||||
fCurrentRun = currentRun;
|
||||
fRunStart = start < fStart ? fStart : start;
|
||||
fRunLimit = limit > fLimit ? fLimit : limit;
|
||||
}
|
||||
|
||||
public void reset(int start, int limit)
|
||||
{
|
||||
reset(start, limit, start);
|
||||
}
|
||||
|
||||
public void first()
|
||||
{
|
||||
set(fStart);
|
||||
}
|
||||
|
||||
public void last()
|
||||
{
|
||||
set(fLimit - 1);
|
||||
}
|
||||
|
||||
public int rangeStart()
|
||||
{
|
||||
return fStart;
|
||||
}
|
||||
|
||||
public int rangeLimit()
|
||||
{
|
||||
return fLimit;
|
||||
}
|
||||
|
||||
public int rangeLength()
|
||||
{
|
||||
return fLimit - fStart;
|
||||
}
|
||||
|
||||
public AttributeMap style()
|
||||
{
|
||||
return fStyle;
|
||||
}
|
||||
|
||||
public int runStart()
|
||||
{
|
||||
return fRunStart;
|
||||
}
|
||||
|
||||
public int runLimit()
|
||||
{
|
||||
return fRunLimit;
|
||||
}
|
||||
|
||||
public int runLength()
|
||||
{
|
||||
return fRunLimit - fRunStart;
|
||||
}
|
||||
|
||||
private int fStart;
|
||||
private int fLimit;
|
||||
private AttributeMap fStyle;
|
||||
private int fRunStart;
|
||||
private int fRunLimit;
|
||||
private int fCurrentRun;
|
||||
}
|
||||
}
|
192
icu4j/src/com/ibm/richtext/styledtext/StyleModifier.java
Executable file
192
icu4j/src/com/ibm/richtext/styledtext/StyleModifier.java
Executable file
|
@ -0,0 +1,192 @@
|
|||
/*
|
||||
* @(#)$RCSfile: StyleModifier.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:45:10 $
|
||||
*
|
||||
* (C) Copyright IBM Corp. 1998-1999. All Rights Reserved.
|
||||
*
|
||||
* The program is provided "as is" without any warranty express or
|
||||
* implied, including the warranty of non-infringement and the implied
|
||||
* warranties of merchantibility and fitness for a particular purpose.
|
||||
* IBM will not be liable for any damages suffered by you as a result
|
||||
* of using the Program. In no event will IBM be liable for any
|
||||
* special, indirect or consequential damages or lost profits even if
|
||||
* IBM has been advised of the possibility of their occurrence. IBM
|
||||
* will not be liable for any third party claims against you.
|
||||
*/
|
||||
package com.ibm.richtext.styledtext;
|
||||
|
||||
import com.ibm.textlayout.attributes.AttributeMap;
|
||||
import com.ibm.textlayout.attributes.AttributeSet;
|
||||
|
||||
/**
|
||||
* StyleModifier is the base class for operations on AttributeMap. To implement
|
||||
* an operation on AttributeMap, subclass StyleModifier and override
|
||||
* <code>modifyStyle</code>. StyleModifiers are used by MText.
|
||||
* <p>
|
||||
* For convenience, this class contains factory methods which will create a
|
||||
* StyleModifier for
|
||||
* certain common operations: attribute union, attribute removal, and AttributeMap
|
||||
* replacement.
|
||||
* @see AttributeMap
|
||||
* @see AttributeSet
|
||||
* @see MText
|
||||
*/
|
||||
/*
|
||||
* {jbr} StyleModifier is not the best name for this class - styles are immutable and never
|
||||
* really modified.
|
||||
*/
|
||||
public class StyleModifier
|
||||
{
|
||||
static final String COPYRIGHT =
|
||||
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
|
||||
/**
|
||||
* Create a StyleModifier.
|
||||
*/
|
||||
protected StyleModifier() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the result of this StyleModifier's operation on the given style.
|
||||
* Default implementation just returns the given style.
|
||||
* @param style the AttributeMap to perform the operation on
|
||||
*/
|
||||
public AttributeMap modifyStyle(AttributeMap style)
|
||||
{
|
||||
return style;
|
||||
}
|
||||
|
||||
/**
|
||||
* A StyleModifier which simply returns the given style.
|
||||
*/
|
||||
public static final StyleModifier IDENTITY = new StyleModifier();
|
||||
|
||||
/**
|
||||
* Create a StyleModifier whose operation is
|
||||
* <code>style.addAttributes(s)</code>,
|
||||
* where <code>style</code> is the AttributeMap passed to
|
||||
* <code>modifyStyle</code>.
|
||||
* @param s the AttributeMap to union with
|
||||
* @return a StyleModifier for this operation
|
||||
*/
|
||||
public static StyleModifier createAddModifier(AttributeMap s) {
|
||||
|
||||
return new StyleAddModifier(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a StyleModifier whose operation is
|
||||
* <code>style.addAttribute(key, value)</code>,
|
||||
* where <code>style</code> is the AttributeMap passed to
|
||||
* <code>modifyStyle</code>.
|
||||
* @param key the key to add
|
||||
* @param value the value to add
|
||||
* @return a StyleModifier for this operation
|
||||
*/
|
||||
public static StyleModifier createAddModifier(Object key,
|
||||
Object value) {
|
||||
|
||||
return new AttributeAddModifier(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a StyleModifier whose operation returns <code>s</code>,
|
||||
* ignoring the parameter to <code>modifyStyle</code>.
|
||||
* @param s the AttributeMap which will replace any other AttributeMap
|
||||
* @return a StyleModifier for this operation
|
||||
*/
|
||||
public static StyleModifier createReplaceModifier(AttributeMap s) {
|
||||
|
||||
return new StyleReplaceModifier(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a StyleModifier whose operation is
|
||||
* <code>style.removeAttributes(s)</code>,
|
||||
* where <code>style</code> is the AttributeMap passed to
|
||||
* <code>modifyStyle</code>.
|
||||
* @param s the AttributeSet of attributes to remove
|
||||
* @return a StyleModifier for this operation
|
||||
*/
|
||||
public static StyleModifier createRemoveModifier(AttributeSet s) {
|
||||
|
||||
return new StyleRemoveModifier(s);
|
||||
}
|
||||
|
||||
static final class AttributeAddModifier extends StyleModifier {
|
||||
|
||||
private Object fKey;
|
||||
private Object fValue;
|
||||
|
||||
public AttributeAddModifier(Object key, Object value) {
|
||||
|
||||
fKey = key;
|
||||
fValue = value;
|
||||
}
|
||||
|
||||
public AttributeMap modifyStyle(AttributeMap style) {
|
||||
|
||||
return style.addAttribute(fKey, fValue);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create this with the styles to add. These styles will add to and override any already
|
||||
* present in the style passed to modifyStyle.
|
||||
*/
|
||||
static final class StyleAddModifier extends StyleModifier
|
||||
{
|
||||
private AttributeMap fStyle;
|
||||
|
||||
public StyleAddModifier(AttributeMap style)
|
||||
{
|
||||
if (style == null) {
|
||||
throw new IllegalArgumentException("style is null");
|
||||
}
|
||||
fStyle = style;
|
||||
}
|
||||
|
||||
public AttributeMap modifyStyle(AttributeMap style)
|
||||
{
|
||||
return style.addAttributes(fStyle);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create this with the styles to replace. All style runs will have only these
|
||||
* styles.
|
||||
*/
|
||||
static final class StyleReplaceModifier extends StyleModifier
|
||||
{
|
||||
private AttributeMap fStyle;
|
||||
|
||||
public StyleReplaceModifier(AttributeMap style)
|
||||
{
|
||||
if (style == null) {
|
||||
throw new IllegalArgumentException("style is null");
|
||||
}
|
||||
fStyle = style;
|
||||
}
|
||||
|
||||
public AttributeMap modifyStyle(AttributeMap style)
|
||||
{
|
||||
return fStyle;
|
||||
}
|
||||
}
|
||||
|
||||
static final class StyleRemoveModifier extends StyleModifier {
|
||||
|
||||
private AttributeSet fRemoveSet;
|
||||
|
||||
public StyleRemoveModifier(AttributeSet removeSet) {
|
||||
|
||||
if (removeSet == null) {
|
||||
throw new IllegalArgumentException("set is null");
|
||||
}
|
||||
fRemoveSet = removeSet;
|
||||
}
|
||||
|
||||
public AttributeMap modifyStyle(AttributeMap style) {
|
||||
|
||||
return style.removeAttributes(fRemoveSet);
|
||||
}
|
||||
}
|
||||
}
|
686
icu4j/src/com/ibm/richtext/styledtext/StyledText.java
Executable file
686
icu4j/src/com/ibm/richtext/styledtext/StyledText.java
Executable file
|
@ -0,0 +1,686 @@
|
|||
/*
|
||||
* @(#)$RCSfile: StyledText.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:45:10 $
|
||||
*
|
||||
* (C) Copyright IBM Corp. 1998-1999. All Rights Reserved.
|
||||
*
|
||||
* The program is provided "as is" without any warranty express or
|
||||
* implied, including the warranty of non-infringement and the implied
|
||||
* warranties of merchantibility and fitness for a particular purpose.
|
||||
* IBM will not be liable for any damages suffered by you as a result
|
||||
* of using the Program. In no event will IBM be liable for any
|
||||
* special, indirect or consequential damages or lost profits even if
|
||||
* IBM has been advised of the possibility of their occurrence. IBM
|
||||
* will not be liable for any third party claims against you.
|
||||
*/
|
||||
package com.ibm.richtext.styledtext;
|
||||
|
||||
import com.ibm.textlayout.attributes.AttributeMap;
|
||||
|
||||
import java.io.Externalizable;
|
||||
import java.io.ObjectInput;
|
||||
import java.io.ObjectOutput;
|
||||
import java.io.IOException;
|
||||
import java.text.CharacterIterator;
|
||||
|
||||
/**
|
||||
* This class is an implementation of MText, a modifyable, styled text
|
||||
* storage model. Additionally, it supports persistance through the
|
||||
* Externalizable interface.
|
||||
* @see MText
|
||||
*/
|
||||
|
||||
/*
|
||||
10/28/96 {jf} - split the character and paragraph style access and setter function around...
|
||||
just to keep things interesting.
|
||||
8/7/96 {jf} - moved paragraph break implementation from AbstractText into Style text.
|
||||
- added countStyles, getStyles, and ReplaceStyles implementation.
|
||||
|
||||
8/14/96 sfb eliminated StyleSheetIterator
|
||||
|
||||
8/29/96 {jbr} changed iter-based replace method - doesn't call at() unless it is safe to do so
|
||||
Also, added checkStartAndLimit for debugging
|
||||
|
||||
7/31/98 Switched from Style to AttributeMap
|
||||
|
||||
*/
|
||||
|
||||
public final class StyledText extends MText implements Externalizable
|
||||
{
|
||||
static final String COPYRIGHT =
|
||||
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
|
||||
private static final int CURRENT_VERSION = 1;
|
||||
private static final long serialVersionUID = 22356934;
|
||||
|
||||
/* unicode storage */
|
||||
private MCharBuffer fCharBuffer;
|
||||
/* character style storage */
|
||||
private MStyleBuffer fStyleBuffer;
|
||||
/* paragraph style storage */
|
||||
private MParagraphBuffer fParagraphBuffer;
|
||||
|
||||
private transient int fTimeStamp = 0;
|
||||
private transient int[] fDamagedRange = { Integer.MAX_VALUE,
|
||||
Integer.MIN_VALUE };
|
||||
|
||||
private static class ForceModifier extends StyleModifier {
|
||||
|
||||
private AttributeMap fStyle = AttributeMap.EMPTY_ATTRIBUTE_MAP;
|
||||
|
||||
void setStyle(AttributeMap style) {
|
||||
|
||||
fStyle = style;
|
||||
}
|
||||
|
||||
public AttributeMap modifyStyle(AttributeMap style) {
|
||||
|
||||
return fStyle;
|
||||
}
|
||||
}
|
||||
|
||||
// Keep this around foruse in replaceCharStylesWith. OK since
|
||||
// this class isn't threadsafe anyway.
|
||||
private transient ForceModifier forceModifier = null;
|
||||
|
||||
//======================================================
|
||||
// CONSTRUCTORS
|
||||
//======================================================
|
||||
/**
|
||||
* Create an empty text object.
|
||||
*/
|
||||
public StyledText()
|
||||
{
|
||||
this(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an empty text object ready to hold at least capacity chars.
|
||||
* @param capacity the minimum capacity of the internal text buffer
|
||||
*/
|
||||
public StyledText(int capacity)
|
||||
{
|
||||
fCharBuffer = capacity>0? new CharBuffer(capacity) : new CharBuffer();
|
||||
fStyleBuffer = new StyleBuffer(this, AttributeMap.EMPTY_ATTRIBUTE_MAP);
|
||||
fParagraphBuffer = new ParagraphBuffer(fCharBuffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a text object with the characters in the string,
|
||||
* in the given style.
|
||||
* @param string the initial contents
|
||||
* @param initialStyle the style of the initial text
|
||||
*/
|
||||
public StyledText(String string, AttributeMap initialStyle)
|
||||
{
|
||||
fCharBuffer = new CharBuffer(string.length());
|
||||
fCharBuffer.replace(0, 0, string, 0, string.length());
|
||||
|
||||
fStyleBuffer = new StyleBuffer(this, initialStyle);
|
||||
fParagraphBuffer = new ParagraphBuffer(fCharBuffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a text object from the given source.
|
||||
* @param source the text to copy
|
||||
*/
|
||||
public StyledText(MConstText source) {
|
||||
this();
|
||||
append(source);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a text object from a subrange of the given source.
|
||||
* @param source the text to copy from
|
||||
* @param srcStart the index of the first character to copy
|
||||
* @param srcLimit the index after the last character to copy
|
||||
*/
|
||||
public StyledText(MConstText source, int srcStart, int srcLimit) {
|
||||
this();
|
||||
replace(0, 0, source, srcStart, srcLimit);
|
||||
}
|
||||
|
||||
public void writeExternal(ObjectOutput out) throws IOException {
|
||||
|
||||
out.writeInt(CURRENT_VERSION);
|
||||
out.writeObject(fCharBuffer);
|
||||
out.writeObject(fStyleBuffer);
|
||||
out.writeObject(fParagraphBuffer);
|
||||
}
|
||||
|
||||
public void readExternal(ObjectInput in) throws IOException,
|
||||
ClassNotFoundException {
|
||||
|
||||
int version = in.readInt();
|
||||
if (version != CURRENT_VERSION) {
|
||||
throw new IOException("Invalid version of StyledText: " + version);
|
||||
}
|
||||
fCharBuffer = (MCharBuffer) in.readObject();
|
||||
fStyleBuffer = (MStyleBuffer) in.readObject();
|
||||
fParagraphBuffer = (MParagraphBuffer) in.readObject();
|
||||
|
||||
resetDamagedRange();
|
||||
}
|
||||
|
||||
//======================================================
|
||||
// MConstText INTERFACES
|
||||
//======================================================
|
||||
|
||||
//--------------------------------------------------------
|
||||
// character access
|
||||
//--------------------------------------------------------
|
||||
/**
|
||||
* Return the character at offset <code>pos</code>.
|
||||
* @param pos a valid offset into the text
|
||||
* @returns the character at offset <code>pos</code>
|
||||
*/
|
||||
public char at(int pos)
|
||||
{
|
||||
return fCharBuffer.at(pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the characters in the range [<code>start</code>, <code>limit</code>)
|
||||
* into the array <code>dst</code>, beginning at <code>dstStart</code>.
|
||||
* @param start offset of first character which will be copied into the array
|
||||
* @param limit offset immediately after the last character which will be copied into the array
|
||||
* @param dst array in which to copy characters. The length of <code>dst</code> must be at least
|
||||
* (<code>dstStart + limit - start</code>).
|
||||
*/
|
||||
public void extractChars(int start, int limit, char[] dst, int dstStart)
|
||||
{
|
||||
fCharBuffer.at(start, limit, dst, dstStart);
|
||||
}
|
||||
|
||||
//-------------------------------------------------------
|
||||
// text model creation
|
||||
//-------------------------------------------------------
|
||||
/**
|
||||
* Create an MConstText containing the characters and styles in the range
|
||||
* [<code>start</code>, <code>limit</code>).
|
||||
* @param start offset of first character in the new text
|
||||
* @param limit offset immediately after the last character in the new text
|
||||
* @return an MConstText object containing the characters and styles in the given range
|
||||
*/
|
||||
public MConstText extract(int start, int limit)
|
||||
{
|
||||
return extractWritable(start, limit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an MText containing the characters and styles in the range
|
||||
* [<code>start</code>, <code>limit</code>).
|
||||
* @param start offset of first character in the new text
|
||||
* @param limit offset immediately after the last character in the new text
|
||||
* @return an MConstText object containing the characters and styles in the given range
|
||||
*/
|
||||
public MText extractWritable(int start, int limit)
|
||||
{
|
||||
MText text = new StyledText();
|
||||
text.replace(0, 0, this, start, limit);
|
||||
text.resetDamagedRange();
|
||||
return text;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------
|
||||
// size/capacity
|
||||
//--------------------------------------------------------
|
||||
/**
|
||||
* Return the length of the MConstText object. The length is the number of characters in the text.
|
||||
* @return the length of the MConstText object
|
||||
*/
|
||||
public int length()
|
||||
{
|
||||
return fCharBuffer.length();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a <code>CharacterIterator</code> over the range [<code>start</code>, <code>limit</code>).
|
||||
* @param start the beginning of the iterator's range
|
||||
* @param limit the limit of the iterator's range
|
||||
* @returns a valid <code>CharacterIterator</code> over the specified range
|
||||
* @see java.text.CharacterIterator
|
||||
*/
|
||||
public CharacterIterator createCharacterIterator(int start, int limit)
|
||||
{
|
||||
return fCharBuffer.createCharacterIterator(start, limit);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------
|
||||
// character styles
|
||||
//--------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Return the index of the first character in the character style run
|
||||
* containing pos. All characters in a style run have the same character
|
||||
* style.
|
||||
* @returns the style at offset <code>pos</code>
|
||||
*/
|
||||
public int characterStyleStart(int pos) {
|
||||
|
||||
checkPos(pos, LESS_THAN_LENGTH);
|
||||
return fStyleBuffer.styleStart(pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the index after the last character in the character style run
|
||||
* containing pos. All characters in a style run have the same character
|
||||
* style.
|
||||
* @returns the style at offset <code>pos</code>
|
||||
*/
|
||||
public int characterStyleLimit(int pos) {
|
||||
|
||||
checkPos(pos, NOT_GREATER_THAN_LENGTH);
|
||||
return fStyleBuffer.styleLimit(pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the style applied to the character at offset <code>pos</code>.
|
||||
* @param pos a valid offset into the text
|
||||
* @returns the style at offset <code>pos</code>
|
||||
*/
|
||||
public AttributeMap characterStyleAt(int pos)
|
||||
{
|
||||
checkPos(pos, NOT_GREATER_THAN_LENGTH);
|
||||
return fStyleBuffer.styleAt(pos);
|
||||
}
|
||||
|
||||
//--------------------------------------------------------
|
||||
// paragraph boundaries and styles
|
||||
//--------------------------------------------------------
|
||||
/**
|
||||
* Return the start of the paragraph containing the character at offset <code>pos</code>.
|
||||
* @param pos a valid offset into the text
|
||||
* @returns the start of the paragraph containing the character at offset <code>pos</code>
|
||||
*/
|
||||
public int paragraphStart(int pos)
|
||||
{
|
||||
checkPos(pos, NOT_GREATER_THAN_LENGTH);
|
||||
return fParagraphBuffer.paragraphStart(pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the limit of the paragraph containing the character at offset <code>pos</code>.
|
||||
* @param pos a valid offset into the text
|
||||
* @returns the limit of the paragraph containing the character at offset <code>pos</code>
|
||||
*/
|
||||
public int paragraphLimit(int pos)
|
||||
{
|
||||
checkPos(pos, NOT_GREATER_THAN_LENGTH);
|
||||
return fParagraphBuffer.paragraphLimit(pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the paragraph style applied to the paragraph containing offset <code>pos</code>.
|
||||
* @param pos a valid offset into the text
|
||||
* @returns the paragraph style in effect at <code>pos</code>
|
||||
*/
|
||||
public AttributeMap paragraphStyleAt(int pos)
|
||||
{
|
||||
checkPos(pos, NOT_GREATER_THAN_LENGTH);
|
||||
return fParagraphBuffer.paragraphStyleAt(pos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current time stamp. The time stamp is
|
||||
* incremented whenever the contents of the MConstText changes.
|
||||
* @return the current paragraph style time stamp
|
||||
*/
|
||||
public int getTimeStamp() {
|
||||
|
||||
return fTimeStamp;
|
||||
}
|
||||
|
||||
//======================================================
|
||||
// MText INTERFACES
|
||||
//======================================================
|
||||
//--------------------------------------------------------
|
||||
// character modfication functions
|
||||
//--------------------------------------------------------
|
||||
|
||||
private void updateDamagedRange(int deleteStart,
|
||||
int deleteLimit,
|
||||
int insertLength) {
|
||||
|
||||
fDamagedRange[0] = Math.min(fDamagedRange[0], deleteStart);
|
||||
|
||||
if (fDamagedRange[1] >= deleteLimit) {
|
||||
int lengthChange = insertLength - (deleteLimit-deleteStart);
|
||||
fDamagedRange[1] += lengthChange;
|
||||
}
|
||||
else {
|
||||
fDamagedRange[1] = deleteStart + insertLength;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the characters and styles in the range [<code>start</code>, <code>limit</code>) with the characters
|
||||
* and styles in <code>srcText</code> in the range [<code>srcStart</code>, <code>srcLimit</code>). <code>srcText</code> is not
|
||||
* modified.
|
||||
* @param start the offset at which the replace operation begins
|
||||
* @param limit the offset at which the replace operation ends. The character and style at
|
||||
* <code>limit</code> is not modified.
|
||||
* @param srcText the source for the new characters and styles
|
||||
* @param srcStart the offset into <code>srcText</code> where new characters and styles will be obtained
|
||||
* @param srcLimit the offset into <code>srcText</code> where the new characters and styles end
|
||||
*/
|
||||
public void replace(int start, int limit, MConstText text, int srcStart, int srcLimit)
|
||||
{
|
||||
if (text == this) {
|
||||
text = new StyledText(text);
|
||||
}
|
||||
|
||||
if (start == limit && srcStart == srcLimit) {
|
||||
return;
|
||||
}
|
||||
|
||||
checkStartLimit(start, limit);
|
||||
|
||||
updateDamagedRange(start, limit, srcLimit-srcStart);
|
||||
|
||||
fCharBuffer.replace(start, limit, text, srcStart, srcLimit);
|
||||
fStyleBuffer.replace(start, limit, text, srcStart, srcLimit);
|
||||
fParagraphBuffer.replace(start, limit, text, srcStart, srcLimit, fDamagedRange);
|
||||
fTimeStamp += 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the characters and styles in the range [<code>start</code>, <code>limit</code>) with the characters
|
||||
* and styles in <code>srcText</code>. <code>srcText</code> is not
|
||||
* modified.
|
||||
* @param start the offset at which the replace operation begins
|
||||
* @param limit the offset at which the replace operation ends. The character and style at
|
||||
* <code>limit</code> is not modified.
|
||||
* @param srcText the source for the new characters and styles
|
||||
*/
|
||||
public void replace(int start, int limit, MConstText text) {
|
||||
|
||||
replace(start, limit, text, 0, text.length());
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the characters in the range [<code>start</code>, <code>limit</code>) with the characters
|
||||
* in <code>srcChars</code> in the range [<code>srcStart</code>, <code>srcLimit</code>). New characters take on the style
|
||||
* <code>charsStyle</code>.
|
||||
* <code>srcChars</code> is not modified.
|
||||
* @param start the offset at which the replace operation begins
|
||||
* @param limit the offset at which the replace operation ends. The character at
|
||||
* <code>limit</code> is not modified.
|
||||
* @param srcChars the source for the new characters
|
||||
* @param srcStart the offset into <code>srcChars</code> where new characters will be obtained
|
||||
* @param srcLimit the offset into <code>srcChars</code> where the new characters end
|
||||
* @param charsStyle the style of the new characters
|
||||
*/
|
||||
public void replace(int start, int limit, char[] srcChars, int srcStart, int srcLimit, AttributeMap charsStyle)
|
||||
{
|
||||
checkStartLimit(start, limit);
|
||||
|
||||
if (start == limit && srcStart == srcLimit) {
|
||||
return;
|
||||
}
|
||||
|
||||
updateDamagedRange(start, limit, srcLimit-srcStart);
|
||||
|
||||
fCharBuffer.replace(start, limit, srcChars, srcStart, srcLimit);
|
||||
|
||||
replaceCharStylesWith(start, limit, start + (srcLimit-srcStart), charsStyle);
|
||||
|
||||
fParagraphBuffer.deleteText(start, limit, fDamagedRange);
|
||||
fParagraphBuffer.insertText(start, srcChars, srcStart, srcLimit);
|
||||
|
||||
fTimeStamp += 1;
|
||||
}
|
||||
|
||||
private void replaceCharStylesWith(int start, int oldLimit, int newLimit, AttributeMap style) {
|
||||
|
||||
if (start < oldLimit) {
|
||||
fStyleBuffer.deleteText(start, oldLimit);
|
||||
}
|
||||
if (start < newLimit) {
|
||||
if (forceModifier == null) {
|
||||
forceModifier = new ForceModifier();
|
||||
}
|
||||
forceModifier.setStyle(style);
|
||||
fStyleBuffer.insertText(start, newLimit);
|
||||
fStyleBuffer.modifyStyles(start, newLimit, forceModifier, null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the characters in the range [<code>start</code>, <code>limit</code>) with the character <code>srcChar</code>.
|
||||
* The new character takes on the style <code>charStyle</code>
|
||||
* @param start the offset at which the replace operation begins
|
||||
* @param limit the offset at which the replace operation ends. The character at
|
||||
* <code>limit</code> is not modified.
|
||||
* @param srcChar the new character
|
||||
* @param charsStyle the style of the new character
|
||||
*/
|
||||
public void replace(int start, int limit, char srcChar, AttributeMap charStyle)
|
||||
{
|
||||
checkStartLimit(start, limit);
|
||||
|
||||
updateDamagedRange(start, limit, 1);
|
||||
|
||||
fCharBuffer.replace(start, limit, srcChar);
|
||||
|
||||
replaceCharStylesWith(start, limit, start + 1, charStyle);
|
||||
|
||||
if (start < limit) {
|
||||
fParagraphBuffer.deleteText(start, limit, fDamagedRange);
|
||||
}
|
||||
|
||||
fParagraphBuffer.insertText(start, srcChar);
|
||||
|
||||
fTimeStamp += 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace the entire contents of this MText (both characters and styles) with
|
||||
* the contents of <code>srcText</code>.
|
||||
* @param srcText the source for the new characters and styles
|
||||
*/
|
||||
public void replaceAll(MConstText srcText)
|
||||
{
|
||||
replace(0, length(), srcText, 0, srcText.length());
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert the contents of <code>srcText</code> (both characters and styles) into this
|
||||
* MText at the position specified by <code>pos</code>.
|
||||
* @param pos The character offset where the new text is to be inserted.
|
||||
* @param srcText The text to insert.
|
||||
*/
|
||||
public void insert(int pos, MConstText srcText)
|
||||
{
|
||||
replace(pos, pos, srcText, 0, srcText.length());
|
||||
}
|
||||
|
||||
/**
|
||||
* Append the contents of <code>srcText</code> (both characters and styles) to the
|
||||
* end of this MText.
|
||||
* @param srcText The text to append.
|
||||
*/
|
||||
public void append(MConstText srcText)
|
||||
{
|
||||
replace(length(), length(), srcText, 0, srcText.length());
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the specified range of characters (and styles).
|
||||
* @param start Offset of the first character to delete.
|
||||
* @param limit Offset of the first character after the range to delete.
|
||||
*/
|
||||
public void remove(int start, int limit)
|
||||
{
|
||||
replace(start, limit, (char[])null, 0, 0, AttributeMap.EMPTY_ATTRIBUTE_MAP);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all characters and styles. Always increments time stamp.
|
||||
*/
|
||||
public void remove()
|
||||
{
|
||||
// rather than going through replace(), just reinitialize the StyledText,
|
||||
// letting the old data structures fall on the floor
|
||||
fCharBuffer = new CharBuffer();
|
||||
fStyleBuffer = new StyleBuffer(this, AttributeMap.EMPTY_ATTRIBUTE_MAP);
|
||||
fParagraphBuffer = new ParagraphBuffer(fCharBuffer);
|
||||
fTimeStamp += 1;
|
||||
fDamagedRange[0] = fDamagedRange[1] = 0;
|
||||
}
|
||||
|
||||
//--------------------------------------------------------
|
||||
// storage management
|
||||
//--------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Minimize the amount of memory used by the MText object.
|
||||
*/
|
||||
public void compress() {
|
||||
|
||||
fCharBuffer.compress();
|
||||
fStyleBuffer.compress();
|
||||
fParagraphBuffer.compress();
|
||||
}
|
||||
|
||||
//--------------------------------------------------------
|
||||
// style modification
|
||||
//--------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Set the style of all characters in the MText object to
|
||||
* <code>AttributeMap.EMPTY_ATTRIBUTE_MAP</code>.
|
||||
*/
|
||||
public void removeCharacterStyles() {
|
||||
|
||||
fStyleBuffer = new StyleBuffer(this, AttributeMap.EMPTY_ATTRIBUTE_MAP);
|
||||
fTimeStamp += 1;
|
||||
fDamagedRange[0] = 0;
|
||||
fDamagedRange[1] = length();
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke the given modifier on all character styles from start to limit.
|
||||
* @param modifier the modifier to apply to the range.
|
||||
* @param start the start of the range of text to modify.
|
||||
* @param limit the limit of the range of text to modify.
|
||||
*/
|
||||
public void modifyCharacterStyles(int start, int limit, StyleModifier modifier) {
|
||||
|
||||
checkStartLimit(start, limit);
|
||||
boolean modified = fStyleBuffer.modifyStyles(start,
|
||||
limit,
|
||||
modifier,
|
||||
fDamagedRange);
|
||||
if (modified) {
|
||||
fTimeStamp += 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke the given modifier on all paragraph styles in paragraphs
|
||||
* containing characters in the range [start, limit).
|
||||
* @param modifier the modifier to apply to the range.
|
||||
* @param start the start of the range of text to modify.
|
||||
* @param limit the limit of the range of text to modify.
|
||||
*/
|
||||
public void modifyParagraphStyles(int start, int limit, StyleModifier modifier) {
|
||||
|
||||
checkStartLimit(start, limit);
|
||||
boolean modified = fParagraphBuffer.modifyParagraphStyles(start,
|
||||
limit,
|
||||
modifier,
|
||||
fDamagedRange);
|
||||
if (modified) {
|
||||
fTimeStamp += 1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the damaged range to an empty interval, and begin accumulating the damaged
|
||||
* range. The damaged range includes every index where a character, character style,
|
||||
* or paragraph style has changed.
|
||||
* @see #damagedRangeStart
|
||||
* @see #damagedRangeLimit
|
||||
*/
|
||||
public void resetDamagedRange() {
|
||||
|
||||
fDamagedRange[0] = Integer.MAX_VALUE;
|
||||
fDamagedRange[1] = Integer.MIN_VALUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the start of the damaged range.
|
||||
* If the start is
|
||||
* <code>Integer.MAX_VALUE</code> and the limit is
|
||||
* <code>Integer.MIN_VALUE</code>, then the damaged range
|
||||
* is empty.
|
||||
* @return the start of the damaged range
|
||||
* @see #damagedRangeLimit
|
||||
* @see #resetDamagedRange
|
||||
*/
|
||||
public int damagedRangeStart() {
|
||||
|
||||
return fDamagedRange[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the limit of the damaged range.
|
||||
* If the start is
|
||||
* <code>Integer.MAX_VALUE</code> and the limit is
|
||||
* <code>Integer.MIN_VALUE</code>, then the damaged range
|
||||
* is empty.
|
||||
* @return the limit of the damaged range
|
||||
* @see #damagedRangeStart
|
||||
* @see #resetDamagedRange
|
||||
*/
|
||||
public int damagedRangeLimit() {
|
||||
|
||||
return fDamagedRange[1];
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
String result ="";
|
||||
for (int i = 0; i < length(); i++) {
|
||||
result += at(i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//======================================================
|
||||
// IMPLEMENTATION
|
||||
//======================================================
|
||||
|
||||
/* check a range to see if it is well formed and within the bounds of the text */
|
||||
private void checkStartLimit(int start, int limit)
|
||||
{
|
||||
if (start > limit) {
|
||||
//System.out.println("Start is less than limit. start:"+start+"; limit:"+limit);
|
||||
throw new IllegalArgumentException("Start is greater than limit. start:"+start+"; limit:"+limit);
|
||||
}
|
||||
|
||||
if (start < 0) {
|
||||
//System.out.println("Start is negative. start:"+start);
|
||||
throw new IllegalArgumentException("Start is negative. start:"+start);
|
||||
}
|
||||
|
||||
if (limit > length()) {
|
||||
//System.out.println("Limit is greater than length. limit:"+limit);
|
||||
throw new IllegalArgumentException("Limit is greater than length. limit:"+limit);
|
||||
}
|
||||
}
|
||||
|
||||
private static final boolean LESS_THAN_LENGTH = false;
|
||||
private static final boolean NOT_GREATER_THAN_LENGTH = true;
|
||||
|
||||
private void checkPos(int pos, boolean endAllowed) {
|
||||
|
||||
int lastValidPos = length();
|
||||
if (endAllowed == LESS_THAN_LENGTH) {
|
||||
--lastValidPos;
|
||||
}
|
||||
|
||||
if (pos < 0 || pos > lastValidPos) {
|
||||
throw new IllegalArgumentException("Position is out of range.");
|
||||
}
|
||||
}
|
||||
}
|
185
icu4j/src/com/ibm/richtext/styledtext/TabStop.java
Executable file
185
icu4j/src/com/ibm/richtext/styledtext/TabStop.java
Executable file
|
@ -0,0 +1,185 @@
|
|||
/*
|
||||
* @(#)$RCSfile: TabStop.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:45:10 $
|
||||
*
|
||||
* (C) Copyright IBM Corp. 1998-1999. All Rights Reserved.
|
||||
*
|
||||
* The program is provided "as is" without any warranty express or
|
||||
* implied, including the warranty of non-infringement and the implied
|
||||
* warranties of merchantibility and fitness for a particular purpose.
|
||||
* IBM will not be liable for any damages suffered by you as a result
|
||||
* of using the Program. In no event will IBM be liable for any
|
||||
* special, indirect or consequential damages or lost profits even if
|
||||
* IBM has been advised of the possibility of their occurrence. IBM
|
||||
* will not be liable for any third party claims against you.
|
||||
*/
|
||||
package com.ibm.richtext.styledtext;
|
||||
|
||||
import java.io.Externalizable;
|
||||
import java.io.ObjectInput;
|
||||
import java.io.ObjectOutput;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* TabStop represents a position on a tab ruler. Each tab stop has a
|
||||
* position, giving its location on the ruler, and one of several
|
||||
* types. The type determines how a segment controled by this TabStop
|
||||
* is positioned on a line:
|
||||
* <ul>
|
||||
* <li><code>kLeading</code> - the leading edge of the segment is aligned to
|
||||
* the TabStop's position</li>
|
||||
* <li><code>kCenter</code> - the segment is centered on this TabStop's
|
||||
* position</li>
|
||||
* <li><code>kTrailing</code> - the trailing edge of the segment is aligned to
|
||||
* the TabStop's position</li>
|
||||
* <li><code>kDecimal</code> - the first decimal in the segment is aligned to
|
||||
* the TabStop's position</li>
|
||||
* <li><code>kAuto</code> - semantically the same as <code>kLeading</code>.
|
||||
* Used by tab rulers to indicate that all subsequent tab stops
|
||||
* will be at autospaced intervals.</li>
|
||||
* </ul>
|
||||
* @see MTabRuler
|
||||
*/
|
||||
public final class TabStop implements Externalizable
|
||||
{
|
||||
static final String COPYRIGHT =
|
||||
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
|
||||
private static final int CURRENT_VERSION = 1;
|
||||
private static final long serialVersionUID = 22356934;
|
||||
|
||||
private byte fType; // left, center, right, decimal
|
||||
private int fPosition; // tab stop position from line origin.
|
||||
|
||||
/**
|
||||
* A TabStop with this type aligns its segment's leading edge
|
||||
* to the TabStop's position.
|
||||
*/
|
||||
public static final byte kLeading = 0;
|
||||
|
||||
/**
|
||||
* A TabStop with this type aligns its segment's center
|
||||
* to the TabStop's position.
|
||||
*/
|
||||
public static final byte kCenter = 1;
|
||||
|
||||
/**
|
||||
* A TabStop with this type aligns its segment's trailing edge
|
||||
* to the TabStop's position.
|
||||
*/
|
||||
public static final byte kTrailing = 2;
|
||||
|
||||
/**
|
||||
* A TabStop with this type aligns its segment's first decimal
|
||||
* to the TabStop's position.
|
||||
*/
|
||||
public static final byte kDecimal = 3;
|
||||
|
||||
/**
|
||||
* A TabStop with this type aligns its segment's leading edge
|
||||
* to the TabStop's position. After a TabStop of this type,
|
||||
* all tabs are at autospace intervals. Usually, clients will
|
||||
* not construct TabStops with this type.
|
||||
*/
|
||||
public static final byte kAuto = 4;
|
||||
|
||||
/**
|
||||
* Create a TabStop with position 0 and type <code>kLeading</code>.
|
||||
*/
|
||||
public TabStop() {
|
||||
|
||||
this(0, kLeading);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a TabStop with the given position and type.
|
||||
* @param position the TabStop's position
|
||||
* @param type the TabStop's type. Must be one of constants
|
||||
* in this class.
|
||||
*/
|
||||
public TabStop(int position, byte type) {
|
||||
|
||||
if (type < kLeading || type > kAuto) {
|
||||
throw new IllegalArgumentException("Invalid tab type");
|
||||
}
|
||||
|
||||
fPosition = position;
|
||||
fType = type;
|
||||
}
|
||||
|
||||
public void readExternal(ObjectInput in) throws IOException,
|
||||
ClassNotFoundException {
|
||||
|
||||
int version = in.readInt();
|
||||
if (version != CURRENT_VERSION) {
|
||||
throw new IOException("Invalid version of TabStop.");
|
||||
}
|
||||
fPosition = in.readInt();
|
||||
fType = in.readByte();
|
||||
}
|
||||
|
||||
public void writeExternal(ObjectOutput out) throws IOException {
|
||||
|
||||
out.writeInt(CURRENT_VERSION);
|
||||
out.writeInt(fPosition);
|
||||
out.writeByte(fType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare this to another Object. TabStops are equal if
|
||||
* their position and type are the same.
|
||||
*/
|
||||
public boolean equals(Object rhs)
|
||||
{
|
||||
if (rhs == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TabStop rhsTab;
|
||||
try {
|
||||
rhsTab = (TabStop) rhs;
|
||||
}
|
||||
catch(ClassCastException e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return fType == rhsTab.fType && fPosition == rhsTab.fPosition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the hash code for this TabStop. The hash code is
|
||||
* <code>position << type</code>.
|
||||
*/
|
||||
public int hashCode() {
|
||||
|
||||
return fPosition << fType;
|
||||
}
|
||||
|
||||
public String toString()
|
||||
{
|
||||
char typeChar;
|
||||
switch (fType) {
|
||||
case kLeading: typeChar = 'L'; break;
|
||||
case kCenter: typeChar = 'C'; break;
|
||||
case kTrailing: typeChar = 'R'; break;
|
||||
case kDecimal: typeChar = 'D'; break;
|
||||
case kAuto: typeChar = 'A'; break;
|
||||
default: typeChar = '?'; break;
|
||||
}
|
||||
return "TabStop[" + Integer.toString(fPosition) + typeChar + ']';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the type of this TabStop. Will be one of the constants
|
||||
* in this class.
|
||||
*/
|
||||
public byte getType() {
|
||||
return fType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the position of this TabStop.
|
||||
*/
|
||||
public int getPosition() {
|
||||
return fPosition;
|
||||
}
|
||||
}
|
||||
|
60
icu4j/src/com/ibm/richtext/styledtext/TestFastIntBinarySearch.java
Executable file
60
icu4j/src/com/ibm/richtext/styledtext/TestFastIntBinarySearch.java
Executable file
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* @(#)$RCSfile: TestFastIntBinarySearch.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:45:10 $
|
||||
*
|
||||
* (C) Copyright IBM Corp. 1998-1999. All Rights Reserved.
|
||||
*
|
||||
* The program is provided "as is" without any warranty express or
|
||||
* implied, including the warranty of non-infringement and the implied
|
||||
* warranties of merchantibility and fitness for a particular purpose.
|
||||
* IBM will not be liable for any damages suffered by you as a result
|
||||
* of using the Program. In no event will IBM be liable for any
|
||||
* special, indirect or consequential damages or lost profits even if
|
||||
* IBM has been advised of the possibility of their occurrence. IBM
|
||||
* will not be liable for any third party claims against you.
|
||||
*/
|
||||
package com.ibm.richtext.styledtext;
|
||||
|
||||
final class TestFastIntBinarySearch {
|
||||
|
||||
static final String COPYRIGHT =
|
||||
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
|
||||
public static void main(String[] args) {
|
||||
|
||||
boolean result = new TestFastIntBinarySearch().test();
|
||||
System.out.println(result? "PASSED" : "FAILED");
|
||||
}
|
||||
|
||||
public boolean test() {
|
||||
|
||||
boolean result = true;
|
||||
int[] test = {-5, -3, 0, 2, 5};
|
||||
FastIntBinarySearch fibs = new FastIntBinarySearch(test);
|
||||
|
||||
for (int i=0; i < 2; i++) {
|
||||
int beforeAny = fibs.findIndex(-6);
|
||||
if (beforeAny != -1) {
|
||||
result = false;
|
||||
}
|
||||
|
||||
int atEnd = fibs.findIndex(5);
|
||||
if (atEnd != test.length-1) {
|
||||
result = false;
|
||||
}
|
||||
|
||||
int afterAny = fibs.findIndex(6);
|
||||
if (afterAny != test.length-1) {
|
||||
result = false;
|
||||
}
|
||||
|
||||
int exactly = fibs.findIndex(-3);
|
||||
if (exactly != 1) {
|
||||
result = false;
|
||||
}
|
||||
|
||||
fibs = new FastIntBinarySearch(new int[] {20, 40});
|
||||
fibs.setData(test);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
39
icu4j/src/com/ibm/richtext/styledtext/Validation.java
Executable file
39
icu4j/src/com/ibm/richtext/styledtext/Validation.java
Executable file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* @(#)$RCSfile: Validation.java,v $ $Revision: 1.1 $ $Date: 2000/04/20 17:45:10 $
|
||||
*
|
||||
* (C) Copyright IBM Corp. 1998-1999. All Rights Reserved.
|
||||
*
|
||||
* The program is provided "as is" without any warranty express or
|
||||
* implied, including the warranty of non-infringement and the implied
|
||||
* warranties of merchantibility and fitness for a particular purpose.
|
||||
* IBM will not be liable for any damages suffered by you as a result
|
||||
* of using the Program. In no event will IBM be liable for any
|
||||
* special, indirect or consequential damages or lost profits even if
|
||||
* IBM has been advised of the possibility of their occurrence. IBM
|
||||
* will not be liable for any third party claims against you.
|
||||
*/
|
||||
package com.ibm.richtext.styledtext;
|
||||
|
||||
/**
|
||||
* Iterators use this class to keep from getting out of sync with
|
||||
* their underlying data. When created, the iterator gets a
|
||||
* Validation instance. If the underlying data changes, the Validation
|
||||
* becomes invalid. Usually iterators will throw exceptions if accessed
|
||||
* after becoming invalid.
|
||||
*/
|
||||
final class Validation {
|
||||
|
||||
static final String COPYRIGHT =
|
||||
"(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
|
||||
private boolean fIsValid = true;
|
||||
|
||||
boolean isValid() {
|
||||
|
||||
return fIsValid;
|
||||
}
|
||||
|
||||
void invalidate() {
|
||||
|
||||
fIsValid = false;
|
||||
}
|
||||
}
|
5
icu4j/src/com/ibm/richtext/styledtext/package.html
Executable file
5
icu4j/src/com/ibm/richtext/styledtext/package.html
Executable file
|
@ -0,0 +1,5 @@
|
|||
<html>
|
||||
<body bgcolor="white">
|
||||
Provides styled text storage and related classes.
|
||||
</body>
|
||||
</html>
|
Loading…
Add table
Reference in a new issue