AttributedStringpublic class AttributedString extends Object An AttributedString holds text and related attribute information. It
may be used as the actual data storage in some cases where a text
reader wants to access attributed text through the AttributedCharacterIterator
interface.
An attribute is a key/value pair, identified by the key. No two
attributes on a given character can have the same key.
The values for an attribute are immutable, or must not be mutated
by clients or storage. They are always passed by reference, and not
cloned. |
Fields Summary |
---|
private static final int | ARRAY_SIZE_INCREMENT | String | text | int | runArraySize | int | runCount | int[] | runStarts | Vector[] | runAttributes | Vector[] | runAttributeValues |
Constructors Summary |
---|
AttributedString(AttributedCharacterIterator[] iterators)Constructs an AttributedString instance with the given
AttributedCharacterIterators. // parallel vector of attribute values for each run
if (iterators == null) {
throw new NullPointerException("Iterators must not be null");
}
if (iterators.length == 0) {
text = "";
}
else {
// Build the String contents
StringBuffer buffer = new StringBuffer();
for (int counter = 0; counter < iterators.length; counter++) {
appendContents(buffer, iterators[counter]);
}
text = buffer.toString();
if (text.length() > 0) {
// Determine the runs, creating a new run when the attributes
// differ.
int offset = 0;
Map last = null;
for (int counter = 0; counter < iterators.length; counter++) {
AttributedCharacterIterator iterator = iterators[counter];
int start = iterator.getBeginIndex();
int end = iterator.getEndIndex();
int index = start;
while (index < end) {
iterator.setIndex(index);
Map attrs = iterator.getAttributes();
if (mapsDiffer(last, attrs)) {
setAttributes(attrs, index - start + offset);
}
last = attrs;
index = iterator.getRunLimit();
}
offset += (end - start);
}
}
}
| public AttributedString(String text)Constructs an AttributedString instance with the given text.
if (text == null) {
throw new NullPointerException();
}
this.text = text;
| public AttributedString(String text, Map attributes)Constructs an AttributedString instance with the given text and attributes.
if (text == null || attributes == null) {
throw new NullPointerException();
}
this.text = text;
if (text.length() == 0) {
if (attributes.isEmpty())
return;
throw new IllegalArgumentException("Can't add attribute to 0-length text");
}
int attributeCount = attributes.size();
if (attributeCount > 0) {
createRunAttributeDataVectors();
Vector newRunAttributes = new Vector(attributeCount);
Vector newRunAttributeValues = new Vector(attributeCount);
runAttributes[0] = newRunAttributes;
runAttributeValues[0] = newRunAttributeValues;
Iterator iterator = attributes.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry entry = (Map.Entry) iterator.next();
newRunAttributes.addElement(entry.getKey());
newRunAttributeValues.addElement(entry.getValue());
}
}
| public AttributedString(AttributedCharacterIterator text)Constructs an AttributedString instance with the given attributed
text represented by AttributedCharacterIterator.
// If performance is critical, this constructor should be
// implemented here rather than invoking the constructor for a
// subrange. We can avoid some range checking in the loops.
this(text, text.getBeginIndex(), text.getEndIndex(), null);
| public AttributedString(AttributedCharacterIterator text, int beginIndex, int endIndex)Constructs an AttributedString instance with the subrange of
the given attributed text represented by
AttributedCharacterIterator. If the given range produces an
empty text, all attributes will be discarded. Note that any
attributes wrapped by an Annotation object are discarded for a
subrange of the original attribute range.
this(text, beginIndex, endIndex, null);
| public AttributedString(AttributedCharacterIterator text, int beginIndex, int endIndex, java.text.AttributedCharacterIterator.Attribute[] attributes)Constructs an AttributedString instance with the subrange of
the given attributed text represented by
AttributedCharacterIterator. Only attributes that match the
given attributes will be incorporated into the instance. If the
given range produces an empty text, all attributes will be
discarded. Note that any attributes wrapped by an Annotation
object are discarded for a subrange of the original attribute
range.
if (text == null) {
throw new NullPointerException();
}
// Validate the given subrange
int textBeginIndex = text.getBeginIndex();
int textEndIndex = text.getEndIndex();
if (beginIndex < textBeginIndex || endIndex > textEndIndex || beginIndex > endIndex)
throw new IllegalArgumentException("Invalid substring range");
// Copy the given string
StringBuffer textBuffer = new StringBuffer();
text.setIndex(beginIndex);
for (char c = text.current(); text.getIndex() < endIndex; c = text.next())
textBuffer.append(c);
this.text = textBuffer.toString();
if (beginIndex == endIndex)
return;
// Select attribute keys to be taken care of
HashSet keys = new HashSet();
if (attributes == null) {
keys.addAll(text.getAllAttributeKeys());
} else {
for (int i = 0; i < attributes.length; i++)
keys.add(attributes[i]);
keys.retainAll(text.getAllAttributeKeys());
}
if (keys.isEmpty())
return;
// Get and set attribute runs for each attribute name. Need to
// scan from the top of the text so that we can discard any
// Annotation that is no longer applied to a subset text segment.
Iterator itr = keys.iterator();
while (itr.hasNext()) {
Attribute attributeKey = (Attribute)itr.next();
text.setIndex(textBeginIndex);
while (text.getIndex() < endIndex) {
int start = text.getRunStart(attributeKey);
int limit = text.getRunLimit(attributeKey);
Object value = text.getAttribute(attributeKey);
if (value != null) {
if (value instanceof Annotation) {
if (start >= beginIndex && limit <= endIndex) {
addAttribute(attributeKey, value, start - beginIndex, limit - beginIndex);
} else {
if (limit > endIndex)
break;
}
} else {
// if the run is beyond the given (subset) range, we
// don't need to process further.
if (start >= endIndex)
break;
if (limit > beginIndex) {
// attribute is applied to any subrange
if (start < beginIndex)
start = beginIndex;
if (limit > endIndex)
limit = endIndex;
if (start != limit) {
addAttribute(attributeKey, value, start - beginIndex, limit - beginIndex);
}
}
}
}
text.setIndex(limit);
}
}
|
Methods Summary |
---|
public void | addAttribute(java.text.AttributedCharacterIterator.Attribute attribute, java.lang.Object value)Adds an attribute to the entire string.
if (attribute == null) {
throw new NullPointerException();
}
int len = length();
if (len == 0) {
throw new IllegalArgumentException("Can't add attribute to 0-length text");
}
addAttributeImpl(attribute, value, 0, len);
| public void | addAttribute(java.text.AttributedCharacterIterator.Attribute attribute, java.lang.Object value, int beginIndex, int endIndex)Adds an attribute to a subrange of the string.
if (attribute == null) {
throw new NullPointerException();
}
if (beginIndex < 0 || endIndex > length() || beginIndex >= endIndex) {
throw new IllegalArgumentException("Invalid substring range");
}
addAttributeImpl(attribute, value, beginIndex, endIndex);
| private synchronized void | addAttributeImpl(java.text.AttributedCharacterIterator.Attribute attribute, java.lang.Object value, int beginIndex, int endIndex)
// make sure we have run attribute data vectors
if (runCount == 0) {
createRunAttributeDataVectors();
}
// break up runs if necessary
int beginRunIndex = ensureRunBreak(beginIndex);
int endRunIndex = ensureRunBreak(endIndex);
addAttributeRunData(attribute, value, beginRunIndex, endRunIndex);
| private void | addAttributeRunData(java.text.AttributedCharacterIterator.Attribute attribute, java.lang.Object value, int beginRunIndex, int endRunIndex)
for (int i = beginRunIndex; i < endRunIndex; i++) {
int keyValueIndex = -1; // index of key and value in our vectors; assume we don't have an entry yet
if (runAttributes[i] == null) {
Vector newRunAttributes = new Vector();
Vector newRunAttributeValues = new Vector();
runAttributes[i] = newRunAttributes;
runAttributeValues[i] = newRunAttributeValues;
} else {
// check whether we have an entry already
keyValueIndex = runAttributes[i].indexOf(attribute);
}
if (keyValueIndex == -1) {
// create new entry
int oldSize = runAttributes[i].size();
runAttributes[i].addElement(attribute);
try {
runAttributeValues[i].addElement(value);
}
catch (Exception e) {
runAttributes[i].setSize(oldSize);
runAttributeValues[i].setSize(oldSize);
}
} else {
// update existing entry
runAttributeValues[i].set(keyValueIndex, value);
}
}
| public void | addAttributes(java.util.Map attributes, int beginIndex, int endIndex)Adds a set of attributes to a subrange of the string.
if (attributes == null) {
throw new NullPointerException();
}
if (beginIndex < 0 || endIndex > length() || beginIndex > endIndex) {
throw new IllegalArgumentException("Invalid substring range");
}
if (beginIndex == endIndex) {
if (attributes.isEmpty())
return;
throw new IllegalArgumentException("Can't add attribute to 0-length text");
}
// make sure we have run attribute data vectors
if (runCount == 0) {
createRunAttributeDataVectors();
}
// break up runs if necessary
int beginRunIndex = ensureRunBreak(beginIndex);
int endRunIndex = ensureRunBreak(endIndex);
Iterator iterator = attributes.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry entry = (Map.Entry) iterator.next();
addAttributeRunData((Attribute) entry.getKey(), entry.getValue(), beginRunIndex, endRunIndex);
}
| private final void | appendContents(java.lang.StringBuffer buf, java.text.CharacterIterator iterator)Appends the contents of the CharacterIterator iterator into the
StringBuffer buf.
int index = iterator.getBeginIndex();
int end = iterator.getEndIndex();
while (index < end) {
iterator.setIndex(index++);
buf.append(iterator.current());
}
| private boolean | attributeValuesMatch(java.util.Set attributes, int runIndex1, int runIndex2)
Iterator iterator = attributes.iterator();
while (iterator.hasNext()) {
Attribute key = (Attribute) iterator.next();
if (!valuesMatch(getAttribute(key, runIndex1), getAttribute(key, runIndex2))) {
return false;
}
}
return true;
| private char | charAt(int index)
return text.charAt(index);
| private final void | createRunAttributeDataVectors()
// use temporary variables so things remain consistent in case of an exception
int newRunStarts[] = new int[ARRAY_SIZE_INCREMENT];
Vector newRunAttributes[] = new Vector[ARRAY_SIZE_INCREMENT];
Vector newRunAttributeValues[] = new Vector[ARRAY_SIZE_INCREMENT];
runStarts = newRunStarts;
runAttributes = newRunAttributes;
runAttributeValues = newRunAttributeValues;
runArraySize = ARRAY_SIZE_INCREMENT;
runCount = 1; // assume initial run starting at index 0
| private final int | ensureRunBreak(int offset)
return ensureRunBreak(offset, true);
| private final int | ensureRunBreak(int offset, boolean copyAttrs)Ensures there is a run break at offset, returning the index of
the run. If this results in splitting a run, two things can happen:
- If copyAttrs is true, the attributes from the existing run
will be placed in both of the newly created runs.
- If copyAttrs is false, the attributes from the existing run
will NOT be copied to the run to the right (>= offset) of the break,
but will exist on the run to the left (< offset).
if (offset == length()) {
return runCount;
}
// search for the run index where this offset should be
int runIndex = 0;
while (runIndex < runCount && runStarts[runIndex] < offset) {
runIndex++;
}
// if the offset is at a run start already, we're done
if (runIndex < runCount && runStarts[runIndex] == offset) {
return runIndex;
}
// we'll have to break up a run
// first, make sure we have enough space in our arrays
if (runCount == runArraySize) {
int newArraySize = runArraySize + ARRAY_SIZE_INCREMENT;
int newRunStarts[] = new int[newArraySize];
Vector newRunAttributes[] = new Vector[newArraySize];
Vector newRunAttributeValues[] = new Vector[newArraySize];
for (int i = 0; i < runArraySize; i++) {
newRunStarts[i] = runStarts[i];
newRunAttributes[i] = runAttributes[i];
newRunAttributeValues[i] = runAttributeValues[i];
}
runStarts = newRunStarts;
runAttributes = newRunAttributes;
runAttributeValues = newRunAttributeValues;
runArraySize = newArraySize;
}
// make copies of the attribute information of the old run that the new one used to be part of
// use temporary variables so things remain consistent in case of an exception
Vector newRunAttributes = null;
Vector newRunAttributeValues = null;
if (copyAttrs) {
Vector oldRunAttributes = runAttributes[runIndex - 1];
Vector oldRunAttributeValues = runAttributeValues[runIndex - 1];
if (oldRunAttributes != null) {
newRunAttributes = (Vector) oldRunAttributes.clone();
}
if (oldRunAttributeValues != null) {
newRunAttributeValues = (Vector) oldRunAttributeValues.clone();
}
}
// now actually break up the run
runCount++;
for (int i = runCount - 1; i > runIndex; i--) {
runStarts[i] = runStarts[i - 1];
runAttributes[i] = runAttributes[i - 1];
runAttributeValues[i] = runAttributeValues[i - 1];
}
runStarts[runIndex] = offset;
runAttributes[runIndex] = newRunAttributes;
runAttributeValues[runIndex] = newRunAttributeValues;
return runIndex;
| private synchronized java.lang.Object | getAttribute(java.text.AttributedCharacterIterator.Attribute attribute, int runIndex)
Vector currentRunAttributes = runAttributes[runIndex];
Vector currentRunAttributeValues = runAttributeValues[runIndex];
if (currentRunAttributes == null) {
return null;
}
int attributeIndex = currentRunAttributes.indexOf(attribute);
if (attributeIndex != -1) {
return currentRunAttributeValues.elementAt(attributeIndex);
}
else {
return null;
}
| private java.lang.Object | getAttributeCheckRange(java.text.AttributedCharacterIterator.Attribute attribute, int runIndex, int beginIndex, int endIndex)
Object value = getAttribute(attribute, runIndex);
if (value instanceof Annotation) {
// need to check whether the annotation's range extends outside the iterator's range
if (beginIndex > 0) {
int currIndex = runIndex;
int runStart = runStarts[currIndex];
while (runStart >= beginIndex &&
valuesMatch(value, getAttribute(attribute, currIndex - 1))) {
currIndex--;
runStart = runStarts[currIndex];
}
if (runStart < beginIndex) {
// annotation's range starts before iterator's range
return null;
}
}
int textLength = length();
if (endIndex < textLength) {
int currIndex = runIndex;
int runLimit = (currIndex < runCount - 1) ? runStarts[currIndex + 1] : textLength;
while (runLimit <= endIndex &&
valuesMatch(value, getAttribute(attribute, currIndex + 1))) {
currIndex++;
runLimit = (currIndex < runCount - 1) ? runStarts[currIndex + 1] : textLength;
}
if (runLimit > endIndex) {
// annotation's range ends after iterator's range
return null;
}
}
// annotation's range is subrange of iterator's range,
// so we can return the value
}
return value;
| public java.text.AttributedCharacterIterator | getIterator()Creates an AttributedCharacterIterator instance that provides access to the entire contents of
this string.
return getIterator(null, 0, length());
| public java.text.AttributedCharacterIterator | getIterator(java.text.AttributedCharacterIterator.Attribute[] attributes)Creates an AttributedCharacterIterator instance that provides access to
selected contents of this string.
Information about attributes not listed in attributes that the
implementor may have need not be made accessible through the iterator.
If the list is null, all available attribute information should be made
accessible.
return getIterator(attributes, 0, length());
| public java.text.AttributedCharacterIterator | getIterator(java.text.AttributedCharacterIterator.Attribute[] attributes, int beginIndex, int endIndex)Creates an AttributedCharacterIterator instance that provides access to
selected contents of this string.
Information about attributes not listed in attributes that the
implementor may have need not be made accessible through the iterator.
If the list is null, all available attribute information should be made
accessible.
return new AttributedStringIterator(attributes, beginIndex, endIndex);
| int | length()
return text.length();
| private static boolean | mapsDiffer(java.util.Map last, java.util.Map attrs)Returns true if the attributes specified in last and attrs differ.
if (last == null) {
return (attrs != null && attrs.size() > 0);
}
return (!last.equals(attrs));
| private void | setAttributes(java.util.Map attrs, int offset)Sets the attributes for the range from offset to the next run break
(typically the end of the text) to the ones specified in attrs.
This is only meant to be called from the constructor!
if (runCount == 0) {
createRunAttributeDataVectors();
}
int index = ensureRunBreak(offset, false);
int size;
if (attrs != null && (size = attrs.size()) > 0) {
Vector runAttrs = new Vector(size);
Vector runValues = new Vector(size);
Iterator iterator = attrs.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry entry = (Map.Entry)iterator.next();
runAttrs.add(entry.getKey());
runValues.add(entry.getValue());
}
runAttributes[index] = runAttrs;
runAttributeValues[index] = runValues;
}
| private static final boolean | valuesMatch(java.lang.Object value1, java.lang.Object value2)
if (value1 == null) {
return value2 == null;
} else {
return value1.equals(value2);
}
|
|