1. /*
  2. * @(#)AttributedString.java 1.36 04/07/16
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package java.text;
  8. import java.util.*;
  9. import java.text.AttributedCharacterIterator.Attribute;
  10. /**
  11. * An AttributedString holds text and related attribute information. It
  12. * may be used as the actual data storage in some cases where a text
  13. * reader wants to access attributed text through the AttributedCharacterIterator
  14. * interface.
  15. *
  16. * @see AttributedCharacterIterator
  17. * @see Annotation
  18. * @since 1.2
  19. */
  20. public class AttributedString {
  21. // since there are no vectors of int, we have to use arrays.
  22. // We allocate them in chunks of 10 elements so we don't have to allocate all the time.
  23. private static final int ARRAY_SIZE_INCREMENT = 10;
  24. // field holding the text
  25. String text;
  26. // fields holding run attribute information
  27. // run attributes are organized by run
  28. int runArraySize; // current size of the arrays
  29. int runCount; // actual number of runs, <= runArraySize
  30. int runStarts[]; // start index for each run
  31. Vector runAttributes[]; // vector of attribute keys for each run
  32. Vector runAttributeValues[]; // parallel vector of attribute values for each run
  33. /**
  34. * Constructs an AttributedString instance with the given
  35. * AttributedCharacterIterators.
  36. *
  37. * @param iterators AttributedCharacterIterators to construct
  38. * AttributedString from.
  39. * @throws NullPointerException if iterators is null
  40. */
  41. AttributedString(AttributedCharacterIterator[] iterators) {
  42. if (iterators == null) {
  43. throw new NullPointerException("Iterators must not be null");
  44. }
  45. if (iterators.length == 0) {
  46. text = "";
  47. }
  48. else {
  49. // Build the String contents
  50. StringBuffer buffer = new StringBuffer();
  51. for (int counter = 0; counter < iterators.length; counter++) {
  52. appendContents(buffer, iterators[counter]);
  53. }
  54. text = buffer.toString();
  55. if (text.length() > 0) {
  56. // Determine the runs, creating a new run when the attributes
  57. // differ.
  58. int offset = 0;
  59. Map last = null;
  60. for (int counter = 0; counter < iterators.length; counter++) {
  61. AttributedCharacterIterator iterator = iterators[counter];
  62. int start = iterator.getBeginIndex();
  63. int end = iterator.getEndIndex();
  64. int index = start;
  65. while (index < end) {
  66. iterator.setIndex(index);
  67. Map attrs = iterator.getAttributes();
  68. if (mapsDiffer(last, attrs)) {
  69. setAttributes(attrs, index - start + offset);
  70. }
  71. last = attrs;
  72. index = iterator.getRunLimit();
  73. }
  74. offset += (end - start);
  75. }
  76. }
  77. }
  78. }
  79. /**
  80. * Constructs an AttributedString instance with the given text.
  81. * @param text The text for this attributed string.
  82. */
  83. public AttributedString(String text) {
  84. if (text == null) {
  85. throw new NullPointerException();
  86. }
  87. this.text = text;
  88. }
  89. /**
  90. * Constructs an AttributedString instance with the given text and attributes.
  91. * @param text The text for this attributed string.
  92. * @param attributes The attributes that apply to the entire string.
  93. * @exception IllegalArgumentException if the text has length 0
  94. * and the attributes parameter is not an empty Map (attributes
  95. * cannot be applied to a 0-length range).
  96. */
  97. public AttributedString(String text,
  98. Map<? extends Attribute, ?> attributes)
  99. {
  100. if (text == null || attributes == null) {
  101. throw new NullPointerException();
  102. }
  103. this.text = text;
  104. if (text.length() == 0) {
  105. if (attributes.isEmpty())
  106. return;
  107. throw new IllegalArgumentException("Can't add attribute to 0-length text");
  108. }
  109. int attributeCount = attributes.size();
  110. if (attributeCount > 0) {
  111. createRunAttributeDataVectors();
  112. Vector newRunAttributes = new Vector(attributeCount);
  113. Vector newRunAttributeValues = new Vector(attributeCount);
  114. runAttributes[0] = newRunAttributes;
  115. runAttributeValues[0] = newRunAttributeValues;
  116. Iterator iterator = attributes.entrySet().iterator();
  117. while (iterator.hasNext()) {
  118. Map.Entry entry = (Map.Entry) iterator.next();
  119. newRunAttributes.addElement(entry.getKey());
  120. newRunAttributeValues.addElement(entry.getValue());
  121. }
  122. }
  123. }
  124. /**
  125. * Constructs an AttributedString instance with the given attributed
  126. * text represented by AttributedCharacterIterator.
  127. * @param text The text for this attributed string.
  128. */
  129. public AttributedString(AttributedCharacterIterator text) {
  130. // If performance is critical, this constructor should be
  131. // implemented here rather than invoking the constructor for a
  132. // subrange. We can avoid some range checking in the loops.
  133. this(text, text.getBeginIndex(), text.getEndIndex(), null);
  134. }
  135. /**
  136. * Constructs an AttributedString instance with the subrange of
  137. * the given attributed text represented by
  138. * AttributedCharacterIterator. If the given range produces an
  139. * empty text, all attributes will be discarded. Note that any
  140. * attributes wrapped by an Annotation object are discarded for a
  141. * subrange of the original attribute range.
  142. *
  143. * @param text The text for this attributed string.
  144. * @param beginIndex Index of the first character of the range.
  145. * @param endIndex Index of the character following the last character
  146. * of the range.
  147. * @exception IllegalArgumentException if the subrange given by
  148. * beginIndex and endIndex is out of the text range.
  149. * @see java.text.Annotation
  150. */
  151. public AttributedString(AttributedCharacterIterator text,
  152. int beginIndex,
  153. int endIndex) {
  154. this(text, beginIndex, endIndex, null);
  155. }
  156. /**
  157. * Constructs an AttributedString instance with the subrange of
  158. * the given attributed text represented by
  159. * AttributedCharacterIterator. Only attributes that match the
  160. * given attributes will be incorporated into the instance. If the
  161. * given range produces an empty text, all attributes will be
  162. * discarded. Note that any attributes wrapped by an Annotation
  163. * object are discarded for a subrange of the original attribute
  164. * range.
  165. *
  166. * @param text The text for this attributed string.
  167. * @param beginIndex Index of the first character of the range.
  168. * @param endIndex Index of the character following the last character
  169. * of the range.
  170. * @param attributes Specifies attributes to be extracted
  171. * from the text. If null is specified, all available attributes will
  172. * be used.
  173. * @exception IllegalArgumentException if the subrange given by
  174. * beginIndex and endIndex is out of the text range.
  175. * @see java.text.Annotation
  176. */
  177. public AttributedString(AttributedCharacterIterator text,
  178. int beginIndex,
  179. int endIndex,
  180. Attribute[] attributes) {
  181. if (text == null) {
  182. throw new NullPointerException();
  183. }
  184. // Validate the given subrange
  185. int textBeginIndex = text.getBeginIndex();
  186. int textEndIndex = text.getEndIndex();
  187. if (beginIndex < textBeginIndex || endIndex > textEndIndex || beginIndex > endIndex)
  188. throw new IllegalArgumentException("Invalid substring range");
  189. // Copy the given string
  190. StringBuffer textBuffer = new StringBuffer();
  191. text.setIndex(beginIndex);
  192. for (char c = text.current(); text.getIndex() < endIndex; c = text.next())
  193. textBuffer.append(c);
  194. this.text = textBuffer.toString();
  195. if (beginIndex == endIndex)
  196. return;
  197. // Select attribute keys to be taken care of
  198. HashSet keys = new HashSet();
  199. if (attributes == null) {
  200. keys.addAll(text.getAllAttributeKeys());
  201. } else {
  202. for (int i = 0; i < attributes.length; i++)
  203. keys.add(attributes[i]);
  204. keys.retainAll(text.getAllAttributeKeys());
  205. }
  206. if (keys.isEmpty())
  207. return;
  208. // Get and set attribute runs for each attribute name. Need to
  209. // scan from the top of the text so that we can discard any
  210. // Annotation that is no longer applied to a subset text segment.
  211. Iterator itr = keys.iterator();
  212. while (itr.hasNext()) {
  213. Attribute attributeKey = (Attribute)itr.next();
  214. text.setIndex(textBeginIndex);
  215. while (text.getIndex() < endIndex) {
  216. int start = text.getRunStart(attributeKey);
  217. int limit = text.getRunLimit(attributeKey);
  218. Object value = text.getAttribute(attributeKey);
  219. if (value != null) {
  220. if (value instanceof Annotation) {
  221. if (start >= beginIndex && limit <= endIndex) {
  222. addAttribute(attributeKey, value, start - beginIndex, limit - beginIndex);
  223. } else {
  224. if (limit > endIndex)
  225. break;
  226. }
  227. } else {
  228. // if the run is beyond the given (subset) range, we
  229. // don't need to process further.
  230. if (start >= endIndex)
  231. break;
  232. if (limit > beginIndex) {
  233. // attribute is applied to any subrange
  234. if (start < beginIndex)
  235. start = beginIndex;
  236. if (limit > endIndex)
  237. limit = endIndex;
  238. if (start != limit) {
  239. addAttribute(attributeKey, value, start - beginIndex, limit - beginIndex);
  240. }
  241. }
  242. }
  243. }
  244. text.setIndex(limit);
  245. }
  246. }
  247. }
  248. /**
  249. * Adds an attribute to the entire string.
  250. * @param attribute the attribute key
  251. * @param value the value of the attribute; may be null
  252. * @exception IllegalArgumentException if the AttributedString has length 0
  253. * (attributes cannot be applied to a 0-length range).
  254. */
  255. public void addAttribute(Attribute attribute, Object value) {
  256. if (attribute == null) {
  257. throw new NullPointerException();
  258. }
  259. int len = length();
  260. if (len == 0) {
  261. throw new IllegalArgumentException("Can't add attribute to 0-length text");
  262. }
  263. addAttributeImpl(attribute, value, 0, len);
  264. }
  265. /**
  266. * Adds an attribute to a subrange of the string.
  267. * @param attribute the attribute key
  268. * @param value The value of the attribute. May be null.
  269. * @param beginIndex Index of the first character of the range.
  270. * @param endIndex Index of the character following the last character of the range.
  271. * @exception IllegalArgumentException if beginIndex is less then 0, endIndex is
  272. * greater than the length of the string, or beginIndex and endIndex together don't
  273. * define a non-empty subrange of the string.
  274. */
  275. public void addAttribute(Attribute attribute, Object value,
  276. int beginIndex, int endIndex) {
  277. if (attribute == null) {
  278. throw new NullPointerException();
  279. }
  280. if (beginIndex < 0 || endIndex > length() || beginIndex >= endIndex) {
  281. throw new IllegalArgumentException("Invalid substring range");
  282. }
  283. addAttributeImpl(attribute, value, beginIndex, endIndex);
  284. }
  285. /**
  286. * Adds a set of attributes to a subrange of the string.
  287. * @param attributes The attributes to be added to the string.
  288. * @param beginIndex Index of the first character of the range.
  289. * @param endIndex Index of the character following the last
  290. * character of the range.
  291. * @exception IllegalArgumentException if beginIndex is less then
  292. * 0, endIndex is greater than the length of the string, or
  293. * beginIndex and endIndex together don't define a non-empty
  294. * subrange of the string and the attributes parameter is not an
  295. * empty Map.
  296. */
  297. public void addAttributes(Map<? extends Attribute, ?> attributes,
  298. int beginIndex, int endIndex)
  299. {
  300. if (attributes == null) {
  301. throw new NullPointerException();
  302. }
  303. if (beginIndex < 0 || endIndex > length() || beginIndex > endIndex) {
  304. throw new IllegalArgumentException("Invalid substring range");
  305. }
  306. if (beginIndex == endIndex) {
  307. if (attributes.isEmpty())
  308. return;
  309. throw new IllegalArgumentException("Can't add attribute to 0-length text");
  310. }
  311. // make sure we have run attribute data vectors
  312. if (runCount == 0) {
  313. createRunAttributeDataVectors();
  314. }
  315. // break up runs if necessary
  316. int beginRunIndex = ensureRunBreak(beginIndex);
  317. int endRunIndex = ensureRunBreak(endIndex);
  318. Iterator iterator = attributes.entrySet().iterator();
  319. while (iterator.hasNext()) {
  320. Map.Entry entry = (Map.Entry) iterator.next();
  321. addAttributeRunData((Attribute) entry.getKey(), entry.getValue(), beginRunIndex, endRunIndex);
  322. }
  323. }
  324. private synchronized void addAttributeImpl(Attribute attribute, Object value,
  325. int beginIndex, int endIndex) {
  326. // make sure we have run attribute data vectors
  327. if (runCount == 0) {
  328. createRunAttributeDataVectors();
  329. }
  330. // break up runs if necessary
  331. int beginRunIndex = ensureRunBreak(beginIndex);
  332. int endRunIndex = ensureRunBreak(endIndex);
  333. addAttributeRunData(attribute, value, beginRunIndex, endRunIndex);
  334. }
  335. private final void createRunAttributeDataVectors() {
  336. // use temporary variables so things remain consistent in case of an exception
  337. int newRunStarts[] = new int[ARRAY_SIZE_INCREMENT];
  338. Vector newRunAttributes[] = new Vector[ARRAY_SIZE_INCREMENT];
  339. Vector newRunAttributeValues[] = new Vector[ARRAY_SIZE_INCREMENT];
  340. runStarts = newRunStarts;
  341. runAttributes = newRunAttributes;
  342. runAttributeValues = newRunAttributeValues;
  343. runArraySize = ARRAY_SIZE_INCREMENT;
  344. runCount = 1; // assume initial run starting at index 0
  345. }
  346. // ensure there's a run break at offset, return the index of the run
  347. private final int ensureRunBreak(int offset) {
  348. return ensureRunBreak(offset, true);
  349. }
  350. /**
  351. * Ensures there is a run break at offset, returning the index of
  352. * the run. If this results in splitting a run, two things can happen:
  353. * <ul>
  354. * <li>If copyAttrs is true, the attributes from the existing run
  355. * will be placed in both of the newly created runs.
  356. * <li>If copyAttrs is false, the attributes from the existing run
  357. * will NOT be copied to the run to the right (>= offset) of the break,
  358. * but will exist on the run to the left (< offset).
  359. * </ul>
  360. */
  361. private final int ensureRunBreak(int offset, boolean copyAttrs) {
  362. if (offset == length()) {
  363. return runCount;
  364. }
  365. // search for the run index where this offset should be
  366. int runIndex = 0;
  367. while (runIndex < runCount && runStarts[runIndex] < offset) {
  368. runIndex++;
  369. }
  370. // if the offset is at a run start already, we're done
  371. if (runIndex < runCount && runStarts[runIndex] == offset) {
  372. return runIndex;
  373. }
  374. // we'll have to break up a run
  375. // first, make sure we have enough space in our arrays
  376. if (runCount == runArraySize) {
  377. int newArraySize = runArraySize + ARRAY_SIZE_INCREMENT;
  378. int newRunStarts[] = new int[newArraySize];
  379. Vector newRunAttributes[] = new Vector[newArraySize];
  380. Vector newRunAttributeValues[] = new Vector[newArraySize];
  381. for (int i = 0; i < runArraySize; i++) {
  382. newRunStarts[i] = runStarts[i];
  383. newRunAttributes[i] = runAttributes[i];
  384. newRunAttributeValues[i] = runAttributeValues[i];
  385. }
  386. runStarts = newRunStarts;
  387. runAttributes = newRunAttributes;
  388. runAttributeValues = newRunAttributeValues;
  389. runArraySize = newArraySize;
  390. }
  391. // make copies of the attribute information of the old run that the new one used to be part of
  392. // use temporary variables so things remain consistent in case of an exception
  393. Vector newRunAttributes = null;
  394. Vector newRunAttributeValues = null;
  395. if (copyAttrs) {
  396. Vector oldRunAttributes = runAttributes[runIndex - 1];
  397. Vector oldRunAttributeValues = runAttributeValues[runIndex - 1];
  398. if (oldRunAttributes != null) {
  399. newRunAttributes = (Vector) oldRunAttributes.clone();
  400. }
  401. if (oldRunAttributeValues != null) {
  402. newRunAttributeValues = (Vector) oldRunAttributeValues.clone();
  403. }
  404. }
  405. // now actually break up the run
  406. runCount++;
  407. for (int i = runCount - 1; i > runIndex; i--) {
  408. runStarts[i] = runStarts[i - 1];
  409. runAttributes[i] = runAttributes[i - 1];
  410. runAttributeValues[i] = runAttributeValues[i - 1];
  411. }
  412. runStarts[runIndex] = offset;
  413. runAttributes[runIndex] = newRunAttributes;
  414. runAttributeValues[runIndex] = newRunAttributeValues;
  415. return runIndex;
  416. }
  417. // add the attribute attribute/value to all runs where beginRunIndex <= runIndex < endRunIndex
  418. private void addAttributeRunData(Attribute attribute, Object value,
  419. int beginRunIndex, int endRunIndex) {
  420. for (int i = beginRunIndex; i < endRunIndex; i++) {
  421. int keyValueIndex = -1; // index of key and value in our vectors; assume we don't have an entry yet
  422. if (runAttributes[i] == null) {
  423. Vector newRunAttributes = new Vector();
  424. Vector newRunAttributeValues = new Vector();
  425. runAttributes[i] = newRunAttributes;
  426. runAttributeValues[i] = newRunAttributeValues;
  427. } else {
  428. // check whether we have an entry already
  429. keyValueIndex = runAttributes[i].indexOf(attribute);
  430. }
  431. if (keyValueIndex == -1) {
  432. // create new entry
  433. int oldSize = runAttributes[i].size();
  434. runAttributes[i].addElement(attribute);
  435. try {
  436. runAttributeValues[i].addElement(value);
  437. }
  438. catch (Exception e) {
  439. runAttributes[i].setSize(oldSize);
  440. runAttributeValues[i].setSize(oldSize);
  441. }
  442. } else {
  443. // update existing entry
  444. runAttributeValues[i].set(keyValueIndex, value);
  445. }
  446. }
  447. }
  448. /**
  449. * Creates an AttributedCharacterIterator instance that provides access to the entire contents of
  450. * this string.
  451. *
  452. * @return An iterator providing access to the text and its attributes.
  453. */
  454. public AttributedCharacterIterator getIterator() {
  455. return getIterator(null, 0, length());
  456. }
  457. /**
  458. * Creates an AttributedCharacterIterator instance that provides access to
  459. * selected contents of this string.
  460. * Information about attributes not listed in attributes that the
  461. * implementor may have need not be made accessible through the iterator.
  462. * If the list is null, all available attribute information should be made
  463. * accessible.
  464. *
  465. * @param attributes a list of attributes that the client is interested in
  466. * @return an iterator providing access to the text and its attributes
  467. */
  468. public AttributedCharacterIterator getIterator(Attribute[] attributes) {
  469. return getIterator(attributes, 0, length());
  470. }
  471. /**
  472. * Creates an AttributedCharacterIterator instance that provides access to
  473. * selected contents of this string.
  474. * Information about attributes not listed in attributes that the
  475. * implementor may have need not be made accessible through the iterator.
  476. * If the list is null, all available attribute information should be made
  477. * accessible.
  478. *
  479. * @param attributes a list of attributes that the client is interested in
  480. * @param beginIndex the index of the first character
  481. * @param endIndex the index of the character following the last character
  482. * @return an iterator providing access to the text and its attributes
  483. * @exception IllegalArgumentException if beginIndex is less then 0,
  484. * endIndex is greater than the length of the string, or beginIndex is
  485. * greater than endIndex.
  486. */
  487. public AttributedCharacterIterator getIterator(Attribute[] attributes, int beginIndex, int endIndex) {
  488. return new AttributedStringIterator(attributes, beginIndex, endIndex);
  489. }
  490. // all (with the exception of length) reading operations are private,
  491. // since AttributedString instances are accessed through iterators.
  492. // length is package private so that CharacterIteratorFieldDelegate can
  493. // access it without creating an AttributedCharacterIterator.
  494. int length() {
  495. return text.length();
  496. }
  497. private char charAt(int index) {
  498. return text.charAt(index);
  499. }
  500. private synchronized Object getAttribute(Attribute attribute, int runIndex) {
  501. Vector currentRunAttributes = runAttributes[runIndex];
  502. Vector currentRunAttributeValues = runAttributeValues[runIndex];
  503. if (currentRunAttributes == null) {
  504. return null;
  505. }
  506. int attributeIndex = currentRunAttributes.indexOf(attribute);
  507. if (attributeIndex != -1) {
  508. return currentRunAttributeValues.elementAt(attributeIndex);
  509. }
  510. else {
  511. return null;
  512. }
  513. }
  514. // gets an attribute value, but returns an annotation only if it's range does not extend outside the range beginIndex..endIndex
  515. private Object getAttributeCheckRange(Attribute attribute, int runIndex, int beginIndex, int endIndex) {
  516. Object value = getAttribute(attribute, runIndex);
  517. if (value instanceof Annotation) {
  518. // need to check whether the annotation's range extends outside the iterator's range
  519. if (beginIndex > 0) {
  520. int currIndex = runIndex;
  521. int runStart = runStarts[currIndex];
  522. while (runStart >= beginIndex &&
  523. valuesMatch(value, getAttribute(attribute, currIndex - 1))) {
  524. currIndex--;
  525. runStart = runStarts[currIndex];
  526. }
  527. if (runStart < beginIndex) {
  528. // annotation's range starts before iterator's range
  529. return null;
  530. }
  531. }
  532. int textLength = length();
  533. if (endIndex < textLength) {
  534. int currIndex = runIndex;
  535. int runLimit = (currIndex < runCount - 1) ? runStarts[currIndex + 1] : textLength;
  536. while (runLimit <= endIndex &&
  537. valuesMatch(value, getAttribute(attribute, currIndex + 1))) {
  538. currIndex++;
  539. runLimit = (currIndex < runCount - 1) ? runStarts[currIndex + 1] : textLength;
  540. }
  541. if (runLimit > endIndex) {
  542. // annotation's range ends after iterator's range
  543. return null;
  544. }
  545. }
  546. // annotation's range is subrange of iterator's range,
  547. // so we can return the value
  548. }
  549. return value;
  550. }
  551. // returns whether all specified attributes have equal values in the runs with the given indices
  552. private boolean attributeValuesMatch(Set attributes, int runIndex1, int runIndex2) {
  553. Iterator iterator = attributes.iterator();
  554. while (iterator.hasNext()) {
  555. Attribute key = (Attribute) iterator.next();
  556. if (!valuesMatch(getAttribute(key, runIndex1), getAttribute(key, runIndex2))) {
  557. return false;
  558. }
  559. }
  560. return true;
  561. }
  562. // returns whether the two objects are either both null or equal
  563. private final static boolean valuesMatch(Object value1, Object value2) {
  564. if (value1 == null) {
  565. return value2 == null;
  566. } else {
  567. return value1.equals(value2);
  568. }
  569. }
  570. /**
  571. * Appends the contents of the CharacterIterator iterator into the
  572. * StringBuffer buf.
  573. */
  574. private final void appendContents(StringBuffer buf,
  575. CharacterIterator iterator) {
  576. int index = iterator.getBeginIndex();
  577. int end = iterator.getEndIndex();
  578. while (index < end) {
  579. iterator.setIndex(index++);
  580. buf.append(iterator.current());
  581. }
  582. }
  583. /**
  584. * Sets the attributes for the range from offset to the the next run break
  585. * (typically the end of the text) to the ones specified in attrs.
  586. * This is only meant to be called from the constructor!
  587. */
  588. private void setAttributes(Map attrs, int offset) {
  589. if (runCount == 0) {
  590. createRunAttributeDataVectors();
  591. }
  592. int index = ensureRunBreak(offset, false);
  593. int size;
  594. if (attrs != null && (size = attrs.size()) > 0) {
  595. Vector runAttrs = new Vector(size);
  596. Vector runValues = new Vector(size);
  597. Iterator iterator = attrs.entrySet().iterator();
  598. while (iterator.hasNext()) {
  599. Map.Entry entry = (Map.Entry)iterator.next();
  600. runAttrs.add(entry.getKey());
  601. runValues.add(entry.getValue());
  602. }
  603. runAttributes[index] = runAttrs;
  604. runAttributeValues[index] = runValues;
  605. }
  606. }
  607. /**
  608. * Returns true if the attributes specified in last and attrs differ.
  609. */
  610. private static boolean mapsDiffer(Map last, Map attrs) {
  611. if (last == null) {
  612. return (attrs != null && attrs.size() > 0);
  613. }
  614. return (!last.equals(attrs));
  615. }
  616. // the iterator class associated with this string class
  617. final private class AttributedStringIterator implements AttributedCharacterIterator {
  618. // note on synchronization:
  619. // we don't synchronize on the iterator, assuming that an iterator is only used in one thread.
  620. // we do synchronize access to the AttributedString however, since it's more likely to be shared between threads.
  621. // start and end index for our iteration
  622. private int beginIndex;
  623. private int endIndex;
  624. // attributes that our client is interested in
  625. private Attribute[] relevantAttributes;
  626. // the current index for our iteration
  627. // invariant: beginIndex <= currentIndex <= endIndex
  628. private int currentIndex;
  629. // information about the run that includes currentIndex
  630. private int currentRunIndex;
  631. private int currentRunStart;
  632. private int currentRunLimit;
  633. // constructor
  634. AttributedStringIterator(Attribute[] attributes, int beginIndex, int endIndex) {
  635. if (beginIndex < 0 || beginIndex > endIndex || endIndex > length()) {
  636. throw new IllegalArgumentException("Invalid substring range");
  637. }
  638. this.beginIndex = beginIndex;
  639. this.endIndex = endIndex;
  640. this.currentIndex = beginIndex;
  641. updateRunInfo();
  642. if (attributes != null) {
  643. relevantAttributes = (Attribute[]) attributes.clone();
  644. }
  645. }
  646. // Object methods. See documentation in that class.
  647. public boolean equals(Object obj) {
  648. if (this == obj) {
  649. return true;
  650. }
  651. if (!(obj instanceof AttributedStringIterator)) {
  652. return false;
  653. }
  654. AttributedStringIterator that = (AttributedStringIterator) obj;
  655. if (AttributedString.this != that.getString())
  656. return false;
  657. if (currentIndex != that.currentIndex || beginIndex != that.beginIndex || endIndex != that.endIndex)
  658. return false;
  659. return true;
  660. }
  661. public int hashCode() {
  662. return text.hashCode() ^ currentIndex ^ beginIndex ^ endIndex;
  663. }
  664. public Object clone() {
  665. try {
  666. AttributedStringIterator other = (AttributedStringIterator) super.clone();
  667. return other;
  668. }
  669. catch (CloneNotSupportedException e) {
  670. throw new InternalError();
  671. }
  672. }
  673. // CharacterIterator methods. See documentation in that interface.
  674. public char first() {
  675. return internalSetIndex(beginIndex);
  676. }
  677. public char last() {
  678. if (endIndex == beginIndex) {
  679. return internalSetIndex(endIndex);
  680. } else {
  681. return internalSetIndex(endIndex - 1);
  682. }
  683. }
  684. public char current() {
  685. if (currentIndex == endIndex) {
  686. return DONE;
  687. } else {
  688. return charAt(currentIndex);
  689. }
  690. }
  691. public char next() {
  692. if (currentIndex < endIndex) {
  693. return internalSetIndex(currentIndex + 1);
  694. }
  695. else {
  696. return DONE;
  697. }
  698. }
  699. public char previous() {
  700. if (currentIndex > beginIndex) {
  701. return internalSetIndex(currentIndex - 1);
  702. }
  703. else {
  704. return DONE;
  705. }
  706. }
  707. public char setIndex(int position) {
  708. if (position < beginIndex || position > endIndex)
  709. throw new IllegalArgumentException("Invalid index");
  710. return internalSetIndex(position);
  711. }
  712. public int getBeginIndex() {
  713. return beginIndex;
  714. }
  715. public int getEndIndex() {
  716. return endIndex;
  717. }
  718. public int getIndex() {
  719. return currentIndex;
  720. }
  721. // AttributedCharacterIterator methods. See documentation in that interface.
  722. public int getRunStart() {
  723. return currentRunStart;
  724. }
  725. public int getRunStart(Attribute attribute) {
  726. if (currentRunStart == beginIndex || currentRunIndex == -1) {
  727. return currentRunStart;
  728. } else {
  729. Object value = getAttribute(attribute);
  730. int runStart = currentRunStart;
  731. int runIndex = currentRunIndex;
  732. while (runStart > beginIndex &&
  733. valuesMatch(value, AttributedString.this.getAttribute(attribute, runIndex - 1))) {
  734. runIndex--;
  735. runStart = runStarts[runIndex];
  736. }
  737. if (runStart < beginIndex) {
  738. runStart = beginIndex;
  739. }
  740. return runStart;
  741. }
  742. }
  743. public int getRunStart(Set<? extends Attribute> attributes) {
  744. if (currentRunStart == beginIndex || currentRunIndex == -1) {
  745. return currentRunStart;
  746. } else {
  747. int runStart = currentRunStart;
  748. int runIndex = currentRunIndex;
  749. while (runStart > beginIndex &&
  750. AttributedString.this.attributeValuesMatch(attributes, currentRunIndex, runIndex - 1)) {
  751. runIndex--;
  752. runStart = runStarts[runIndex];
  753. }
  754. if (runStart < beginIndex) {
  755. runStart = beginIndex;
  756. }
  757. return runStart;
  758. }
  759. }
  760. public int getRunLimit() {
  761. return currentRunLimit;
  762. }
  763. public int getRunLimit(Attribute attribute) {
  764. if (currentRunLimit == endIndex || currentRunIndex == -1) {
  765. return currentRunLimit;
  766. } else {
  767. Object value = getAttribute(attribute);
  768. int runLimit = currentRunLimit;
  769. int runIndex = currentRunIndex;
  770. while (runLimit < endIndex &&
  771. valuesMatch(value, AttributedString.this.getAttribute(attribute, runIndex + 1))) {
  772. runIndex++;
  773. runLimit = runIndex < runCount - 1 ? runStarts[runIndex + 1] : endIndex;
  774. }
  775. if (runLimit > endIndex) {
  776. runLimit = endIndex;
  777. }
  778. return runLimit;
  779. }
  780. }
  781. public int getRunLimit(Set<? extends Attribute> attributes) {
  782. if (currentRunLimit == endIndex || currentRunIndex == -1) {
  783. return currentRunLimit;
  784. } else {
  785. int runLimit = currentRunLimit;
  786. int runIndex = currentRunIndex;
  787. while (runLimit < endIndex &&
  788. AttributedString.this.attributeValuesMatch(attributes, currentRunIndex, runIndex + 1)) {
  789. runIndex++;
  790. runLimit = runIndex < runCount - 1 ? runStarts[runIndex + 1] : endIndex;
  791. }
  792. if (runLimit > endIndex) {
  793. runLimit = endIndex;
  794. }
  795. return runLimit;
  796. }
  797. }
  798. public Map<Attribute,Object> getAttributes() {
  799. if (runAttributes == null || currentRunIndex == -1 || runAttributes[currentRunIndex] == null) {
  800. // ??? would be nice to return null, but current spec doesn't allow it
  801. // returning Hashtable saves AttributeMap from dealing with emptiness
  802. return new Hashtable();
  803. }
  804. return new AttributeMap(currentRunIndex, beginIndex, endIndex);
  805. }
  806. public Set<Attribute> getAllAttributeKeys() {
  807. // ??? This should screen out attribute keys that aren't relevant to the client
  808. if (runAttributes == null) {
  809. // ??? would be nice to return null, but current spec doesn't allow it
  810. // returning HashSet saves us from dealing with emptiness
  811. return new HashSet();
  812. }
  813. synchronized (AttributedString.this) {
  814. // ??? should try to create this only once, then update if necessary,
  815. // and give callers read-only view
  816. Set keys = new HashSet();
  817. int i = 0;
  818. while (i < runCount) {
  819. if (runStarts[i] < endIndex && (i == runCount - 1 || runStarts[i + 1] > beginIndex)) {
  820. Vector currentRunAttributes = runAttributes[i];
  821. if (currentRunAttributes != null) {
  822. int j = currentRunAttributes.size();
  823. while (j-- > 0) {
  824. keys.add(currentRunAttributes.get(j));
  825. }
  826. }
  827. }
  828. i++;
  829. }
  830. return keys;
  831. }
  832. }
  833. public Object getAttribute(Attribute attribute) {
  834. int runIndex = currentRunIndex;
  835. if (runIndex < 0) {
  836. return null;
  837. }
  838. return AttributedString.this.getAttributeCheckRange(attribute, runIndex, beginIndex, endIndex);
  839. }
  840. // internally used methods
  841. private AttributedString getString() {
  842. return AttributedString.this;
  843. }
  844. // set the current index, update information about the current run if necessary,
  845. // return the character at the current index
  846. private char internalSetIndex(int position) {
  847. currentIndex = position;
  848. if (position < currentRunStart || position >= currentRunLimit) {
  849. updateRunInfo();
  850. }
  851. if (currentIndex == endIndex) {
  852. return DONE;
  853. } else {
  854. return charAt(position);
  855. }
  856. }
  857. // update the information about the current run
  858. private void updateRunInfo() {
  859. if (currentIndex == endIndex) {
  860. currentRunStart = currentRunLimit = endIndex;
  861. currentRunIndex = -1;
  862. } else {
  863. synchronized (AttributedString.this) {
  864. int runIndex = -1;
  865. while (runIndex < runCount - 1 && runStarts[runIndex + 1] <= currentIndex)
  866. runIndex++;
  867. currentRunIndex = runIndex;
  868. if (runIndex >= 0) {
  869. currentRunStart = runStarts[runIndex];
  870. if (currentRunStart < beginIndex)
  871. currentRunStart = beginIndex;
  872. }
  873. else {
  874. currentRunStart = beginIndex;
  875. }
  876. if (runIndex < runCount - 1) {
  877. currentRunLimit = runStarts[runIndex + 1];
  878. if (currentRunLimit > endIndex)
  879. currentRunLimit = endIndex;
  880. }
  881. else {
  882. currentRunLimit = endIndex;
  883. }
  884. }
  885. }
  886. }
  887. }
  888. // the map class associated with this string class, giving access to the attributes of one run
  889. final private class AttributeMap extends AbstractMap<Attribute,Object> {
  890. int runIndex;
  891. int beginIndex;
  892. int endIndex;
  893. AttributeMap(int runIndex, int beginIndex, int endIndex) {
  894. this.runIndex = runIndex;
  895. this.beginIndex = beginIndex;
  896. this.endIndex = endIndex;
  897. }
  898. public Set entrySet() {
  899. HashSet set = new HashSet();
  900. synchronized (AttributedString.this) {
  901. int size = runAttributes[runIndex].size();
  902. for (int i = 0; i < size; i++) {
  903. Attribute key = (Attribute) runAttributes[runIndex].get(i);
  904. Object value = runAttributeValues[runIndex].get(i);
  905. if (value instanceof Annotation) {
  906. value = AttributedString.this.getAttributeCheckRange(key,
  907. runIndex, beginIndex, endIndex);
  908. if (value == null) {
  909. continue;
  910. }
  911. }
  912. Map.Entry entry = new AttributeEntry(key, value);
  913. set.add(entry);
  914. }
  915. }
  916. return set;
  917. }
  918. public Object get(Object key) {
  919. return AttributedString.this.getAttributeCheckRange((Attribute) key, runIndex, beginIndex, endIndex);
  920. }
  921. }
  922. }
  923. class AttributeEntry implements Map.Entry {
  924. private Attribute key;
  925. private Object value;
  926. AttributeEntry(Attribute key, Object value) {
  927. this.key = key;
  928. this.value = value;
  929. }
  930. public boolean equals(Object o) {
  931. if (!(o instanceof AttributeEntry)) {
  932. return false;
  933. }
  934. AttributeEntry other = (AttributeEntry) o;
  935. return other.key.equals(key) &&
  936. (value == null ? other.value == null : other.value.equals(value));
  937. }
  938. public Object getKey() {
  939. return key;
  940. }
  941. public Object getValue() {
  942. return value;
  943. }
  944. public Object setValue(Object newValue) {
  945. throw new UnsupportedOperationException();
  946. }
  947. public int hashCode() {
  948. return key.hashCode() ^ (value==null ? 0 : value.hashCode());
  949. }
  950. public String toString() {
  951. return key.toString()+"="+value.toString();
  952. }
  953. }