diff --git a/icu4j/src/com/ibm/richtext/styledtext/CharBuffer.java b/icu4j/src/com/ibm/richtext/styledtext/CharBuffer.java
new file mode 100755
index 00000000000..7108f7a9901
--- /dev/null
+++ b/icu4j/src/com/ibm/richtext/styledtext/CharBuffer.java
@@ -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)];
+ }
+}
diff --git a/icu4j/src/com/ibm/richtext/styledtext/CharBufferIterator.java b/icu4j/src/com/ibm/richtext/styledtext/CharBufferIterator.java
new file mode 100755
index 00000000000..6a0ac16f793
--- /dev/null
+++ b/icu4j/src/com/ibm/richtext/styledtext/CharBufferIterator.java
@@ -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;
+ }
+ }
+}
diff --git a/icu4j/src/com/ibm/richtext/styledtext/FastIntBinarySearch.java b/icu4j/src/com/ibm/richtext/styledtext/FastIntBinarySearch.java
new file mode 100755
index 00000000000..e297a7b71e7
--- /dev/null
+++ b/icu4j/src/com/ibm/richtext/styledtext/FastIntBinarySearch.java
@@ -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 value. 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;
+ }
+}
diff --git a/icu4j/src/com/ibm/richtext/styledtext/MCharBuffer.java b/icu4j/src/com/ibm/richtext/styledtext/MCharBuffer.java
new file mode 100755
index 00000000000..0eebff17c85
--- /dev/null
+++ b/icu4j/src/com/ibm/richtext/styledtext/MCharBuffer.java
@@ -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();
+}
diff --git a/icu4j/src/com/ibm/richtext/styledtext/MConstText.java b/icu4j/src/com/ibm/richtext/styledtext/MConstText.java
new file mode 100755
index 00000000000..ea6e41956b2
--- /dev/null
+++ b/icu4j/src/com/ibm/richtext/styledtext/MConstText.java
@@ -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 char
. Character and paragraph
+ * styles are represented by the AttributeMap
class.
+ *
+ * Characters in the text are accessed with an integer index using the
+ * at
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
+ * java.text.CharacterIterator
.
+ *
+ * Every character in the text has a character style associated with it,
+ * represented by the AttributeMap
class. The character
+ * style for a particular character can be obtained using the
+ * characterStyleAt
method.
+ *
+ * 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 \n
or U+2029
).
+ * Every
+ * paragraph has a paragraph style associated with it, represented
+ * by the AttributeMap
class. Paragraph boundaries and
+ * styles can be obtained from the MConstText.
+ *
+ * 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. + *
+ * 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 pos
.
+* @param pos a valid offset into the text
+* @returns the character at offset pos
+*/
+ public abstract char at(int pos);
+
+/**
+* Copy the characters in the range [start
, limit
)
+* into the array dst
, beginning at dstStart
.
+* @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 dst
must be at least
+* (dstStart + limit - start
).
+*/
+ public abstract void extractChars(int start, int limit, char[] dst, int dstStart);
+
+/**
+* Create an MConstText containing the characters and styles in the range
+* [start
, limit
).
+* @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 java.text.CharacterIterator
over all
+* of the characters in the text. Default implementation calls
+* createCharacterIterator(0, length())
+* @return a java.text.CharacterIterator
over all
+* of the characters in the text
+*/
+ public CharacterIterator createCharacterIterator() {
+
+ return createCharacterIterator(0, length());
+ }
+
+/**
+* Create a java.text.CharacterIterator
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 java.text.CharacterIterator
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 pos
+*/
+ 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 pos
+*/
+ public abstract int characterStyleLimit(int pos);
+
+/**
+* Return the style applied to the character at offset pos
.
+* @param pos a valid offset into the text
+* @returns the style at offset pos
+*/
+ public abstract AttributeMap characterStyleAt(int pos);
+
+//========================================================
+// PARAGRAPH BOUNDARIES
+//========================================================
+/**
+* Return the start of the paragraph containing the character at offset pos
.
+* @param pos a valid offset into the text
+* @returns the start of the paragraph containing the character at offset pos
+*/
+ public abstract int paragraphStart(int pos);
+
+/**
+* Return the limit of the paragraph containing the character at offset pos
.
+* @param pos a valid offset into the text
+* @returns the limit of the paragraph containing the character at offset pos
+*/
+ public abstract int paragraphLimit(int pos);
+
+/**
+* Return the paragraph style applied to the paragraph containing offset pos
.
+* @param pos a valid offset into the text
+* @returns the paragraph style in effect at pos
+*/
+ 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 rhs
+*/
+ 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 rhs
+*/
+ 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
+ *
+ * where+ * at(0) + + * at(length/2)*31^1 + + * at(length-1)*31^2 + + * characterStyleAt(0).hashCode()*31^3 + + * paragraphStyleAt(length-1).hashCode()*31^4 + *
^
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;
+ }
+}
\ No newline at end of file
diff --git a/icu4j/src/com/ibm/richtext/styledtext/MParagraphBuffer.java b/icu4j/src/com/ibm/richtext/styledtext/MParagraphBuffer.java
new file mode 100755
index 00000000000..6fb4f311b8e
--- /dev/null
+++ b/icu4j/src/com/ibm/richtext/styledtext/MParagraphBuffer.java
@@ -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 pos.
+*/
+ abstract int paragraphStart(int pos);
+
+/**
+* Returns the limit of the paragraph containing offset pos.
+*/
+ abstract int paragraphLimit(int pos);
+
+/**
+* Returns the style of the paragraph containing offset pos.
+*/
+ abstract AttributeMap paragraphStyleAt(int offset);
+
+/**
+* Process a character insertion at offset start.
+* If a paragraph break was inserted, propogate paragraph style at
+* start to new paragraph.
+*/
+ abstract void insertText(int start, char insertedChar);
+
+/**
+* Process character insertion at offset start.
+* Each new paragraph gets paragraph style at
+* start.
+*/
+ 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 srcText.
+* @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 srcText
to copy
+* @param srcLimit the first index after the range in srcText
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
+* style.
+*/
+ abstract boolean modifyParagraphStyles(int start,
+ int limit,
+ StyleModifier modifier,
+ int[] damagedRange);
+
+/**
+* Minimize the amount of memory used by this object.
+*/
+ abstract void compress();
+}
diff --git a/icu4j/src/com/ibm/richtext/styledtext/MStyleBuffer.java b/icu4j/src/com/ibm/richtext/styledtext/MStyleBuffer.java
new file mode 100755
index 00000000000..133fa30a83c
--- /dev/null
+++ b/icu4j/src/com/ibm/richtext/styledtext/MStyleBuffer.java
@@ -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 MText. A "style run" consists of a
+* style and the interval on which the style applies.
+* +* MStyleBuffer includes methods to call when text is inserted into +* or deleted from the MText. These methods update the +* style runs in accordance with the commonly accepted behavior for +* style runs. +*
+* 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. +*
+* MStyleBuffer supplies a method for replacing the style runs on a text range with the runs +* represented in an MStyleRunIterator. 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. +*
+*
+* @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 start is increased by limit-start.
+* @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
+* start is truncated to end at start. The
+* style run containing (start+length) is set to begin
+* at (start+length). Runs in between are deleted.
+* If the deletion occurs entirely within one style run, the length of the style
+* run is reduced by length.
+* @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 start and limit with styles in
+* iter. 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 MStyleRunIterator 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 pos.
+* @param pos an offset into the text
+* @returns the style of the character at offset
+*/
+ 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();
+}
diff --git a/icu4j/src/com/ibm/richtext/styledtext/MTabRuler.java b/icu4j/src/com/ibm/richtext/styledtext/MTabRuler.java
new file mode 100755
index 00000000000..40fa4e1e053
--- /dev/null
+++ b/icu4j/src/com/ibm/richtext/styledtext/MTabRuler.java
@@ -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 firstTab
+ * method; subsequent TabStops are obtained with the nextTab
+ * method.
+ *
+ * If a TabStop with type TabStop.kAuto
is returned, all tabs
+ * after that TabStop will also have type TabStop.kAuto
, and
+ * their positions will be multiples of autoSpacing
.
+ * @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 tabToTest
+ */
+ 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 fromPosition
moved to position
+ * toPosition
. 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);
+ }
+}
\ No newline at end of file
diff --git a/icu4j/src/com/ibm/richtext/styledtext/MText.java b/icu4j/src/com/ibm/richtext/styledtext/MText.java
new file mode 100755
index 00000000000..54be5967559
--- /dev/null
+++ b/icu4j/src/com/ibm/richtext/styledtext/MText.java
@@ -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.
+ *
+ * Styled characters (from another MConstText
instance) added
+ * to the text retain their original character styles. The style of plain characters
+ * (specified as a char
or char[]
) 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.
+ *
+ * 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: + *
+ * 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. + *+ * For example, suppose there is a single paragraph of text with style 'A', + * delimited with a paragraph separator 'P': + *
+ * AAAAAAP + *+ * Suppose the following styled paragraphs are inserted into the above text + * after the fourth character: + *
+ * BBBBPCCCPDDD + *+ * Then the original paragraph style of each character is: + *
+ * AAAABBBBPCCCPDDDAAP + *+ * The resulting paragraph styles are: + *
+ * BBBBBBBBPCCCPAAAAAP + *+ * 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: + *
+ * AAAAAAP + *+ * This paragraph-style propagation policy is sometimes referred to as + * following styles win, since styles at the end of the paragraph + * become the style for the entire paragraph. + *
+ * This class can accumulate a damaged range - 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 [
+* StyleBuffer stores the intervals on which styles apply in a RunArray
+* object (see RunArray for more information). The styles are stored in
+* an array of AttributeMap objects.
+*
+* RunArray 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.
+*
+* StyleBuffer uses the offsets in RunArray as the boundaries of style runs.
+* A style run begins at each offset in RunArray, and each style run continues to
+* the next offset. The style array is kept in sync with the array of offsets in RunArray;
+* that is, the style which begins at RunArray[i] is stored in StyleArray[i].
+*
+* The first entry in the RunArray 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 text,
+ * and with a single run of defaultStyle.
+ */
+ 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 initialLength and with a
+ * single run of defaultStyle.
+ */
+
+ 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: insertText and deleteText.
+* @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 start is increased by length. 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
+* start is truncated to end at start. The
+* style run containing (start+length) is set to begin
+* at (start+length). Runs in between are deleted.
+* If the deletion occurs entirely within one style run, the length of the style
+* run is reduced by length.
+* 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 pos.
+* @param pos an offset into the text
+* @returns the style of the character at offset
+*/
+ 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 StyleRunIterator.
+* @param pos an offset into the text. The iterator's run start and run limit are
+* set to the run containing pos.
+* @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 StyleRunIterator.
+* @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;
+ }
+}
\ No newline at end of file
diff --git a/icu4j/src/com/ibm/richtext/styledtext/StyleModifier.java b/icu4j/src/com/ibm/richtext/styledtext/StyleModifier.java
new file mode 100755
index 00000000000..90453c1136d
--- /dev/null
+++ b/icu4j/src/com/ibm/richtext/styledtext/StyleModifier.java
@@ -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
+ *
+ * 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
+ * start
, limit
) with the characters
+* and styles in srcText
in the range [srcStart
, srcLimit
). srcText
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
+* limit
is not modified.
+* @param srcText the source for the new characters and styles
+* @param srcStart the offset into srcText
where new characters and styles will be obtained
+* @param srcLimit the offset into srcText
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 [start
, limit
) with the characters
+* and styles in srcText
. srcText
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
+* limit
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 [start
, limit
) with the characters
+* in srcChars
in the range [srcStart
, srcLimit
). New characters take on the style
+* charsStyle
.
+* srcChars
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
+* limit
is not modified.
+* @param srcChars the source for the new characters
+* @param srcStart the offset into srcChars
where new characters will be obtained
+* @param srcLimit the offset into srcChars
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 [start
, limit
) with the character srcChar
.
+* The new character takes on the style charStyle
+* @param start the offset at which the replace operation begins
+* @param limit the offset at which the replace operation ends. The character at
+* limit
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 srcText
.
+* @param srcText the source for the new characters and styles
+*/
+ public abstract void replaceAll(MConstText srcText);
+
+/**
+* Insert the contents of srcText
(both characters and styles) into this
+* MText at the position specified by pos
.
+* @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 srcText
(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
+* [start
, limit
).
+* @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
+* AttributeMap.EMPTY_ATTRIBUTE_MAP
.
+*/
+ 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();
+}
diff --git a/icu4j/src/com/ibm/richtext/styledtext/ParagraphBuffer.java b/icu4j/src/com/ibm/richtext/styledtext/ParagraphBuffer.java
new file mode 100755
index 00000000000..555737975e7
--- /dev/null
+++ b/icu4j/src/com/ibm/richtext/styledtext/ParagraphBuffer.java
@@ -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 charBuffer.
+*/
+ 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 start.
+* If a paragraph break was inserted, propogate paragraph style at
+* start 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 start.
+* Each new paragraph gets paragraph style at
+* start.
+*/
+ 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 pos.
+*/
+ 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 pos.
+*/
+ 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 pos.
+*/
+ 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 srcText.
+* @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 srcText
to copy
+* @param srcLimit the first index after the range in srcText
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
+* style.
+*/
+ 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; iTabStop.kAuto
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 tabs
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 TabStop.kAuto
.
+ * @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. ruler
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. 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*/ 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 fromPosition
moved to position
+ * toPosition
.
+ * @param ruler the original ruler. The MTabRuler will be the same as
+ * this except for the moved tab. 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*/ 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());
+ }
+
+}
diff --git a/icu4j/src/com/ibm/richtext/styledtext/StyleBuffer.java b/icu4j/src/com/ibm/richtext/styledtext/StyleBuffer.java
new file mode 100755
index 00000000000..23cdb139a4a
--- /dev/null
+++ b/icu4j/src/com/ibm/richtext/styledtext/StyleBuffer.java
@@ -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 MStyleBuffer. It maintains
+* AttributeMap objects to apply to the text in an MText object,
+* and the
+* intervals on which those styles apply.
+* modifyStyle
. StyleModifiers are used by MText.
+ * style.addAttributes(s)
,
+ * where style
is the AttributeMap passed to
+ * modifyStyle
.
+ * @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
+ * style.addAttribute(key, value)
,
+ * where style
is the AttributeMap passed to
+ * modifyStyle
.
+ * @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 s
,
+ * ignoring the parameter to modifyStyle
.
+ * @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
+ * style.removeAttributes(s)
,
+ * where style
is the AttributeMap passed to
+ * modifyStyle
.
+ * @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);
+ }
+ }
+}
diff --git a/icu4j/src/com/ibm/richtext/styledtext/StyledText.java b/icu4j/src/com/ibm/richtext/styledtext/StyledText.java
new file mode 100755
index 00000000000..55206461278
--- /dev/null
+++ b/icu4j/src/com/ibm/richtext/styledtext/StyledText.java
@@ -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 pos
.
+* @param pos a valid offset into the text
+* @returns the character at offset pos
+*/
+ public char at(int pos)
+ {
+ return fCharBuffer.at(pos);
+ }
+
+/**
+* Copy the characters in the range [start
, limit
)
+* into the array dst
, beginning at dstStart
.
+* @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 dst
must be at least
+* (dstStart + limit - start
).
+*/
+ 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
+* [start
, limit
).
+* @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
+* [start
, limit
).
+* @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 CharacterIterator
over the range [start
, limit
).
+* @param start the beginning of the iterator's range
+* @param limit the limit of the iterator's range
+* @returns a valid CharacterIterator
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 pos
+*/
+ 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 pos
+*/
+ public int characterStyleLimit(int pos) {
+
+ checkPos(pos, NOT_GREATER_THAN_LENGTH);
+ return fStyleBuffer.styleLimit(pos);
+ }
+
+/**
+* Return the style applied to the character at offset pos
.
+* @param pos a valid offset into the text
+* @returns the style at offset pos
+*/
+ 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 pos
.
+* @param pos a valid offset into the text
+* @returns the start of the paragraph containing the character at offset pos
+*/
+ 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 pos
.
+* @param pos a valid offset into the text
+* @returns the limit of the paragraph containing the character at offset pos
+*/
+ 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 pos
.
+* @param pos a valid offset into the text
+* @returns the paragraph style in effect at pos
+*/
+ 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 [start
, limit
) with the characters
+* and styles in srcText
in the range [srcStart
, srcLimit
). srcText
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
+* limit
is not modified.
+* @param srcText the source for the new characters and styles
+* @param srcStart the offset into srcText
where new characters and styles will be obtained
+* @param srcLimit the offset into srcText
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 [start
, limit
) with the characters
+* and styles in srcText
. srcText
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
+* limit
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 [start
, limit
) with the characters
+* in srcChars
in the range [srcStart
, srcLimit
). New characters take on the style
+* charsStyle
.
+* srcChars
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
+* limit
is not modified.
+* @param srcChars the source for the new characters
+* @param srcStart the offset into srcChars
where new characters will be obtained
+* @param srcLimit the offset into srcChars
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 [start
, limit
) with the character srcChar
.
+* The new character takes on the style charStyle
+* @param start the offset at which the replace operation begins
+* @param limit the offset at which the replace operation ends. The character at
+* limit
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 srcText
.
+* @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 srcText
(both characters and styles) into this
+* MText at the position specified by pos
.
+* @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 srcText
(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
+* AttributeMap.EMPTY_ATTRIBUTE_MAP
.
+*/
+ 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
+* Integer.MAX_VALUE
and the limit is
+* Integer.MIN_VALUE
, 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
+* Integer.MAX_VALUE
and the limit is
+* Integer.MIN_VALUE
, 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.");
+ }
+ }
+}
diff --git a/icu4j/src/com/ibm/richtext/styledtext/TabStop.java b/icu4j/src/com/ibm/richtext/styledtext/TabStop.java
new file mode 100755
index 00000000000..b57889cf96c
--- /dev/null
+++ b/icu4j/src/com/ibm/richtext/styledtext/TabStop.java
@@ -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:
+ *
+ *
+ * @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 kLeading
- the leading edge of the segment is aligned to
+ * the TabStop's positionkCenter
- the segment is centered on this TabStop's
+ * positionkTrailing
- the trailing edge of the segment is aligned to
+ * the TabStop's positionkDecimal
- the first decimal in the segment is aligned to
+ * the TabStop's positionkAuto
- semantically the same as kLeading
.
+ * Used by tab rulers to indicate that all subsequent tab stops
+ * will be at autospaced intervals.kLeading
.
+ */
+ 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
+ * position << type
.
+ */
+ 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;
+ }
+}
+
diff --git a/icu4j/src/com/ibm/richtext/styledtext/TestFastIntBinarySearch.java b/icu4j/src/com/ibm/richtext/styledtext/TestFastIntBinarySearch.java
new file mode 100755
index 00000000000..04cbf0ce813
--- /dev/null
+++ b/icu4j/src/com/ibm/richtext/styledtext/TestFastIntBinarySearch.java
@@ -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;
+ }
+}
\ No newline at end of file
diff --git a/icu4j/src/com/ibm/richtext/styledtext/Validation.java b/icu4j/src/com/ibm/richtext/styledtext/Validation.java
new file mode 100755
index 00000000000..2ef7b556825
--- /dev/null
+++ b/icu4j/src/com/ibm/richtext/styledtext/Validation.java
@@ -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;
+ }
+}
diff --git a/icu4j/src/com/ibm/richtext/styledtext/package.html b/icu4j/src/com/ibm/richtext/styledtext/package.html
new file mode 100755
index 00000000000..dd580d30dbf
--- /dev/null
+++ b/icu4j/src/com/ibm/richtext/styledtext/package.html
@@ -0,0 +1,5 @@
+
+
+Provides styled text storage and related classes.
+
+