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