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 */ 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 */ 125 public AttributedString(String text) { 126 if (text == null) { 127 throw new NullPointerException(); 128 } 129 this.text = text; 130 } 131 132 /** 133 * Constructs an AttributedString instance with the given text and attributes. 134 * @param text The text for this attributed string. 135 * @param attributes The attributes that apply to the entire string. 136 * @exception NullPointerException if <code>text</code> or 137 * <code>attributes</code> is null. 138 * @exception IllegalArgumentException if the text has length 0 139 * and the attributes parameter is not an empty Map (attributes 140 * cannot be applied to a 0-length range). 141 */ 142 public AttributedString(String text, 143 Map<? extends Attribute, ?> attributes) 144 { 145 if (text == null || attributes == null) { 146 throw new NullPointerException(); 147 } 148 this.text = text; 149 150 if (text.length() == 0) { 151 if (attributes.isEmpty()) 152 return; 153 throw new IllegalArgumentException("Can't add attribute to 0-length text"); 154 } 155 156 int attributeCount = attributes.size(); 157 if (attributeCount > 0) { 158 createRunAttributeDataVectors(); 159 Vector 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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 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 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 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 */ 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 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 */ 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 */ 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 */ 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. 591 int length() { 592 return text.length(); 593 } 594 595 private char charAt(int index) { 596 return text.charAt(index); 597 } 598 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 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 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 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 */ 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 */ 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 */ 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 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 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 787 public int hashCode() { 788 return text.hashCode() ^ currentIndex ^ beginIndex ^ endIndex; 789 } 790 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 803 public char first() { 804 return internalSetIndex(beginIndex); 805 } 806 807 public char last() { 808 if (endIndex == beginIndex) { 809 return internalSetIndex(endIndex); 810 } else { 811 return internalSetIndex(endIndex - 1); 812 } 813 } 814 815 public char current() { 816 if (currentIndex == endIndex) { 817 return DONE; 818 } else { 819 return charAt(currentIndex); 820 } 821 } 822 823 public char next() { 824 if (currentIndex < endIndex) { 825 return internalSetIndex(currentIndex + 1); 826 } 827 else { 828 return DONE; 829 } 830 } 831 832 public char previous() { 833 if (currentIndex > beginIndex) { 834 return internalSetIndex(currentIndex - 1); 835 } 836 else { 837 return DONE; 838 } 839 } 840 841 public char setIndex(int position) { 842 if (position < beginIndex || position > endIndex) 843 throw new IllegalArgumentException("Invalid index"); 844 return internalSetIndex(position); 845 } 846 847 public int getBeginIndex() { 848 return beginIndex; 849 } 850 851 public int getEndIndex() { 852 return endIndex; 853 } 854 855 public int getIndex() { 856 return currentIndex; 857 } 858 859 // AttributedCharacterIterator methods. See documentation in that interface. 860 861 public int getRunStart() { 862 return currentRunStart; 863 } 864 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 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 902 public int getRunLimit() { 903 return currentRunLimit; 904 } 905 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 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 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 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 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 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 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 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 1049 AttributeMap(int runIndex, int beginIndex, int endIndex) { 1050 this.runIndex = runIndex; 1051 this.beginIndex = beginIndex; 1052 this.endIndex = endIndex; 1053 } 1054 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 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 1087 AttributeEntry(Attribute key, Object value) { 1088 this.key = key; 1089 this.value = value; 1090 } 1091 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 1101 public Object getKey() { 1102 return key; 1103 } 1104 1105 public Object getValue() { 1106 return value; 1107 } 1108 1109 public Object setValue(Object newValue) { 1110 throw new UnsupportedOperationException(); 1111 } 1112 1113 public int hashCode() { 1114 return key.hashCode() ^ (value==null ? 0 : value.hashCode()); 1115 } 1116 1117 public String toString() { 1118 return key.toString()+"="+value.toString(); 1119 } 1120 }