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