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