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