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