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 */ AttributedString(AttributedCharacterIterator[] iterators)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 */ AttributedString(String text)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 */ AttributedString(String text, Map<? extends Attribute, ?> attributes)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 */ AttributedString(AttributedCharacterIterator text)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 */ AttributedString(AttributedCharacterIterator text, int beginIndex, int endIndex)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 */ AttributedString(AttributedCharacterIterator text, int beginIndex, int endIndex, Attribute[] attributes)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 */ addAttribute(Attribute attribute, Object value)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 */ addAttribute(Attribute attribute, Object value, int beginIndex, int endIndex)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 */ addAttributes(Map<? extends Attribute, ?> attributes, int beginIndex, int endIndex)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 addAttributeImpl(Attribute attribute, Object value, int beginIndex, int endIndex)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 createRunAttributeDataVectors()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 ensureRunBreak(int offset)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 */ ensureRunBreak(int offset, boolean copyAttrs)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 addAttributeRunData(Attribute attribute, Object value, int beginRunIndex, int 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 */ getIterator()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 */ getIterator(Attribute[] attributes)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 */ getIterator(Attribute[] attributes, int beginIndex, int endIndex)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. length()603 int length() { 604 return text.length(); 605 } 606 charAt(int index)607 private char charAt(int index) { 608 return text.charAt(index); 609 } 610 getAttribute(Attribute attribute, int runIndex)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 getAttributeCheckRange(Attribute attribute, int runIndex, int beginIndex, int 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 attributeValuesMatch(Set<? extends Attribute> attributes, int runIndex1, int runIndex2)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 valuesMatch(Object value1, Object value2)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 */ appendContents(StringBuffer buf, CharacterIterator iterator)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 */ setAttributes(Map<Attribute, Object> attrs, int offset)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 */ mapsDiffer(Map<K, V> last, Map<K, V> attrs)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 AttributedStringIterator(Attribute[] attributes, int beginIndex, int endIndex)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 equals(Object obj)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 hashCode()799 public int hashCode() { 800 return text.hashCode() ^ currentIndex ^ beginIndex ^ endIndex; 801 } 802 clone()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 first()815 public char first() { 816 return internalSetIndex(beginIndex); 817 } 818 last()819 public char last() { 820 if (endIndex == beginIndex) { 821 return internalSetIndex(endIndex); 822 } else { 823 return internalSetIndex(endIndex - 1); 824 } 825 } 826 current()827 public char current() { 828 if (currentIndex == endIndex) { 829 return DONE; 830 } else { 831 return charAt(currentIndex); 832 } 833 } 834 next()835 public char next() { 836 if (currentIndex < endIndex) { 837 return internalSetIndex(currentIndex + 1); 838 } 839 else { 840 return DONE; 841 } 842 } 843 previous()844 public char previous() { 845 if (currentIndex > beginIndex) { 846 return internalSetIndex(currentIndex - 1); 847 } 848 else { 849 return DONE; 850 } 851 } 852 setIndex(int position)853 public char setIndex(int position) { 854 if (position < beginIndex || position > endIndex) 855 throw new IllegalArgumentException("Invalid index"); 856 return internalSetIndex(position); 857 } 858 getBeginIndex()859 public int getBeginIndex() { 860 return beginIndex; 861 } 862 getEndIndex()863 public int getEndIndex() { 864 return endIndex; 865 } 866 getIndex()867 public int getIndex() { 868 return currentIndex; 869 } 870 871 // AttributedCharacterIterator methods. See documentation in that interface. 872 getRunStart()873 public int getRunStart() { 874 return currentRunStart; 875 } 876 getRunStart(Attribute attribute)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 getRunStart(Set<? extends Attribute> attributes)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 getRunLimit()914 public int getRunLimit() { 915 return currentRunLimit; 916 } 917 getRunLimit(Attribute attribute)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 getRunLimit(Set<? extends Attribute> attributes)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 getAttributes()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 getAllAttributeKeys()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 getAttribute(Attribute attribute)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 getString()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 internalSetIndex(int position)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 updateRunInfo()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 AttributeMap(int runIndex, int beginIndex, int endIndex)1061 AttributeMap(int runIndex, int beginIndex, int endIndex) { 1062 this.runIndex = runIndex; 1063 this.beginIndex = beginIndex; 1064 this.endIndex = endIndex; 1065 } 1066 entrySet()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 get(Object key)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 AttributeEntry(Attribute key, Object value)1100 AttributeEntry(Attribute key, Object value) { 1101 this.key = key; 1102 this.value = value; 1103 } 1104 equals(Object o)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 getKey()1114 public Attribute getKey() { 1115 return key; 1116 } 1117 getValue()1118 public Object getValue() { 1119 return value; 1120 } 1121 setValue(Object newValue)1122 public Object setValue(Object newValue) { 1123 throw new UnsupportedOperationException(); 1124 } 1125 hashCode()1126 public int hashCode() { 1127 return key.hashCode() ^ (value==null ? 0 : value.hashCode()); 1128 } 1129 toString()1130 public String toString() { 1131 return key.toString()+"="+value.toString(); 1132 } 1133 } 1134