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