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