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