RichEdit control classes - styled text storage.

X-SVN-Rev: 1185
This commit is contained in:
Alan Liu 2000-04-20 17:45:10 +00:00
parent 7dd3e16ec6
commit 0622187d5a
19 changed files with 4781 additions and 0 deletions

View 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)];
}
}

View 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;
}
}
}

View 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;
}
}

View 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();
}

View 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;
}
}

View 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();
}

View 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();
}

View 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);
}
}

View 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();
}

View 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;
}
}

View 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;
}
}

View 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());
}
}

View 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;
}
}

View 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);
}
}
}

View 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.");
}
}
}

View 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;
}
}

View 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;
}
}

View 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;
}
}

View file

@ -0,0 +1,5 @@
<html>
<body bgcolor="white">
Provides styled text storage and related classes.
</body>
</html>