Home » openjdk-7 » java » text » [javadoc | source]

    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   }

Home » openjdk-7 » java » text » [javadoc | source]