0001 /*
0002 * Copyright 1997-2006 Sun Microsystems, Inc. All Rights Reserved.
0003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004 *
0005 * This code is free software; you can redistribute it and/or modify it
0006 * under the terms of the GNU General Public License version 2 only, as
0007 * published by the Free Software Foundation. Sun designates this
0008 * particular file as subject to the "Classpath" exception as provided
0009 * by Sun in the LICENSE file that accompanied this code.
0010 *
0011 * This code is distributed in the hope that it will be useful, but WITHOUT
0012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014 * version 2 for more details (a copy is included in the LICENSE file that
0015 * accompanied this code).
0016 *
0017 * You should have received a copy of the GNU General Public License version
0018 * 2 along with this work; if not, write to the Free Software Foundation,
0019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020 *
0021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022 * CA 95054 USA or visit www.sun.com if you need additional information or
0023 * have any questions.
0024 */
0025
0026 package java.text;
0027
0028 import java.util.*;
0029 import java.text.AttributedCharacterIterator.Attribute;
0030
0031 /**
0032 * An AttributedString holds text and related attribute information. It
0033 * may be used as the actual data storage in some cases where a text
0034 * reader wants to access attributed text through the AttributedCharacterIterator
0035 * interface.
0036 *
0037 * <p>
0038 * An attribute is a key/value pair, identified by the key. No two
0039 * attributes on a given character can have the same key.
0040 *
0041 * <p>The values for an attribute are immutable, or must not be mutated
0042 * by clients or storage. They are always passed by reference, and not
0043 * cloned.
0044 *
0045 * @see AttributedCharacterIterator
0046 * @see Annotation
0047 * @since 1.2
0048 */
0049
0050 public class AttributedString {
0051
0052 // since there are no vectors of int, we have to use arrays.
0053 // We allocate them in chunks of 10 elements so we don't have to allocate all the time.
0054 private static final int ARRAY_SIZE_INCREMENT = 10;
0055
0056 // field holding the text
0057 String text;
0058
0059 // fields holding run attribute information
0060 // run attributes are organized by run
0061 int runArraySize; // current size of the arrays
0062 int runCount; // actual number of runs, <= runArraySize
0063 int runStarts[]; // start index for each run
0064 Vector runAttributes[]; // vector of attribute keys for each run
0065 Vector runAttributeValues[]; // parallel vector of attribute values for each run
0066
0067 /**
0068 * Constructs an AttributedString instance with the given
0069 * AttributedCharacterIterators.
0070 *
0071 * @param iterators AttributedCharacterIterators to construct
0072 * AttributedString from.
0073 * @throws NullPointerException if iterators is null
0074 */
0075 AttributedString(AttributedCharacterIterator[] iterators) {
0076 if (iterators == null) {
0077 throw new NullPointerException("Iterators must not be null");
0078 }
0079 if (iterators.length == 0) {
0080 text = "";
0081 } else {
0082 // Build the String contents
0083 StringBuffer buffer = new StringBuffer();
0084 for (int counter = 0; counter < iterators.length; counter++) {
0085 appendContents(buffer, iterators[counter]);
0086 }
0087
0088 text = buffer.toString();
0089
0090 if (text.length() > 0) {
0091 // Determine the runs, creating a new run when the attributes
0092 // differ.
0093 int offset = 0;
0094 Map last = null;
0095
0096 for (int counter = 0; counter < iterators.length; counter++) {
0097 AttributedCharacterIterator iterator = iterators[counter];
0098 int start = iterator.getBeginIndex();
0099 int end = iterator.getEndIndex();
0100 int index = start;
0101
0102 while (index < end) {
0103 iterator.setIndex(index);
0104
0105 Map attrs = iterator.getAttributes();
0106
0107 if (mapsDiffer(last, attrs)) {
0108 setAttributes(attrs, index - start + offset);
0109 }
0110 last = attrs;
0111 index = iterator.getRunLimit();
0112 }
0113 offset += (end - start);
0114 }
0115 }
0116 }
0117 }
0118
0119 /**
0120 * Constructs an AttributedString instance with the given text.
0121 * @param text The text for this attributed string.
0122 * @exception NullPointerException if <code>text</code> is null.
0123 */
0124 public AttributedString(String text) {
0125 if (text == null) {
0126 throw new NullPointerException();
0127 }
0128 this .text = text;
0129 }
0130
0131 /**
0132 * Constructs an AttributedString instance with the given text and attributes.
0133 * @param text The text for this attributed string.
0134 * @param attributes The attributes that apply to the entire string.
0135 * @exception NullPointerException if <code>text</code> or
0136 * <code>attributes</code> is null.
0137 * @exception IllegalArgumentException if the text has length 0
0138 * and the attributes parameter is not an empty Map (attributes
0139 * cannot be applied to a 0-length range).
0140 */
0141 public AttributedString(String text,
0142 Map<? extends Attribute, ?> attributes) {
0143 if (text == null || attributes == null) {
0144 throw new NullPointerException();
0145 }
0146 this .text = text;
0147
0148 if (text.length() == 0) {
0149 if (attributes.isEmpty())
0150 return;
0151 throw new IllegalArgumentException(
0152 "Can't add attribute to 0-length text");
0153 }
0154
0155 int attributeCount = attributes.size();
0156 if (attributeCount > 0) {
0157 createRunAttributeDataVectors();
0158 Vector newRunAttributes = new Vector(attributeCount);
0159 Vector newRunAttributeValues = new Vector(attributeCount);
0160 runAttributes[0] = newRunAttributes;
0161 runAttributeValues[0] = newRunAttributeValues;
0162 Iterator iterator = attributes.entrySet().iterator();
0163 while (iterator.hasNext()) {
0164 Map.Entry entry = (Map.Entry) iterator.next();
0165 newRunAttributes.addElement(entry.getKey());
0166 newRunAttributeValues.addElement(entry.getValue());
0167 }
0168 }
0169 }
0170
0171 /**
0172 * Constructs an AttributedString instance with the given attributed
0173 * text represented by AttributedCharacterIterator.
0174 * @param text The text for this attributed string.
0175 * @exception NullPointerException if <code>text</code> is null.
0176 */
0177 public AttributedString(AttributedCharacterIterator text) {
0178 // If performance is critical, this constructor should be
0179 // implemented here rather than invoking the constructor for a
0180 // subrange. We can avoid some range checking in the loops.
0181 this (text, text.getBeginIndex(), text.getEndIndex(), null);
0182 }
0183
0184 /**
0185 * Constructs an AttributedString instance with the subrange of
0186 * the given attributed text represented by
0187 * AttributedCharacterIterator. If the given range produces an
0188 * empty text, all attributes will be discarded. Note that any
0189 * attributes wrapped by an Annotation object are discarded for a
0190 * subrange of the original attribute range.
0191 *
0192 * @param text The text for this attributed string.
0193 * @param beginIndex Index of the first character of the range.
0194 * @param endIndex Index of the character following the last character
0195 * of the range.
0196 * @exception NullPointerException if <code>text</code> is null.
0197 * @exception IllegalArgumentException if the subrange given by
0198 * beginIndex and endIndex is out of the text range.
0199 * @see java.text.Annotation
0200 */
0201 public AttributedString(AttributedCharacterIterator text,
0202 int beginIndex, int endIndex) {
0203 this (text, beginIndex, endIndex, null);
0204 }
0205
0206 /**
0207 * Constructs an AttributedString instance with the subrange of
0208 * the given attributed text represented by
0209 * AttributedCharacterIterator. Only attributes that match the
0210 * given attributes will be incorporated into the instance. If the
0211 * given range produces an empty text, all attributes will be
0212 * discarded. Note that any attributes wrapped by an Annotation
0213 * object are discarded for a subrange of the original attribute
0214 * range.
0215 *
0216 * @param text The text for this attributed string.
0217 * @param beginIndex Index of the first character of the range.
0218 * @param endIndex Index of the character following the last character
0219 * of the range.
0220 * @param attributes Specifies attributes to be extracted
0221 * from the text. If null is specified, all available attributes will
0222 * be used.
0223 * @exception NullPointerException if <code>text</code> is null.
0224 * @exception IllegalArgumentException if the subrange given by
0225 * beginIndex and endIndex is out of the text range.
0226 * @see java.text.Annotation
0227 */
0228 public AttributedString(AttributedCharacterIterator text,
0229 int beginIndex, int endIndex, Attribute[] attributes) {
0230 if (text == null) {
0231 throw new NullPointerException();
0232 }
0233
0234 // Validate the given subrange
0235 int textBeginIndex = text.getBeginIndex();
0236 int textEndIndex = text.getEndIndex();
0237 if (beginIndex < textBeginIndex || endIndex > textEndIndex
0238 || beginIndex > endIndex)
0239 throw new IllegalArgumentException(
0240 "Invalid substring range");
0241
0242 // Copy the given string
0243 StringBuffer textBuffer = new StringBuffer();
0244 text.setIndex(beginIndex);
0245 for (char c = text.current(); text.getIndex() < endIndex; c = text
0246 .next())
0247 textBuffer.append(c);
0248 this .text = textBuffer.toString();
0249
0250 if (beginIndex == endIndex)
0251 return;
0252
0253 // Select attribute keys to be taken care of
0254 HashSet keys = new HashSet();
0255 if (attributes == null) {
0256 keys.addAll(text.getAllAttributeKeys());
0257 } else {
0258 for (int i = 0; i < attributes.length; i++)
0259 keys.add(attributes[i]);
0260 keys.retainAll(text.getAllAttributeKeys());
0261 }
0262 if (keys.isEmpty())
0263 return;
0264
0265 // Get and set attribute runs for each attribute name. Need to
0266 // scan from the top of the text so that we can discard any
0267 // Annotation that is no longer applied to a subset text segment.
0268 Iterator itr = keys.iterator();
0269 while (itr.hasNext()) {
0270 Attribute attributeKey = (Attribute) itr.next();
0271 text.setIndex(textBeginIndex);
0272 while (text.getIndex() < endIndex) {
0273 int start = text.getRunStart(attributeKey);
0274 int limit = text.getRunLimit(attributeKey);
0275 Object value = text.getAttribute(attributeKey);
0276
0277 if (value != null) {
0278 if (value instanceof Annotation) {
0279 if (start >= beginIndex && limit <= endIndex) {
0280 addAttribute(attributeKey, value, start
0281 - beginIndex, limit - beginIndex);
0282 } else {
0283 if (limit > endIndex)
0284 break;
0285 }
0286 } else {
0287 // if the run is beyond the given (subset) range, we
0288 // don't need to process further.
0289 if (start >= endIndex)
0290 break;
0291 if (limit > beginIndex) {
0292 // attribute is applied to any subrange
0293 if (start < beginIndex)
0294 start = beginIndex;
0295 if (limit > endIndex)
0296 limit = endIndex;
0297 if (start != limit) {
0298 addAttribute(attributeKey, value, start
0299 - beginIndex, limit
0300 - beginIndex);
0301 }
0302 }
0303 }
0304 }
0305 text.setIndex(limit);
0306 }
0307 }
0308 }
0309
0310 /**
0311 * Adds an attribute to the entire string.
0312 * @param attribute the attribute key
0313 * @param value the value of the attribute; may be null
0314 * @exception NullPointerException if <code>attribute</code> is null.
0315 * @exception IllegalArgumentException if the AttributedString has length 0
0316 * (attributes cannot be applied to a 0-length range).
0317 */
0318 public void addAttribute(Attribute attribute, Object value) {
0319
0320 if (attribute == null) {
0321 throw new NullPointerException();
0322 }
0323
0324 int len = length();
0325 if (len == 0) {
0326 throw new IllegalArgumentException(
0327 "Can't add attribute to 0-length text");
0328 }
0329
0330 addAttributeImpl(attribute, value, 0, len);
0331 }
0332
0333 /**
0334 * Adds an attribute to a subrange of the string.
0335 * @param attribute the attribute key
0336 * @param value The value of the attribute. May be null.
0337 * @param beginIndex Index of the first character of the range.
0338 * @param endIndex Index of the character following the last character of the range.
0339 * @exception NullPointerException if <code>attribute</code> is null.
0340 * @exception IllegalArgumentException if beginIndex is less then 0, endIndex is
0341 * greater than the length of the string, or beginIndex and endIndex together don't
0342 * define a non-empty subrange of the string.
0343 */
0344 public void addAttribute(Attribute attribute, Object value,
0345 int beginIndex, int endIndex) {
0346
0347 if (attribute == null) {
0348 throw new NullPointerException();
0349 }
0350
0351 if (beginIndex < 0 || endIndex > length()
0352 || beginIndex >= endIndex) {
0353 throw new IllegalArgumentException(
0354 "Invalid substring range");
0355 }
0356
0357 addAttributeImpl(attribute, value, beginIndex, endIndex);
0358 }
0359
0360 /**
0361 * Adds a set of attributes to a subrange of the string.
0362 * @param attributes The attributes to be added to the string.
0363 * @param beginIndex Index of the first character of the range.
0364 * @param endIndex Index of the character following the last
0365 * character of the range.
0366 * @exception NullPointerException if <code>attributes</code> is null.
0367 * @exception IllegalArgumentException if beginIndex is less then
0368 * 0, endIndex is greater than the length of the string, or
0369 * beginIndex and endIndex together don't define a non-empty
0370 * subrange of the string and the attributes parameter is not an
0371 * empty Map.
0372 */
0373 public void addAttributes(Map<? extends Attribute, ?> attributes,
0374 int beginIndex, int endIndex) {
0375 if (attributes == null) {
0376 throw new NullPointerException();
0377 }
0378
0379 if (beginIndex < 0 || endIndex > length()
0380 || beginIndex > endIndex) {
0381 throw new IllegalArgumentException(
0382 "Invalid substring range");
0383 }
0384 if (beginIndex == endIndex) {
0385 if (attributes.isEmpty())
0386 return;
0387 throw new IllegalArgumentException(
0388 "Can't add attribute to 0-length text");
0389 }
0390
0391 // make sure we have run attribute data vectors
0392 if (runCount == 0) {
0393 createRunAttributeDataVectors();
0394 }
0395
0396 // break up runs if necessary
0397 int beginRunIndex = ensureRunBreak(beginIndex);
0398 int endRunIndex = ensureRunBreak(endIndex);
0399
0400 Iterator iterator = attributes.entrySet().iterator();
0401 while (iterator.hasNext()) {
0402 Map.Entry entry = (Map.Entry) iterator.next();
0403 addAttributeRunData((Attribute) entry.getKey(), entry
0404 .getValue(), beginRunIndex, endRunIndex);
0405 }
0406 }
0407
0408 private synchronized void addAttributeImpl(Attribute attribute,
0409 Object value, int beginIndex, int endIndex) {
0410
0411 // make sure we have run attribute data vectors
0412 if (runCount == 0) {
0413 createRunAttributeDataVectors();
0414 }
0415
0416 // break up runs if necessary
0417 int beginRunIndex = ensureRunBreak(beginIndex);
0418 int endRunIndex = ensureRunBreak(endIndex);
0419
0420 addAttributeRunData(attribute, value, beginRunIndex,
0421 endRunIndex);
0422 }
0423
0424 private final void createRunAttributeDataVectors() {
0425 // use temporary variables so things remain consistent in case of an exception
0426 int newRunStarts[] = new int[ARRAY_SIZE_INCREMENT];
0427 Vector newRunAttributes[] = new Vector[ARRAY_SIZE_INCREMENT];
0428 Vector newRunAttributeValues[] = new Vector[ARRAY_SIZE_INCREMENT];
0429 runStarts = newRunStarts;
0430 runAttributes = newRunAttributes;
0431 runAttributeValues = newRunAttributeValues;
0432 runArraySize = ARRAY_SIZE_INCREMENT;
0433 runCount = 1; // assume initial run starting at index 0
0434 }
0435
0436 // ensure there's a run break at offset, return the index of the run
0437 private final int ensureRunBreak(int offset) {
0438 return ensureRunBreak(offset, true);
0439 }
0440
0441 /**
0442 * Ensures there is a run break at offset, returning the index of
0443 * the run. If this results in splitting a run, two things can happen:
0444 * <ul>
0445 * <li>If copyAttrs is true, the attributes from the existing run
0446 * will be placed in both of the newly created runs.
0447 * <li>If copyAttrs is false, the attributes from the existing run
0448 * will NOT be copied to the run to the right (>= offset) of the break,
0449 * but will exist on the run to the left (< offset).
0450 * </ul>
0451 */
0452 private final int ensureRunBreak(int offset, boolean copyAttrs) {
0453 if (offset == length()) {
0454 return runCount;
0455 }
0456
0457 // search for the run index where this offset should be
0458 int runIndex = 0;
0459 while (runIndex < runCount && runStarts[runIndex] < offset) {
0460 runIndex++;
0461 }
0462
0463 // if the offset is at a run start already, we're done
0464 if (runIndex < runCount && runStarts[runIndex] == offset) {
0465 return runIndex;
0466 }
0467
0468 // we'll have to break up a run
0469 // first, make sure we have enough space in our arrays
0470 if (runCount == runArraySize) {
0471 int newArraySize = runArraySize + ARRAY_SIZE_INCREMENT;
0472 int newRunStarts[] = new int[newArraySize];
0473 Vector newRunAttributes[] = new Vector[newArraySize];
0474 Vector newRunAttributeValues[] = new Vector[newArraySize];
0475 for (int i = 0; i < runArraySize; i++) {
0476 newRunStarts[i] = runStarts[i];
0477 newRunAttributes[i] = runAttributes[i];
0478 newRunAttributeValues[i] = runAttributeValues[i];
0479 }
0480 runStarts = newRunStarts;
0481 runAttributes = newRunAttributes;
0482 runAttributeValues = newRunAttributeValues;
0483 runArraySize = newArraySize;
0484 }
0485
0486 // make copies of the attribute information of the old run that the new one used to be part of
0487 // use temporary variables so things remain consistent in case of an exception
0488 Vector newRunAttributes = null;
0489 Vector newRunAttributeValues = null;
0490
0491 if (copyAttrs) {
0492 Vector oldRunAttributes = runAttributes[runIndex - 1];
0493 Vector oldRunAttributeValues = runAttributeValues[runIndex - 1];
0494 if (oldRunAttributes != null) {
0495 newRunAttributes = (Vector) oldRunAttributes.clone();
0496 }
0497 if (oldRunAttributeValues != null) {
0498 newRunAttributeValues = (Vector) oldRunAttributeValues
0499 .clone();
0500 }
0501 }
0502
0503 // now actually break up the run
0504 runCount++;
0505 for (int i = runCount - 1; i > runIndex; i--) {
0506 runStarts[i] = runStarts[i - 1];
0507 runAttributes[i] = runAttributes[i - 1];
0508 runAttributeValues[i] = runAttributeValues[i - 1];
0509 }
0510 runStarts[runIndex] = offset;
0511 runAttributes[runIndex] = newRunAttributes;
0512 runAttributeValues[runIndex] = newRunAttributeValues;
0513
0514 return runIndex;
0515 }
0516
0517 // add the attribute attribute/value to all runs where beginRunIndex <= runIndex < endRunIndex
0518 private void addAttributeRunData(Attribute attribute, Object value,
0519 int beginRunIndex, int endRunIndex) {
0520
0521 for (int i = beginRunIndex; i < endRunIndex; i++) {
0522 int keyValueIndex = -1; // index of key and value in our vectors; assume we don't have an entry yet
0523 if (runAttributes[i] == null) {
0524 Vector newRunAttributes = new Vector();
0525 Vector newRunAttributeValues = new Vector();
0526 runAttributes[i] = newRunAttributes;
0527 runAttributeValues[i] = newRunAttributeValues;
0528 } else {
0529 // check whether we have an entry already
0530 keyValueIndex = runAttributes[i].indexOf(attribute);
0531 }
0532
0533 if (keyValueIndex == -1) {
0534 // create new entry
0535 int oldSize = runAttributes[i].size();
0536 runAttributes[i].addElement(attribute);
0537 try {
0538 runAttributeValues[i].addElement(value);
0539 } catch (Exception e) {
0540 runAttributes[i].setSize(oldSize);
0541 runAttributeValues[i].setSize(oldSize);
0542 }
0543 } else {
0544 // update existing entry
0545 runAttributeValues[i].set(keyValueIndex, value);
0546 }
0547 }
0548 }
0549
0550 /**
0551 * Creates an AttributedCharacterIterator instance that provides access to the entire contents of
0552 * this string.
0553 *
0554 * @return An iterator providing access to the text and its attributes.
0555 */
0556 public AttributedCharacterIterator getIterator() {
0557 return getIterator(null, 0, length());
0558 }
0559
0560 /**
0561 * Creates an AttributedCharacterIterator instance that provides access to
0562 * selected contents of this string.
0563 * Information about attributes not listed in attributes that the
0564 * implementor may have need not be made accessible through the iterator.
0565 * If the list is null, all available attribute information should be made
0566 * accessible.
0567 *
0568 * @param attributes a list of attributes that the client is interested in
0569 * @return an iterator providing access to the entire text and its selected attributes
0570 */
0571 public AttributedCharacterIterator getIterator(
0572 Attribute[] attributes) {
0573 return getIterator(attributes, 0, length());
0574 }
0575
0576 /**
0577 * Creates an AttributedCharacterIterator instance that provides access to
0578 * selected contents of this string.
0579 * Information about attributes not listed in attributes that the
0580 * implementor may have need not be made accessible through the iterator.
0581 * If the list is null, all available attribute information should be made
0582 * accessible.
0583 *
0584 * @param attributes a list of attributes that the client is interested in
0585 * @param beginIndex the index of the first character
0586 * @param endIndex the index of the character following the last character
0587 * @return an iterator providing access to the text and its attributes
0588 * @exception IllegalArgumentException if beginIndex is less then 0,
0589 * endIndex is greater than the length of the string, or beginIndex is
0590 * greater than endIndex.
0591 */
0592 public AttributedCharacterIterator getIterator(
0593 Attribute[] attributes, int beginIndex, int endIndex) {
0594 return new AttributedStringIterator(attributes, beginIndex,
0595 endIndex);
0596 }
0597
0598 // all (with the exception of length) reading operations are private,
0599 // since AttributedString instances are accessed through iterators.
0600
0601 // length is package private so that CharacterIteratorFieldDelegate can
0602 // access it without creating an AttributedCharacterIterator.
0603 int length() {
0604 return text.length();
0605 }
0606
0607 private char charAt(int index) {
0608 return text.charAt(index);
0609 }
0610
0611 private synchronized Object getAttribute(Attribute attribute,
0612 int runIndex) {
0613 Vector currentRunAttributes = runAttributes[runIndex];
0614 Vector currentRunAttributeValues = runAttributeValues[runIndex];
0615 if (currentRunAttributes == null) {
0616 return null;
0617 }
0618 int attributeIndex = currentRunAttributes.indexOf(attribute);
0619 if (attributeIndex != -1) {
0620 return currentRunAttributeValues.elementAt(attributeIndex);
0621 } else {
0622 return null;
0623 }
0624 }
0625
0626 // gets an attribute value, but returns an annotation only if it's range does not extend outside the range beginIndex..endIndex
0627 private Object getAttributeCheckRange(Attribute attribute,
0628 int runIndex, int beginIndex, int endIndex) {
0629 Object value = getAttribute(attribute, runIndex);
0630 if (value instanceof Annotation) {
0631 // need to check whether the annotation's range extends outside the iterator's range
0632 if (beginIndex > 0) {
0633 int currIndex = runIndex;
0634 int runStart = runStarts[currIndex];
0635 while (runStart >= beginIndex
0636 && valuesMatch(value, getAttribute(attribute,
0637 currIndex - 1))) {
0638 currIndex--;
0639 runStart = runStarts[currIndex];
0640 }
0641 if (runStart < beginIndex) {
0642 // annotation's range starts before iterator's range
0643 return null;
0644 }
0645 }
0646 int textLength = length();
0647 if (endIndex < textLength) {
0648 int currIndex = runIndex;
0649 int runLimit = (currIndex < runCount - 1) ? runStarts[currIndex + 1]
0650 : textLength;
0651 while (runLimit <= endIndex
0652 && valuesMatch(value, getAttribute(attribute,
0653 currIndex + 1))) {
0654 currIndex++;
0655 runLimit = (currIndex < runCount - 1) ? runStarts[currIndex + 1]
0656 : textLength;
0657 }
0658 if (runLimit > endIndex) {
0659 // annotation's range ends after iterator's range
0660 return null;
0661 }
0662 }
0663 // annotation's range is subrange of iterator's range,
0664 // so we can return the value
0665 }
0666 return value;
0667 }
0668
0669 // returns whether all specified attributes have equal values in the runs with the given indices
0670 private boolean attributeValuesMatch(Set attributes, int runIndex1,
0671 int runIndex2) {
0672 Iterator iterator = attributes.iterator();
0673 while (iterator.hasNext()) {
0674 Attribute key = (Attribute) iterator.next();
0675 if (!valuesMatch(getAttribute(key, runIndex1),
0676 getAttribute(key, runIndex2))) {
0677 return false;
0678 }
0679 }
0680 return true;
0681 }
0682
0683 // returns whether the two objects are either both null or equal
0684 private final static boolean valuesMatch(Object value1,
0685 Object value2) {
0686 if (value1 == null) {
0687 return value2 == null;
0688 } else {
0689 return value1.equals(value2);
0690 }
0691 }
0692
0693 /**
0694 * Appends the contents of the CharacterIterator iterator into the
0695 * StringBuffer buf.
0696 */
0697 private final void appendContents(StringBuffer buf,
0698 CharacterIterator iterator) {
0699 int index = iterator.getBeginIndex();
0700 int end = iterator.getEndIndex();
0701
0702 while (index < end) {
0703 iterator.setIndex(index++);
0704 buf.append(iterator.current());
0705 }
0706 }
0707
0708 /**
0709 * Sets the attributes for the range from offset to the next run break
0710 * (typically the end of the text) to the ones specified in attrs.
0711 * This is only meant to be called from the constructor!
0712 */
0713 private void setAttributes(Map attrs, int offset) {
0714 if (runCount == 0) {
0715 createRunAttributeDataVectors();
0716 }
0717
0718 int index = ensureRunBreak(offset, false);
0719 int size;
0720
0721 if (attrs != null && (size = attrs.size()) > 0) {
0722 Vector runAttrs = new Vector(size);
0723 Vector runValues = new Vector(size);
0724 Iterator iterator = attrs.entrySet().iterator();
0725
0726 while (iterator.hasNext()) {
0727 Map.Entry entry = (Map.Entry) iterator.next();
0728
0729 runAttrs.add(entry.getKey());
0730 runValues.add(entry.getValue());
0731 }
0732 runAttributes[index] = runAttrs;
0733 runAttributeValues[index] = runValues;
0734 }
0735 }
0736
0737 /**
0738 * Returns true if the attributes specified in last and attrs differ.
0739 */
0740 private static boolean mapsDiffer(Map last, Map attrs) {
0741 if (last == null) {
0742 return (attrs != null && attrs.size() > 0);
0743 }
0744 return (!last.equals(attrs));
0745 }
0746
0747 // the iterator class associated with this string class
0748
0749 final private class AttributedStringIterator implements
0750 AttributedCharacterIterator {
0751
0752 // note on synchronization:
0753 // we don't synchronize on the iterator, assuming that an iterator is only used in one thread.
0754 // we do synchronize access to the AttributedString however, since it's more likely to be shared between threads.
0755
0756 // start and end index for our iteration
0757 private int beginIndex;
0758 private int endIndex;
0759
0760 // attributes that our client is interested in
0761 private Attribute[] relevantAttributes;
0762
0763 // the current index for our iteration
0764 // invariant: beginIndex <= currentIndex <= endIndex
0765 private int currentIndex;
0766
0767 // information about the run that includes currentIndex
0768 private int currentRunIndex;
0769 private int currentRunStart;
0770 private int currentRunLimit;
0771
0772 // constructor
0773 AttributedStringIterator(Attribute[] attributes,
0774 int beginIndex, int endIndex) {
0775
0776 if (beginIndex < 0 || beginIndex > endIndex
0777 || endIndex > length()) {
0778 throw new IllegalArgumentException(
0779 "Invalid substring range");
0780 }
0781
0782 this .beginIndex = beginIndex;
0783 this .endIndex = endIndex;
0784 this .currentIndex = beginIndex;
0785 updateRunInfo();
0786 if (attributes != null) {
0787 relevantAttributes = (Attribute[]) attributes.clone();
0788 }
0789 }
0790
0791 // Object methods. See documentation in that class.
0792
0793 public boolean equals(Object obj) {
0794 if (this == obj) {
0795 return true;
0796 }
0797 if (!(obj instanceof AttributedStringIterator)) {
0798 return false;
0799 }
0800
0801 AttributedStringIterator that = (AttributedStringIterator) obj;
0802
0803 if (AttributedString.this != that.getString())
0804 return false;
0805 if (currentIndex != that.currentIndex
0806 || beginIndex != that.beginIndex
0807 || endIndex != that.endIndex)
0808 return false;
0809 return true;
0810 }
0811
0812 public int hashCode() {
0813 return text.hashCode() ^ currentIndex ^ beginIndex
0814 ^ endIndex;
0815 }
0816
0817 public Object clone() {
0818 try {
0819 AttributedStringIterator other = (AttributedStringIterator) super
0820 .clone();
0821 return other;
0822 } catch (CloneNotSupportedException e) {
0823 throw new InternalError();
0824 }
0825 }
0826
0827 // CharacterIterator methods. See documentation in that interface.
0828
0829 public char first() {
0830 return internalSetIndex(beginIndex);
0831 }
0832
0833 public char last() {
0834 if (endIndex == beginIndex) {
0835 return internalSetIndex(endIndex);
0836 } else {
0837 return internalSetIndex(endIndex - 1);
0838 }
0839 }
0840
0841 public char current() {
0842 if (currentIndex == endIndex) {
0843 return DONE;
0844 } else {
0845 return charAt(currentIndex);
0846 }
0847 }
0848
0849 public char next() {
0850 if (currentIndex < endIndex) {
0851 return internalSetIndex(currentIndex + 1);
0852 } else {
0853 return DONE;
0854 }
0855 }
0856
0857 public char previous() {
0858 if (currentIndex > beginIndex) {
0859 return internalSetIndex(currentIndex - 1);
0860 } else {
0861 return DONE;
0862 }
0863 }
0864
0865 public char setIndex(int position) {
0866 if (position < beginIndex || position > endIndex)
0867 throw new IllegalArgumentException("Invalid index");
0868 return internalSetIndex(position);
0869 }
0870
0871 public int getBeginIndex() {
0872 return beginIndex;
0873 }
0874
0875 public int getEndIndex() {
0876 return endIndex;
0877 }
0878
0879 public int getIndex() {
0880 return currentIndex;
0881 }
0882
0883 // AttributedCharacterIterator methods. See documentation in that interface.
0884
0885 public int getRunStart() {
0886 return currentRunStart;
0887 }
0888
0889 public int getRunStart(Attribute attribute) {
0890 if (currentRunStart == beginIndex || currentRunIndex == -1) {
0891 return currentRunStart;
0892 } else {
0893 Object value = getAttribute(attribute);
0894 int runStart = currentRunStart;
0895 int runIndex = currentRunIndex;
0896 while (runStart > beginIndex
0897 && valuesMatch(value, AttributedString.this
0898 .getAttribute(attribute, runIndex - 1))) {
0899 runIndex--;
0900 runStart = runStarts[runIndex];
0901 }
0902 if (runStart < beginIndex) {
0903 runStart = beginIndex;
0904 }
0905 return runStart;
0906 }
0907 }
0908
0909 public int getRunStart(Set<? extends Attribute> attributes) {
0910 if (currentRunStart == beginIndex || currentRunIndex == -1) {
0911 return currentRunStart;
0912 } else {
0913 int runStart = currentRunStart;
0914 int runIndex = currentRunIndex;
0915 while (runStart > beginIndex
0916 && AttributedString.this .attributeValuesMatch(
0917 attributes, currentRunIndex,
0918 runIndex - 1)) {
0919 runIndex--;
0920 runStart = runStarts[runIndex];
0921 }
0922 if (runStart < beginIndex) {
0923 runStart = beginIndex;
0924 }
0925 return runStart;
0926 }
0927 }
0928
0929 public int getRunLimit() {
0930 return currentRunLimit;
0931 }
0932
0933 public int getRunLimit(Attribute attribute) {
0934 if (currentRunLimit == endIndex || currentRunIndex == -1) {
0935 return currentRunLimit;
0936 } else {
0937 Object value = getAttribute(attribute);
0938 int runLimit = currentRunLimit;
0939 int runIndex = currentRunIndex;
0940 while (runLimit < endIndex
0941 && valuesMatch(value, AttributedString.this
0942 .getAttribute(attribute, runIndex + 1))) {
0943 runIndex++;
0944 runLimit = runIndex < runCount - 1 ? runStarts[runIndex + 1]
0945 : endIndex;
0946 }
0947 if (runLimit > endIndex) {
0948 runLimit = endIndex;
0949 }
0950 return runLimit;
0951 }
0952 }
0953
0954 public int getRunLimit(Set<? extends Attribute> attributes) {
0955 if (currentRunLimit == endIndex || currentRunIndex == -1) {
0956 return currentRunLimit;
0957 } else {
0958 int runLimit = currentRunLimit;
0959 int runIndex = currentRunIndex;
0960 while (runLimit < endIndex
0961 && AttributedString.this .attributeValuesMatch(
0962 attributes, currentRunIndex,
0963 runIndex + 1)) {
0964 runIndex++;
0965 runLimit = runIndex < runCount - 1 ? runStarts[runIndex + 1]
0966 : endIndex;
0967 }
0968 if (runLimit > endIndex) {
0969 runLimit = endIndex;
0970 }
0971 return runLimit;
0972 }
0973 }
0974
0975 public Map<Attribute, Object> getAttributes() {
0976 if (runAttributes == null || currentRunIndex == -1
0977 || runAttributes[currentRunIndex] == null) {
0978 // ??? would be nice to return null, but current spec doesn't allow it
0979 // returning Hashtable saves AttributeMap from dealing with emptiness
0980 return new Hashtable();
0981 }
0982 return new AttributeMap(currentRunIndex, beginIndex,
0983 endIndex);
0984 }
0985
0986 public Set<Attribute> getAllAttributeKeys() {
0987 // ??? This should screen out attribute keys that aren't relevant to the client
0988 if (runAttributes == null) {
0989 // ??? would be nice to return null, but current spec doesn't allow it
0990 // returning HashSet saves us from dealing with emptiness
0991 return new HashSet();
0992 }
0993 synchronized (AttributedString.this ) {
0994 // ??? should try to create this only once, then update if necessary,
0995 // and give callers read-only view
0996 Set keys = new HashSet();
0997 int i = 0;
0998 while (i < runCount) {
0999 if (runStarts[i] < endIndex
1000 && (i == runCount - 1 || runStarts[i + 1] > beginIndex)) {
1001 Vector currentRunAttributes = runAttributes[i];
1002 if (currentRunAttributes != null) {
1003 int j = currentRunAttributes.size();
1004 while (j-- > 0) {
1005 keys.add(currentRunAttributes.get(j));
1006 }
1007 }
1008 }
1009 i++;
1010 }
1011 return keys;
1012 }
1013 }
1014
1015 public Object getAttribute(Attribute attribute) {
1016 int runIndex = currentRunIndex;
1017 if (runIndex < 0) {
1018 return null;
1019 }
1020 return AttributedString.this .getAttributeCheckRange(
1021 attribute, runIndex, beginIndex, endIndex);
1022 }
1023
1024 // internally used methods
1025
1026 private AttributedString getString() {
1027 return AttributedString.this ;
1028 }
1029
1030 // set the current index, update information about the current run if necessary,
1031 // return the character at the current index
1032 private char internalSetIndex(int position) {
1033 currentIndex = position;
1034 if (position < currentRunStart
1035 || position >= currentRunLimit) {
1036 updateRunInfo();
1037 }
1038 if (currentIndex == endIndex) {
1039 return DONE;
1040 } else {
1041 return charAt(position);
1042 }
1043 }
1044
1045 // update the information about the current run
1046 private void updateRunInfo() {
1047 if (currentIndex == endIndex) {
1048 currentRunStart = currentRunLimit = endIndex;
1049 currentRunIndex = -1;
1050 } else {
1051 synchronized (AttributedString.this ) {
1052 int runIndex = -1;
1053 while (runIndex < runCount - 1
1054 && runStarts[runIndex + 1] <= currentIndex)
1055 runIndex++;
1056 currentRunIndex = runIndex;
1057 if (runIndex >= 0) {
1058 currentRunStart = runStarts[runIndex];
1059 if (currentRunStart < beginIndex)
1060 currentRunStart = beginIndex;
1061 } else {
1062 currentRunStart = beginIndex;
1063 }
1064 if (runIndex < runCount - 1) {
1065 currentRunLimit = runStarts[runIndex + 1];
1066 if (currentRunLimit > endIndex)
1067 currentRunLimit = endIndex;
1068 } else {
1069 currentRunLimit = endIndex;
1070 }
1071 }
1072 }
1073 }
1074
1075 }
1076
1077 // the map class associated with this string class, giving access to the attributes of one run
1078
1079 final private class AttributeMap extends
1080 AbstractMap<Attribute, Object> {
1081
1082 int runIndex;
1083 int beginIndex;
1084 int endIndex;
1085
1086 AttributeMap(int runIndex, int beginIndex, int endIndex) {
1087 this .runIndex = runIndex;
1088 this .beginIndex = beginIndex;
1089 this .endIndex = endIndex;
1090 }
1091
1092 public Set entrySet() {
1093 HashSet set = new HashSet();
1094 synchronized (AttributedString.this ) {
1095 int size = runAttributes[runIndex].size();
1096 for (int i = 0; i < size; i++) {
1097 Attribute key = (Attribute) runAttributes[runIndex]
1098 .get(i);
1099 Object value = runAttributeValues[runIndex].get(i);
1100 if (value instanceof Annotation) {
1101 value = AttributedString.this
1102 .getAttributeCheckRange(key, runIndex,
1103 beginIndex, endIndex);
1104 if (value == null) {
1105 continue;
1106 }
1107 }
1108 Map.Entry entry = new AttributeEntry(key, value);
1109 set.add(entry);
1110 }
1111 }
1112 return set;
1113 }
1114
1115 public Object get(Object key) {
1116 return AttributedString.this .getAttributeCheckRange(
1117 (Attribute) key, runIndex, beginIndex, endIndex);
1118 }
1119 }
1120 }
1121
1122 class AttributeEntry implements Map.Entry {
1123
1124 private Attribute key;
1125 private Object value;
1126
1127 AttributeEntry(Attribute key, Object value) {
1128 this .key = key;
1129 this .value = value;
1130 }
1131
1132 public boolean equals(Object o) {
1133 if (!(o instanceof AttributeEntry)) {
1134 return false;
1135 }
1136 AttributeEntry other = (AttributeEntry) o;
1137 return other.key.equals(key)
1138 && (value == null ? other.value == null : other.value
1139 .equals(value));
1140 }
1141
1142 public Object getKey() {
1143 return key;
1144 }
1145
1146 public Object getValue() {
1147 return value;
1148 }
1149
1150 public Object setValue(Object newValue) {
1151 throw new UnsupportedOperationException();
1152 }
1153
1154 public int hashCode() {
1155 return key.hashCode() ^ (value == null ? 0 : value.hashCode());
1156 }
1157
1158 public String toString() {
1159 return key.toString() + "=" + value.toString();
1160 }
1161 }
|