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