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 // 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 */ AttributedString(AttributedCharacterIterator[] iterators)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.isEmpty()) { 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 */ AttributedString(String text)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 */ AttributedString(String text, Map<? extends Attribute, ?> attributes)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.isEmpty()) { 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 */ AttributedString(AttributedCharacterIterator text)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 */ AttributedString(AttributedCharacterIterator text, int beginIndex, int endIndex)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 */ AttributedString(AttributedCharacterIterator text, int beginIndex, int endIndex, Attribute[] attributes)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 */ addAttribute(Attribute attribute, Object value)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 */ addAttribute(Attribute attribute, Object value, int beginIndex, int endIndex)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 */ addAttributes(Map<? extends Attribute, ?> attributes, int beginIndex, int endIndex)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 addAttributeImpl(Attribute attribute, Object value, int beginIndex, int endIndex)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 createRunAttributeDataVectors()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 ensureRunBreak(int offset)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 */ ensureRunBreak(int offset, boolean copyAttrs)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 addAttributeRunData(Attribute attribute, Object value, int beginRunIndex, int 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 */ getIterator()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 */ getIterator(Attribute[] attributes)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 */ getIterator(Attribute[] attributes, int beginIndex, int endIndex)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. length()596 int length() { 597 return text.length(); 598 } 599 charAt(int index)600 private char charAt(int index) { 601 return text.charAt(index); 602 } 603 getAttribute(Attribute attribute, int runIndex)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 getAttributeCheckRange(Attribute attribute, int runIndex, int beginIndex, int 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 attributeValuesMatch(Set<? extends Attribute> attributes, int runIndex1, int runIndex2)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 valuesMatch(Object value1, Object value2)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 */ appendContents(StringBuffer buf, CharacterIterator iterator)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 */ setAttributes(Map<Attribute, Object> attrs, int offset)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 */ mapsDiffer(Map<K, V> last, Map<K, V> attrs)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 AttributedStringIterator(Attribute[] attributes, int beginIndex, int endIndex)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 equals(Object obj)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 hashCode()792 public int hashCode() { 793 return text.hashCode() ^ currentIndex ^ beginIndex ^ endIndex; 794 } 795 clone()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 first()808 public char first() { 809 return internalSetIndex(beginIndex); 810 } 811 last()812 public char last() { 813 if (endIndex == beginIndex) { 814 return internalSetIndex(endIndex); 815 } else { 816 return internalSetIndex(endIndex - 1); 817 } 818 } 819 current()820 public char current() { 821 if (currentIndex == endIndex) { 822 return DONE; 823 } else { 824 return charAt(currentIndex); 825 } 826 } 827 next()828 public char next() { 829 if (currentIndex < endIndex) { 830 return internalSetIndex(currentIndex + 1); 831 } 832 else { 833 return DONE; 834 } 835 } 836 previous()837 public char previous() { 838 if (currentIndex > beginIndex) { 839 return internalSetIndex(currentIndex - 1); 840 } 841 else { 842 return DONE; 843 } 844 } 845 setIndex(int position)846 public char setIndex(int position) { 847 if (position < beginIndex || position > endIndex) 848 throw new IllegalArgumentException("Invalid index"); 849 return internalSetIndex(position); 850 } 851 getBeginIndex()852 public int getBeginIndex() { 853 return beginIndex; 854 } 855 getEndIndex()856 public int getEndIndex() { 857 return endIndex; 858 } 859 getIndex()860 public int getIndex() { 861 return currentIndex; 862 } 863 864 // AttributedCharacterIterator methods. See documentation in that interface. 865 getRunStart()866 public int getRunStart() { 867 return currentRunStart; 868 } 869 getRunStart(Attribute attribute)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 getRunStart(Set<? extends Attribute> attributes)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 getRunLimit()907 public int getRunLimit() { 908 return currentRunLimit; 909 } 910 getRunLimit(Attribute attribute)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 getRunLimit(Set<? extends Attribute> attributes)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 getAttributes()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 getAllAttributeKeys()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 getAttribute(Attribute attribute)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 getString()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 internalSetIndex(int position)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 updateRunInfo()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 AttributeMap(int runIndex, int beginIndex, int endIndex)1054 AttributeMap(int runIndex, int beginIndex, int endIndex) { 1055 this.runIndex = runIndex; 1056 this.beginIndex = beginIndex; 1057 this.endIndex = endIndex; 1058 } 1059 entrySet()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 get(Object key)1082 public Object get(Object key) { 1083 return AttributedString.this.getAttributeCheckRange((Attribute) key, runIndex, beginIndex, endIndex); 1084 } 1085 } 1086 } 1087 1088 class AttributeEntry implements Map.Entry<Attribute,Object> { 1089 1090 private Attribute key; 1091 private Object value; 1092 AttributeEntry(Attribute key, Object value)1093 AttributeEntry(Attribute key, Object value) { 1094 this.key = key; 1095 this.value = value; 1096 } 1097 equals(Object o)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 getKey()1107 public Attribute getKey() { 1108 return key; 1109 } 1110 getValue()1111 public Object getValue() { 1112 return value; 1113 } 1114 setValue(Object newValue)1115 public Object setValue(Object newValue) { 1116 throw new UnsupportedOperationException(); 1117 } 1118 hashCode()1119 public int hashCode() { 1120 return key.hashCode() ^ (value==null ? 0 : value.hashCode()); 1121 } 1122 toString()1123 public String toString() { 1124 return key.toString()+"="+value.toString(); 1125 } 1126 } 1127