home *** CD-ROM | disk | FTP | other *** search
/ Java 1.2 How-To / JavaHowTo.iso / 3rdParty / jbuilder / unsupported / JDK1.2beta3 / SOURCE / SRC.ZIP / java / text / AttributedString.java < prev    next >
Encoding:
Java Source  |  1998-03-20  |  17.0 KB  |  484 lines

  1. /*
  2.  * @(#)AttributedString.java    1.14 98/03/18
  3.  *
  4.  * Copyright 1997, 1998 by Sun Microsystems, Inc.,
  5.  * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
  6.  * All rights reserved.
  7.  *
  8.  * This software is the confidential and proprietary information
  9.  * of Sun Microsystems, Inc. ("Confidential Information").  You
  10.  * shall not disclose such Confidential Information and shall use
  11.  * it only in accordance with the terms of the license agreement
  12.  * you entered into with Sun.
  13.  */
  14.  
  15. package java.text;
  16. import java.text.AttributedCharacterIterator;
  17. import java.util.Vector;
  18.  
  19. import java.util.Vector;
  20. import java.util.Enumeration;
  21.  
  22. /**
  23. * An AttributedString holds text and related attribute information. It
  24. * may be used as the actual data storage in some cases where a text
  25. * reader wants to access attributed text through the AttributedCharacterIterator
  26. * interface.
  27. *
  28. * @see AttributedCharacterIterator
  29. * @see Annotation
  30. */
  31.  
  32. public class AttributedString {
  33.  
  34.     // since there are no vectors of int, we have to use arrays.
  35.     // We allocate them in chunks of 10 elements so we don't have to allocate all the time.
  36.     private static final int ARRAY_SIZE_INCREMENT = 10;
  37.  
  38.     // field holding the text
  39.     String text;
  40.     
  41.     // fields holding run attribute information
  42.     // run attributes are organized by run
  43.     int runArraySize;               // current size of the arrays
  44.     int runCount;                   // actual number of runs, <= runArraySize
  45.     int runStarts[];                // start index for each run
  46.     Vector runAttributeNames[];     // vector of attribute names for each run
  47.     Vector runAttributeValues[];    // parallel vector of attribute values for each run
  48.  
  49.     // fields holding range attribute information
  50.     // range attributes are organized by attribute name
  51.     Vector rangeAttributeNames;
  52.     Vector rangeAttributeVectors;
  53.     
  54.     /**
  55.      * Constructs an AttributedString instance with the given text.
  56.      */
  57.     public AttributedString(String text) {
  58.         if (text == null) {
  59.             throw new NullPointerException();
  60.         }
  61.         this.text = text;
  62.     }
  63.     
  64.     /**
  65.      * Adds an attribute to the string.
  66.      * @param attributeName The name of the attribute
  67.      * @param beginIndex Index of the first character of the range
  68.      * @param endIndex Index of the character following the last character of the range
  69.      * @param value The value of the attribute
  70.      */
  71.     public synchronized void addAttribute(String attributeName,
  72.             int beginIndex, int endIndex, Object value) {
  73.         
  74.         // check whether the input data is valid
  75.         if (attributeName == null) {
  76.             throw new NullPointerException();
  77.         }
  78.         // we don't allow attributes for 0-length runs. This also means we don't allow
  79.         // any run attributes if the entire text has length 0.
  80.         if (beginIndex < 0 || endIndex > length() || beginIndex >= endIndex) {
  81.             throw new IllegalArgumentException("Invalid substring range");
  82.         }
  83.  
  84.         // make sure we have run attribute data vectors
  85.         if (runCount == 0) {
  86.             // use temporary variables so things remain consistent in case of an exception
  87.             int newRunStarts[] = new int[ARRAY_SIZE_INCREMENT];
  88.             Vector newRunAttributeNames[] = new Vector[ARRAY_SIZE_INCREMENT];
  89.             Vector newRunAttributeValues[] = new Vector[ARRAY_SIZE_INCREMENT];
  90.             runStarts = newRunStarts;
  91.             runAttributeNames = newRunAttributeNames;
  92.             runAttributeValues = newRunAttributeValues;
  93.             runArraySize = ARRAY_SIZE_INCREMENT;
  94.             runCount = 1; // assume initial run starting at index 0
  95.         }
  96.         
  97.         // break up runs if necessary
  98.         int beginRunIndex = ensureRunBreak(beginIndex);
  99.         int endRunIndex = ensureRunBreak(endIndex);
  100.         
  101.         // add the attribute to the run data
  102.         for (int i = beginRunIndex; i < endRunIndex; i++) {
  103.             if (runAttributeNames[i] == null) {
  104.                 Vector newRunAttributeNames = new Vector();
  105.                 Vector newRunAttributeValues = new Vector();
  106.                 runAttributeNames[i] = newRunAttributeNames;
  107.                 runAttributeValues[i] = newRunAttributeValues;
  108.             }
  109.             int oldSize = runAttributeNames[i].size();
  110.             runAttributeNames[i].addElement(attributeName);
  111.             try {
  112.                 runAttributeValues[i].addElement(value);
  113.             }
  114.             catch (Exception e) {
  115.                 runAttributeNames[i].setSize(oldSize);
  116.                 runAttributeValues[i].setSize(oldSize);
  117.             }
  118.         }
  119.     }
  120.  
  121.     // ensure there's a run break at offset, return the index of the run
  122.     private final int ensureRunBreak(int offset) {
  123.  
  124.         if (offset == length()) {
  125.             return runCount;
  126.         }
  127.  
  128.         // search for the run index where this offset should be
  129.         int runIndex = 0;
  130.         while (runIndex < runCount && runStarts[runIndex] < offset) {
  131.             runIndex++;
  132.         }
  133.  
  134.         // if the offset is at a run start already, we're done
  135.         if (runIndex < runCount && runStarts[runIndex] == offset) {
  136.             return runIndex;
  137.         }
  138.         
  139.         // we'll have to break up a run
  140.         // first, make sure we have enough space in our arrays
  141.         if (runCount == runArraySize) {
  142.             int newArraySize = runArraySize + ARRAY_SIZE_INCREMENT;
  143.             int newRunStarts[] = new int[newArraySize];
  144.             Vector newRunAttributeNames[] = new Vector[newArraySize];
  145.             Vector newRunAttributeValues[] = new Vector[newArraySize];
  146.             for (int i = 0; i < runArraySize; i++) {
  147.                 newRunStarts[i] = runStarts[i];
  148.                 newRunAttributeNames[i] = runAttributeNames[i];
  149.                 newRunAttributeValues[i] = runAttributeValues[i];
  150.             }
  151.             runStarts = newRunStarts;
  152.             runAttributeNames = newRunAttributeNames;
  153.             runAttributeValues = newRunAttributeValues;
  154.             runArraySize = newArraySize;
  155.         }
  156.         
  157.         // make copies of the attribute information of the old run that the new one used to be part of
  158.         // use temporary variables so things remain consistent in case of an exception
  159.         Vector newRunAttributeNames = null;
  160.         Vector newRunAttributeValues = null;
  161.         Vector oldRunAttributeNames = runAttributeNames[runIndex - 1];
  162.         Vector oldRunAttributeValues = runAttributeValues[runIndex - 1];
  163.         if (oldRunAttributeNames != null) {
  164.             newRunAttributeNames = (Vector) oldRunAttributeNames.clone();
  165.         }
  166.         if (oldRunAttributeValues != null) {
  167.             newRunAttributeValues = (Vector) oldRunAttributeValues.clone();
  168.         }
  169.         
  170.         // now actually break up the run
  171.         runCount++;
  172.         for (int i = runCount - 1; i > runIndex; i--) {
  173.             runStarts[i] = runStarts[i - 1];
  174.             runAttributeNames[i] = runAttributeNames[i - 1];
  175.             runAttributeValues[i] = runAttributeValues[i - 1];
  176.         }
  177.         runStarts[runIndex] = offset;
  178.         runAttributeNames[runIndex] = newRunAttributeNames;
  179.         runAttributeValues[runIndex] = newRunAttributeValues;
  180.  
  181.         return runIndex;
  182.     }
  183.  
  184.     /**
  185.      * Creates an AttributedCharacterIterator instance that provides access to the entire contents of
  186.      * this string.
  187.      *
  188.      * @return an iterator providing access to the text and its attributes
  189.      */
  190.     public AttributedCharacterIterator getIterator() {
  191.         return getIterator(0, length(), null);
  192.     }
  193.  
  194.     /**
  195.      * Creates an AttributedCharacterIterator instance that provides access to selected contents of
  196.      * this string.
  197.      * Information about attributes not listed in attributeNames that the implementor may have
  198.      * need not be made accessible through the iterator.
  199.      *
  200.      * @param attributeNames A list of attributes that the client is interested in
  201.      * @return An iterator providing access to the text and its attributes
  202.      */
  203.     public AttributedCharacterIterator getIterator(String[] attributeNames) {
  204.         return getIterator(0, length(), attributeNames);
  205.     }
  206.  
  207.     /**
  208.      * Creates an AttributedCharacterIterator instance that provides access to selected contents of
  209.      * this string.
  210.      * Information about attributes not listed in attributeNames that the implementor may have
  211.      * need not be made accessible through the iterator. If the list is null, all available
  212.      * attribute information should be made accessible.
  213.      *
  214.      * @param beginIndex the index of the first character
  215.      * @param endIndex the index of the character following the last character
  216.      * @param attributeNames a list of attributes that the client is interested in
  217.      * @return an iterator providing access to the text and its attributes
  218.      */
  219.     public AttributedCharacterIterator getIterator(int beginIndex, int endIndex, String[] attributeNames) {
  220.         return new AttributedStringIterator(beginIndex, endIndex, attributeNames);
  221.     }
  222.  
  223.     // all reading operations are private, since AttributedString instances are
  224.     // accessed through iterators.
  225.  
  226.     int length() {
  227.         return text.length();
  228.     }
  229.     
  230.     char charAt(int index) {
  231.         return text.charAt(index);
  232.     }
  233.  
  234.     // the iterator class associated with this string class
  235.  
  236.     final class AttributedStringIterator implements AttributedCharacterIterator {
  237.         
  238.         // note on synchronization:
  239.         // we don't synchronize on the iterator, assuming that an iterator is only used in one thread.
  240.         // we do synchronize access to the AttributedString however, since it's more likely to be shared between threads.
  241.  
  242.         // start and end index for our iteration
  243.         private int beginIndex;
  244.         private int endIndex;
  245.         
  246.         // attributes that our client is interested in
  247.         private String[] relevantAttributeNames;
  248.  
  249.         // the current index for our iteration
  250.         // invariant: beginIndex <= currentIndex <= endIndex
  251.         private int currentIndex;
  252.  
  253.         // information about the run that includes currentIndex
  254.         private int currentRunIndex;
  255.         private int currentRunStart;
  256.         private int currentRunLimit;
  257.         
  258.         // constructor
  259.         AttributedStringIterator(int beginIndex, int endIndex, String[] attributeNames) {
  260.         
  261.             if (beginIndex < 0 || beginIndex > endIndex || endIndex > length()) {
  262.                 throw new IllegalArgumentException("Invalid substring range");
  263.             }
  264.             
  265.             this.beginIndex = beginIndex;
  266.             this.endIndex = endIndex;
  267.             this.currentIndex = beginIndex;
  268.             updateRunInfo();
  269.             if (attributeNames != null) {
  270.                 relevantAttributeNames = (String[]) attributeNames.clone();
  271.             }
  272.         }
  273.         
  274.         // CharacterIterator methods. See documentation in that interface.
  275.         
  276.         public char first() {
  277.             return internalSetIndex(beginIndex);
  278.         }
  279.         
  280.         public char last() {
  281.             if (endIndex == beginIndex) {
  282.                 return internalSetIndex(endIndex);
  283.             } else {
  284.                 return internalSetIndex(endIndex - 1);
  285.             }
  286.         }
  287.         
  288.         public char current() {
  289.             if (currentIndex == endIndex) {
  290.                 return DONE;
  291.             } else {
  292.                 return charAt(currentIndex);
  293.             }
  294.         }
  295.  
  296.         public char next() {
  297.             if (currentIndex < endIndex) {
  298.                 return internalSetIndex(currentIndex + 1);
  299.             }
  300.             else {
  301.                 return DONE;
  302.             }
  303.         }
  304.  
  305.         public char previous() {
  306.             if (currentIndex > beginIndex) {
  307.                 return internalSetIndex(currentIndex - 1);
  308.             }
  309.             else {
  310.                 return DONE;
  311.             }
  312.         }
  313.  
  314.         public char setIndex(int position) {
  315.             if (position < beginIndex || position > endIndex)
  316.                 throw new IllegalArgumentException("Invalid index");
  317.             return internalSetIndex(position);
  318.         }
  319.  
  320.         public int getBeginIndex() {
  321.             return beginIndex;
  322.         }
  323.  
  324.         public int getEndIndex() {
  325.             return endIndex;
  326.         }
  327.  
  328.         public int getIndex() {
  329.             return currentIndex;
  330.         }
  331.  
  332.         public Object clone() {
  333.             try {
  334.                 AttributedStringIterator other = (AttributedStringIterator) super.clone();
  335.                 return other;
  336.             }
  337.             catch (CloneNotSupportedException e) {
  338.                 throw new InternalError();
  339.             }
  340.         }
  341.  
  342.         // AttributedCharacterIterator methods. See documentation in that interface.
  343.  
  344.         public int getRunStart() {
  345.             return currentRunStart;
  346.         }
  347.  
  348.         public int getRunStart(String attributeName) {
  349.             // ??? need to merge runs
  350.             return currentRunStart;
  351.         }
  352.  
  353.         public int getRunStart(Enumeration attributeNames) {
  354.             // ??? need to merge runs
  355.             return currentRunStart;
  356.         }
  357.  
  358.         public int getRunLimit() {
  359.             return currentRunLimit;
  360.         }
  361.         
  362.         public int getRunLimit(String attributeName) {
  363.             // ??? need to merge runs
  364.             return currentRunLimit;
  365.         }
  366.         
  367.         public int getRunLimit(Enumeration attributeNames) {
  368.             // ??? need to merge runs
  369.             return currentRunLimit;
  370.         }
  371.         
  372.         public AttributeSet getAttributes() {
  373.             // ??? need implementation
  374.             return null;
  375.         }
  376.         
  377.         public Enumeration getAllAttributeNames() {
  378.             throw new NoSuchMethodError("not implemented yet");
  379.         }
  380.         
  381.         public Object getAttribute(String attributeName) {
  382.             int runIndex = currentRunIndex;
  383.             if (runIndex < 0) {
  384.                 return null;
  385.             }
  386.             synchronized (AttributedString.this) {
  387.                 Vector currentRunAttributeNames = runAttributeNames[runIndex];
  388.                 Vector currentRunAttributeValues = runAttributeValues[runIndex];
  389.                 if (currentRunAttributeNames == null) {
  390.                     return null;
  391.                 }
  392.                 int attributeIndex = currentRunAttributeNames.indexOf(attributeName);
  393.                 if (attributeIndex != -1) {
  394.                     return currentRunAttributeValues.elementAt(attributeIndex);
  395.                 }
  396.                 else {
  397.                     return null;
  398.                 }
  399.             }
  400.         }
  401.         
  402.         public Enumeration getAttributeNames() {
  403.             // ??? This should screen out attribute names that aren't relevant to the client
  404.             int runIndex = currentRunIndex;
  405.             if (runIndex < 0) {
  406.                 return null;
  407.             }
  408.             synchronized (AttributedString.this) {
  409.                 Vector currentRunAttributeNames = runAttributeNames[currentRunIndex];
  410.                 if (currentRunAttributeNames == null) {
  411.                     return null;
  412.                 }
  413.                 String[] names = new String[currentRunAttributeNames.size()];
  414.                 currentRunAttributeNames.copyInto(names);
  415.                 return new ArrayEnumeration(names);
  416.             }
  417.         }
  418.         
  419.         // internally used methods
  420.         
  421.         // set the current index, update information about the current run if necessary,
  422.         // return the character at the current index
  423.         private char internalSetIndex(int position) {
  424.             currentIndex = position;
  425.             if (position < currentRunStart || position >= currentRunLimit) {
  426.                 updateRunInfo();
  427.             }
  428.             if (currentIndex == endIndex) {
  429.                 return DONE;
  430.             } else {
  431.                 return charAt(position);
  432.             }
  433.         }
  434.  
  435.         // update the information about the current run
  436.         private void updateRunInfo() {
  437.             if (currentIndex == endIndex) {
  438.                 currentRunStart = currentRunLimit = endIndex;
  439.             } else {
  440.                 synchronized (AttributedString.this) {
  441.                     int runIndex = -1;
  442.                     while (runIndex < runCount - 1 && runStarts[runIndex + 1] <= currentIndex)
  443.                         runIndex++;
  444.                     currentRunIndex = runIndex;
  445.                     if (runIndex >= 0) {
  446.                         currentRunStart = runStarts[runIndex];
  447.                     }
  448.                     else {
  449.                         currentRunStart = beginIndex;
  450.                     }
  451.                     if (runIndex < runCount - 1) {
  452.                         currentRunLimit = runStarts[runIndex + 1];
  453.                     }
  454.                     else {
  455.                         currentRunLimit = endIndex;
  456.                     }
  457.                 }
  458.             }
  459.         }
  460.     }
  461. }
  462.  
  463. // ArrayEnumeration - should find a better place for this
  464. final class ArrayEnumeration implements Enumeration {
  465.  
  466.     private Object[] array;
  467.     private int i = 0;
  468.  
  469.     public ArrayEnumeration(Object[] array) {
  470.         this.array = array;
  471.     }
  472.  
  473.     public boolean hasMoreElements() {
  474.     return i < array.length;
  475.     }
  476.  
  477.     public Object nextElement() {
  478.         if (i < array.length)
  479.             return array[i++];
  480.         else
  481.             throw new java.util.NoSuchElementException();
  482.     }
  483. }
  484.