home *** CD-ROM | disk | FTP | other *** search
Java Source | 1998-03-20 | 17.0 KB | 484 lines |
- /*
- * @(#)AttributedString.java 1.14 98/03/18
- *
- * Copyright 1997, 1998 by Sun Microsystems, Inc.,
- * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
- * All rights reserved.
- *
- * This software is the confidential and proprietary information
- * of Sun Microsystems, Inc. ("Confidential Information"). You
- * shall not disclose such Confidential Information and shall use
- * it only in accordance with the terms of the license agreement
- * you entered into with Sun.
- */
-
- package java.text;
- import java.text.AttributedCharacterIterator;
- import java.util.Vector;
-
- import java.util.Vector;
- import java.util.Enumeration;
-
- /**
- * An AttributedString holds text and related attribute information. It
- * may be used as the actual data storage in some cases where a text
- * reader wants to access attributed text through the AttributedCharacterIterator
- * interface.
- *
- * @see AttributedCharacterIterator
- * @see Annotation
- */
-
- public class AttributedString {
-
- // since there are no vectors of int, we have to use arrays.
- // We allocate them in chunks of 10 elements so we don't have to allocate all the time.
- private static final int ARRAY_SIZE_INCREMENT = 10;
-
- // field holding the text
- String text;
-
- // fields holding run attribute information
- // run attributes are organized by run
- int runArraySize; // current size of the arrays
- int runCount; // actual number of runs, <= runArraySize
- int runStarts[]; // start index for each run
- Vector runAttributeNames[]; // vector of attribute names for each run
- Vector runAttributeValues[]; // parallel vector of attribute values for each run
-
- // fields holding range attribute information
- // range attributes are organized by attribute name
- Vector rangeAttributeNames;
- Vector rangeAttributeVectors;
-
- /**
- * Constructs an AttributedString instance with the given text.
- */
- public AttributedString(String text) {
- if (text == null) {
- throw new NullPointerException();
- }
- this.text = text;
- }
-
- /**
- * Adds an attribute to the string.
- * @param attributeName The name of the attribute
- * @param beginIndex Index of the first character of the range
- * @param endIndex Index of the character following the last character of the range
- * @param value The value of the attribute
- */
- public synchronized void addAttribute(String attributeName,
- int beginIndex, int endIndex, Object value) {
-
- // check whether the input data is valid
- if (attributeName == null) {
- throw new NullPointerException();
- }
- // we don't allow attributes for 0-length runs. This also means we don't allow
- // any run attributes if the entire text has length 0.
- if (beginIndex < 0 || endIndex > length() || beginIndex >= endIndex) {
- throw new IllegalArgumentException("Invalid substring range");
- }
-
- // make sure we have run attribute data vectors
- if (runCount == 0) {
- // use temporary variables so things remain consistent in case of an exception
- int newRunStarts[] = new int[ARRAY_SIZE_INCREMENT];
- Vector newRunAttributeNames[] = new Vector[ARRAY_SIZE_INCREMENT];
- Vector newRunAttributeValues[] = new Vector[ARRAY_SIZE_INCREMENT];
- runStarts = newRunStarts;
- runAttributeNames = newRunAttributeNames;
- runAttributeValues = newRunAttributeValues;
- runArraySize = ARRAY_SIZE_INCREMENT;
- runCount = 1; // assume initial run starting at index 0
- }
-
- // break up runs if necessary
- int beginRunIndex = ensureRunBreak(beginIndex);
- int endRunIndex = ensureRunBreak(endIndex);
-
- // add the attribute to the run data
- for (int i = beginRunIndex; i < endRunIndex; i++) {
- if (runAttributeNames[i] == null) {
- Vector newRunAttributeNames = new Vector();
- Vector newRunAttributeValues = new Vector();
- runAttributeNames[i] = newRunAttributeNames;
- runAttributeValues[i] = newRunAttributeValues;
- }
- int oldSize = runAttributeNames[i].size();
- runAttributeNames[i].addElement(attributeName);
- try {
- runAttributeValues[i].addElement(value);
- }
- catch (Exception e) {
- runAttributeNames[i].setSize(oldSize);
- runAttributeValues[i].setSize(oldSize);
- }
- }
- }
-
- // ensure there's a run break at offset, return the index of the run
- private final int ensureRunBreak(int offset) {
-
- if (offset == length()) {
- return runCount;
- }
-
- // search for the run index where this offset should be
- int runIndex = 0;
- while (runIndex < runCount && runStarts[runIndex] < offset) {
- runIndex++;
- }
-
- // if the offset is at a run start already, we're done
- if (runIndex < runCount && runStarts[runIndex] == offset) {
- return runIndex;
- }
-
- // we'll have to break up a run
- // first, make sure we have enough space in our arrays
- if (runCount == runArraySize) {
- int newArraySize = runArraySize + ARRAY_SIZE_INCREMENT;
- int newRunStarts[] = new int[newArraySize];
- Vector newRunAttributeNames[] = new Vector[newArraySize];
- Vector newRunAttributeValues[] = new Vector[newArraySize];
- for (int i = 0; i < runArraySize; i++) {
- newRunStarts[i] = runStarts[i];
- newRunAttributeNames[i] = runAttributeNames[i];
- newRunAttributeValues[i] = runAttributeValues[i];
- }
- runStarts = newRunStarts;
- runAttributeNames = newRunAttributeNames;
- runAttributeValues = newRunAttributeValues;
- runArraySize = newArraySize;
- }
-
- // make copies of the attribute information of the old run that the new one used to be part of
- // use temporary variables so things remain consistent in case of an exception
- Vector newRunAttributeNames = null;
- Vector newRunAttributeValues = null;
- Vector oldRunAttributeNames = runAttributeNames[runIndex - 1];
- Vector oldRunAttributeValues = runAttributeValues[runIndex - 1];
- if (oldRunAttributeNames != null) {
- newRunAttributeNames = (Vector) oldRunAttributeNames.clone();
- }
- if (oldRunAttributeValues != null) {
- newRunAttributeValues = (Vector) oldRunAttributeValues.clone();
- }
-
- // now actually break up the run
- runCount++;
- for (int i = runCount - 1; i > runIndex; i--) {
- runStarts[i] = runStarts[i - 1];
- runAttributeNames[i] = runAttributeNames[i - 1];
- runAttributeValues[i] = runAttributeValues[i - 1];
- }
- runStarts[runIndex] = offset;
- runAttributeNames[runIndex] = newRunAttributeNames;
- runAttributeValues[runIndex] = newRunAttributeValues;
-
- return runIndex;
- }
-
- /**
- * Creates an AttributedCharacterIterator instance that provides access to the entire contents of
- * this string.
- *
- * @return an iterator providing access to the text and its attributes
- */
- public AttributedCharacterIterator getIterator() {
- return getIterator(0, length(), null);
- }
-
- /**
- * Creates an AttributedCharacterIterator instance that provides access to selected contents of
- * this string.
- * Information about attributes not listed in attributeNames that the implementor may have
- * need not be made accessible through the iterator.
- *
- * @param attributeNames A list of attributes that the client is interested in
- * @return An iterator providing access to the text and its attributes
- */
- public AttributedCharacterIterator getIterator(String[] attributeNames) {
- return getIterator(0, length(), attributeNames);
- }
-
- /**
- * Creates an AttributedCharacterIterator instance that provides access to selected contents of
- * this string.
- * Information about attributes not listed in attributeNames that the implementor may have
- * need not be made accessible through the iterator. If the list is null, all available
- * attribute information should be made accessible.
- *
- * @param beginIndex the index of the first character
- * @param endIndex the index of the character following the last character
- * @param attributeNames a list of attributes that the client is interested in
- * @return an iterator providing access to the text and its attributes
- */
- public AttributedCharacterIterator getIterator(int beginIndex, int endIndex, String[] attributeNames) {
- return new AttributedStringIterator(beginIndex, endIndex, attributeNames);
- }
-
- // all reading operations are private, since AttributedString instances are
- // accessed through iterators.
-
- int length() {
- return text.length();
- }
-
- char charAt(int index) {
- return text.charAt(index);
- }
-
- // the iterator class associated with this string class
-
- final class AttributedStringIterator implements AttributedCharacterIterator {
-
- // note on synchronization:
- // we don't synchronize on the iterator, assuming that an iterator is only used in one thread.
- // we do synchronize access to the AttributedString however, since it's more likely to be shared between threads.
-
- // start and end index for our iteration
- private int beginIndex;
- private int endIndex;
-
- // attributes that our client is interested in
- private String[] relevantAttributeNames;
-
- // the current index for our iteration
- // invariant: beginIndex <= currentIndex <= endIndex
- private int currentIndex;
-
- // information about the run that includes currentIndex
- private int currentRunIndex;
- private int currentRunStart;
- private int currentRunLimit;
-
- // constructor
- AttributedStringIterator(int beginIndex, int endIndex, String[] attributeNames) {
-
- if (beginIndex < 0 || beginIndex > endIndex || endIndex > length()) {
- throw new IllegalArgumentException("Invalid substring range");
- }
-
- this.beginIndex = beginIndex;
- this.endIndex = endIndex;
- this.currentIndex = beginIndex;
- updateRunInfo();
- if (attributeNames != null) {
- relevantAttributeNames = (String[]) attributeNames.clone();
- }
- }
-
- // CharacterIterator methods. See documentation in that interface.
-
- public char first() {
- return internalSetIndex(beginIndex);
- }
-
- public char last() {
- if (endIndex == beginIndex) {
- return internalSetIndex(endIndex);
- } else {
- return internalSetIndex(endIndex - 1);
- }
- }
-
- public char current() {
- if (currentIndex == endIndex) {
- return DONE;
- } else {
- return charAt(currentIndex);
- }
- }
-
- public char next() {
- if (currentIndex < endIndex) {
- return internalSetIndex(currentIndex + 1);
- }
- else {
- return DONE;
- }
- }
-
- public char previous() {
- if (currentIndex > beginIndex) {
- return internalSetIndex(currentIndex - 1);
- }
- else {
- return DONE;
- }
- }
-
- public char setIndex(int position) {
- if (position < beginIndex || position > endIndex)
- throw new IllegalArgumentException("Invalid index");
- return internalSetIndex(position);
- }
-
- public int getBeginIndex() {
- return beginIndex;
- }
-
- public int getEndIndex() {
- return endIndex;
- }
-
- public int getIndex() {
- return currentIndex;
- }
-
- public Object clone() {
- try {
- AttributedStringIterator other = (AttributedStringIterator) super.clone();
- return other;
- }
- catch (CloneNotSupportedException e) {
- throw new InternalError();
- }
- }
-
- // AttributedCharacterIterator methods. See documentation in that interface.
-
- public int getRunStart() {
- return currentRunStart;
- }
-
- public int getRunStart(String attributeName) {
- // ??? need to merge runs
- return currentRunStart;
- }
-
- public int getRunStart(Enumeration attributeNames) {
- // ??? need to merge runs
- return currentRunStart;
- }
-
- public int getRunLimit() {
- return currentRunLimit;
- }
-
- public int getRunLimit(String attributeName) {
- // ??? need to merge runs
- return currentRunLimit;
- }
-
- public int getRunLimit(Enumeration attributeNames) {
- // ??? need to merge runs
- return currentRunLimit;
- }
-
- public AttributeSet getAttributes() {
- // ??? need implementation
- return null;
- }
-
- public Enumeration getAllAttributeNames() {
- throw new NoSuchMethodError("not implemented yet");
- }
-
- public Object getAttribute(String attributeName) {
- int runIndex = currentRunIndex;
- if (runIndex < 0) {
- return null;
- }
- synchronized (AttributedString.this) {
- Vector currentRunAttributeNames = runAttributeNames[runIndex];
- Vector currentRunAttributeValues = runAttributeValues[runIndex];
- if (currentRunAttributeNames == null) {
- return null;
- }
- int attributeIndex = currentRunAttributeNames.indexOf(attributeName);
- if (attributeIndex != -1) {
- return currentRunAttributeValues.elementAt(attributeIndex);
- }
- else {
- return null;
- }
- }
- }
-
- public Enumeration getAttributeNames() {
- // ??? This should screen out attribute names that aren't relevant to the client
- int runIndex = currentRunIndex;
- if (runIndex < 0) {
- return null;
- }
- synchronized (AttributedString.this) {
- Vector currentRunAttributeNames = runAttributeNames[currentRunIndex];
- if (currentRunAttributeNames == null) {
- return null;
- }
- String[] names = new String[currentRunAttributeNames.size()];
- currentRunAttributeNames.copyInto(names);
- return new ArrayEnumeration(names);
- }
- }
-
- // internally used methods
-
- // set the current index, update information about the current run if necessary,
- // return the character at the current index
- private char internalSetIndex(int position) {
- currentIndex = position;
- if (position < currentRunStart || position >= currentRunLimit) {
- updateRunInfo();
- }
- if (currentIndex == endIndex) {
- return DONE;
- } else {
- return charAt(position);
- }
- }
-
- // update the information about the current run
- private void updateRunInfo() {
- if (currentIndex == endIndex) {
- currentRunStart = currentRunLimit = endIndex;
- } else {
- synchronized (AttributedString.this) {
- int runIndex = -1;
- while (runIndex < runCount - 1 && runStarts[runIndex + 1] <= currentIndex)
- runIndex++;
- currentRunIndex = runIndex;
- if (runIndex >= 0) {
- currentRunStart = runStarts[runIndex];
- }
- else {
- currentRunStart = beginIndex;
- }
- if (runIndex < runCount - 1) {
- currentRunLimit = runStarts[runIndex + 1];
- }
- else {
- currentRunLimit = endIndex;
- }
- }
- }
- }
- }
- }
-
- // ArrayEnumeration - should find a better place for this
- final class ArrayEnumeration implements Enumeration {
-
- private Object[] array;
- private int i = 0;
-
- public ArrayEnumeration(Object[] array) {
- this.array = array;
- }
-
- public boolean hasMoreElements() {
- return i < array.length;
- }
-
- public Object nextElement() {
- if (i < array.length)
- return array[i++];
- else
- throw new java.util.NoSuchElementException();
- }
- }
-