Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/java/text/AttributedString.java
38829 views
/*1* Copyright (c) 1997, 2012, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* by Oracle in the LICENSE file that accompanied this code.9*10* This code is distributed in the hope that it will be useful, but WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 2 along with this work; if not, write to the Free Software Foundation,18* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.19*20* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425package java.text;2627import java.util.*;28import java.text.AttributedCharacterIterator.Attribute;2930/**31* An AttributedString holds text and related attribute information. It32* may be used as the actual data storage in some cases where a text33* reader wants to access attributed text through the AttributedCharacterIterator34* interface.35*36* <p>37* An attribute is a key/value pair, identified by the key. No two38* attributes on a given character can have the same key.39*40* <p>The values for an attribute are immutable, or must not be mutated41* by clients or storage. They are always passed by reference, and not42* cloned.43*44* @see AttributedCharacterIterator45* @see Annotation46* @since 1.247*/4849public class AttributedString {5051// since there are no vectors of int, we have to use arrays.52// We allocate them in chunks of 10 elements so we don't have to allocate all the time.53private static final int ARRAY_SIZE_INCREMENT = 10;5455// field holding the text56String text;5758// fields holding run attribute information59// run attributes are organized by run60int runArraySize; // current size of the arrays61int runCount; // actual number of runs, <= runArraySize62int runStarts[]; // start index for each run63Vector<Attribute> runAttributes[]; // vector of attribute keys for each run64Vector<Object> runAttributeValues[]; // parallel vector of attribute values for each run6566/**67* Constructs an AttributedString instance with the given68* AttributedCharacterIterators.69*70* @param iterators AttributedCharacterIterators to construct71* AttributedString from.72* @throws NullPointerException if iterators is null73*/74AttributedString(AttributedCharacterIterator[] iterators) {75if (iterators == null) {76throw new NullPointerException("Iterators must not be null");77}78if (iterators.length == 0) {79text = "";80}81else {82// Build the String contents83StringBuffer buffer = new StringBuffer();84for (int counter = 0; counter < iterators.length; counter++) {85appendContents(buffer, iterators[counter]);86}8788text = buffer.toString();8990if (text.length() > 0) {91// Determine the runs, creating a new run when the attributes92// differ.93int offset = 0;94Map<Attribute,Object> last = null;9596for (int counter = 0; counter < iterators.length; counter++) {97AttributedCharacterIterator iterator = iterators[counter];98int start = iterator.getBeginIndex();99int end = iterator.getEndIndex();100int index = start;101102while (index < end) {103iterator.setIndex(index);104105Map<Attribute,Object> attrs = iterator.getAttributes();106107if (mapsDiffer(last, attrs)) {108setAttributes(attrs, index - start + offset);109}110last = attrs;111index = iterator.getRunLimit();112}113offset += (end - start);114}115}116}117}118119/**120* Constructs an AttributedString instance with the given text.121* @param text The text for this attributed string.122* @exception NullPointerException if <code>text</code> is null.123*/124public AttributedString(String text) {125if (text == null) {126throw new NullPointerException();127}128this.text = text;129}130131/**132* Constructs an AttributedString instance with the given text and attributes.133* @param text The text for this attributed string.134* @param attributes The attributes that apply to the entire string.135* @exception NullPointerException if <code>text</code> or136* <code>attributes</code> is null.137* @exception IllegalArgumentException if the text has length 0138* and the attributes parameter is not an empty Map (attributes139* cannot be applied to a 0-length range).140*/141public AttributedString(String text,142Map<? extends Attribute, ?> attributes)143{144if (text == null || attributes == null) {145throw new NullPointerException();146}147this.text = text;148149if (text.length() == 0) {150if (attributes.isEmpty())151return;152throw new IllegalArgumentException("Can't add attribute to 0-length text");153}154155int attributeCount = attributes.size();156if (attributeCount > 0) {157createRunAttributeDataVectors();158Vector<Attribute> newRunAttributes = new Vector<>(attributeCount);159Vector<Object> newRunAttributeValues = new Vector<>(attributeCount);160runAttributes[0] = newRunAttributes;161runAttributeValues[0] = newRunAttributeValues;162163Iterator<? extends Map.Entry<? extends Attribute, ?>> iterator = attributes.entrySet().iterator();164while (iterator.hasNext()) {165Map.Entry<? extends Attribute, ?> entry = iterator.next();166newRunAttributes.addElement(entry.getKey());167newRunAttributeValues.addElement(entry.getValue());168}169}170}171172/**173* Constructs an AttributedString instance with the given attributed174* text represented by AttributedCharacterIterator.175* @param text The text for this attributed string.176* @exception NullPointerException if <code>text</code> is null.177*/178public AttributedString(AttributedCharacterIterator text) {179// If performance is critical, this constructor should be180// implemented here rather than invoking the constructor for a181// subrange. We can avoid some range checking in the loops.182this(text, text.getBeginIndex(), text.getEndIndex(), null);183}184185/**186* Constructs an AttributedString instance with the subrange of187* the given attributed text represented by188* AttributedCharacterIterator. If the given range produces an189* empty text, all attributes will be discarded. Note that any190* attributes wrapped by an Annotation object are discarded for a191* 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 character196* of the range.197* @exception NullPointerException if <code>text</code> is null.198* @exception IllegalArgumentException if the subrange given by199* beginIndex and endIndex is out of the text range.200* @see java.text.Annotation201*/202public AttributedString(AttributedCharacterIterator text,203int beginIndex,204int endIndex) {205this(text, beginIndex, endIndex, null);206}207208/**209* Constructs an AttributedString instance with the subrange of210* the given attributed text represented by211* AttributedCharacterIterator. Only attributes that match the212* given attributes will be incorporated into the instance. If the213* given range produces an empty text, all attributes will be214* discarded. Note that any attributes wrapped by an Annotation215* object are discarded for a subrange of the original attribute216* 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 character221* of the range.222* @param attributes Specifies attributes to be extracted223* from the text. If null is specified, all available attributes will224* be used.225* @exception NullPointerException if <code>text</code> is null.226* @exception IllegalArgumentException if the subrange given by227* beginIndex and endIndex is out of the text range.228* @see java.text.Annotation229*/230public AttributedString(AttributedCharacterIterator text,231int beginIndex,232int endIndex,233Attribute[] attributes) {234if (text == null) {235throw new NullPointerException();236}237238// Validate the given subrange239int textBeginIndex = text.getBeginIndex();240int textEndIndex = text.getEndIndex();241if (beginIndex < textBeginIndex || endIndex > textEndIndex || beginIndex > endIndex)242throw new IllegalArgumentException("Invalid substring range");243244// Copy the given string245StringBuffer textBuffer = new StringBuffer();246text.setIndex(beginIndex);247for (char c = text.current(); text.getIndex() < endIndex; c = text.next())248textBuffer.append(c);249this.text = textBuffer.toString();250251if (beginIndex == endIndex)252return;253254// Select attribute keys to be taken care of255HashSet<Attribute> keys = new HashSet<>();256if (attributes == null) {257keys.addAll(text.getAllAttributeKeys());258} else {259for (int i = 0; i < attributes.length; i++)260keys.add(attributes[i]);261keys.retainAll(text.getAllAttributeKeys());262}263if (keys.isEmpty())264return;265266// Get and set attribute runs for each attribute name. Need to267// scan from the top of the text so that we can discard any268// Annotation that is no longer applied to a subset text segment.269Iterator<Attribute> itr = keys.iterator();270while (itr.hasNext()) {271Attribute attributeKey = itr.next();272text.setIndex(textBeginIndex);273while (text.getIndex() < endIndex) {274int start = text.getRunStart(attributeKey);275int limit = text.getRunLimit(attributeKey);276Object value = text.getAttribute(attributeKey);277278if (value != null) {279if (value instanceof Annotation) {280if (start >= beginIndex && limit <= endIndex) {281addAttribute(attributeKey, value, start - beginIndex, limit - beginIndex);282} else {283if (limit > endIndex)284break;285}286} else {287// if the run is beyond the given (subset) range, we288// don't need to process further.289if (start >= endIndex)290break;291if (limit > beginIndex) {292// attribute is applied to any subrange293if (start < beginIndex)294start = beginIndex;295if (limit > endIndex)296limit = endIndex;297if (start != limit) {298addAttribute(attributeKey, value, start - beginIndex, limit - beginIndex);299}300}301}302}303text.setIndex(limit);304}305}306}307308/**309* Adds an attribute to the entire string.310* @param attribute the attribute key311* @param value the value of the attribute; may be null312* @exception NullPointerException if <code>attribute</code> is null.313* @exception IllegalArgumentException if the AttributedString has length 0314* (attributes cannot be applied to a 0-length range).315*/316public void addAttribute(Attribute attribute, Object value) {317318if (attribute == null) {319throw new NullPointerException();320}321322int len = length();323if (len == 0) {324throw new IllegalArgumentException("Can't add attribute to 0-length text");325}326327addAttributeImpl(attribute, value, 0, len);328}329330/**331* Adds an attribute to a subrange of the string.332* @param attribute the attribute key333* @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 is338* greater than the length of the string, or beginIndex and endIndex together don't339* define a non-empty subrange of the string.340*/341public void addAttribute(Attribute attribute, Object value,342int beginIndex, int endIndex) {343344if (attribute == null) {345throw new NullPointerException();346}347348if (beginIndex < 0 || endIndex > length() || beginIndex >= endIndex) {349throw new IllegalArgumentException("Invalid substring range");350}351352addAttributeImpl(attribute, value, beginIndex, endIndex);353}354355/**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 last360* character of the range.361* @exception NullPointerException if <code>attributes</code> is null.362* @exception IllegalArgumentException if beginIndex is less then363* 0, endIndex is greater than the length of the string, or364* beginIndex and endIndex together don't define a non-empty365* subrange of the string and the attributes parameter is not an366* empty Map.367*/368public void addAttributes(Map<? extends Attribute, ?> attributes,369int beginIndex, int endIndex)370{371if (attributes == null) {372throw new NullPointerException();373}374375if (beginIndex < 0 || endIndex > length() || beginIndex > endIndex) {376throw new IllegalArgumentException("Invalid substring range");377}378if (beginIndex == endIndex) {379if (attributes.isEmpty())380return;381throw new IllegalArgumentException("Can't add attribute to 0-length text");382}383384// make sure we have run attribute data vectors385if (runCount == 0) {386createRunAttributeDataVectors();387}388389// break up runs if necessary390int beginRunIndex = ensureRunBreak(beginIndex);391int endRunIndex = ensureRunBreak(endIndex);392393Iterator<? extends Map.Entry<? extends Attribute, ?>> iterator =394attributes.entrySet().iterator();395while (iterator.hasNext()) {396Map.Entry<? extends Attribute, ?> entry = iterator.next();397addAttributeRunData(entry.getKey(), entry.getValue(), beginRunIndex, endRunIndex);398}399}400401private synchronized void addAttributeImpl(Attribute attribute, Object value,402int beginIndex, int endIndex) {403404// make sure we have run attribute data vectors405if (runCount == 0) {406createRunAttributeDataVectors();407}408409// break up runs if necessary410int beginRunIndex = ensureRunBreak(beginIndex);411int endRunIndex = ensureRunBreak(endIndex);412413addAttributeRunData(attribute, value, beginRunIndex, endRunIndex);414}415416private final void createRunAttributeDataVectors() {417// use temporary variables so things remain consistent in case of an exception418int newRunStarts[] = new int[ARRAY_SIZE_INCREMENT];419420@SuppressWarnings("unchecked")421Vector<Attribute> newRunAttributes[] = (Vector<Attribute>[]) new Vector<?>[ARRAY_SIZE_INCREMENT];422423@SuppressWarnings("unchecked")424Vector<Object> newRunAttributeValues[] = (Vector<Object>[]) new Vector<?>[ARRAY_SIZE_INCREMENT];425426runStarts = newRunStarts;427runAttributes = newRunAttributes;428runAttributeValues = newRunAttributeValues;429runArraySize = ARRAY_SIZE_INCREMENT;430runCount = 1; // assume initial run starting at index 0431}432433// ensure there's a run break at offset, return the index of the run434private final int ensureRunBreak(int offset) {435return ensureRunBreak(offset, true);436}437438/**439* Ensures there is a run break at offset, returning the index of440* the run. If this results in splitting a run, two things can happen:441* <ul>442* <li>If copyAttrs is true, the attributes from the existing run443* will be placed in both of the newly created runs.444* <li>If copyAttrs is false, the attributes from the existing run445* will NOT be copied to the run to the right (>= offset) of the break,446* but will exist on the run to the left (< offset).447* </ul>448*/449private final int ensureRunBreak(int offset, boolean copyAttrs) {450if (offset == length()) {451return runCount;452}453454// search for the run index where this offset should be455int runIndex = 0;456while (runIndex < runCount && runStarts[runIndex] < offset) {457runIndex++;458}459460// if the offset is at a run start already, we're done461if (runIndex < runCount && runStarts[runIndex] == offset) {462return runIndex;463}464465// we'll have to break up a run466// first, make sure we have enough space in our arrays467if (runCount == runArraySize) {468int newArraySize = runArraySize + ARRAY_SIZE_INCREMENT;469int newRunStarts[] = new int[newArraySize];470471@SuppressWarnings("unchecked")472Vector<Attribute> newRunAttributes[] = (Vector<Attribute>[]) new Vector<?>[newArraySize];473474@SuppressWarnings("unchecked")475Vector<Object> newRunAttributeValues[] = (Vector<Object>[]) new Vector<?>[newArraySize];476477for (int i = 0; i < runArraySize; i++) {478newRunStarts[i] = runStarts[i];479newRunAttributes[i] = runAttributes[i];480newRunAttributeValues[i] = runAttributeValues[i];481}482runStarts = newRunStarts;483runAttributes = newRunAttributes;484runAttributeValues = newRunAttributeValues;485runArraySize = newArraySize;486}487488// make copies of the attribute information of the old run that the new one used to be part of489// use temporary variables so things remain consistent in case of an exception490Vector<Attribute> newRunAttributes = null;491Vector<Object> newRunAttributeValues = null;492493if (copyAttrs) {494Vector<Attribute> oldRunAttributes = runAttributes[runIndex - 1];495Vector<Object> oldRunAttributeValues = runAttributeValues[runIndex - 1];496if (oldRunAttributes != null) {497newRunAttributes = new Vector<>(oldRunAttributes);498}499if (oldRunAttributeValues != null) {500newRunAttributeValues = new Vector<>(oldRunAttributeValues);501}502}503504// now actually break up the run505runCount++;506for (int i = runCount - 1; i > runIndex; i--) {507runStarts[i] = runStarts[i - 1];508runAttributes[i] = runAttributes[i - 1];509runAttributeValues[i] = runAttributeValues[i - 1];510}511runStarts[runIndex] = offset;512runAttributes[runIndex] = newRunAttributes;513runAttributeValues[runIndex] = newRunAttributeValues;514515return runIndex;516}517518// add the attribute attribute/value to all runs where beginRunIndex <= runIndex < endRunIndex519private void addAttributeRunData(Attribute attribute, Object value,520int beginRunIndex, int endRunIndex) {521522for (int i = beginRunIndex; i < endRunIndex; i++) {523int keyValueIndex = -1; // index of key and value in our vectors; assume we don't have an entry yet524if (runAttributes[i] == null) {525Vector<Attribute> newRunAttributes = new Vector<>();526Vector<Object> newRunAttributeValues = new Vector<>();527runAttributes[i] = newRunAttributes;528runAttributeValues[i] = newRunAttributeValues;529} else {530// check whether we have an entry already531keyValueIndex = runAttributes[i].indexOf(attribute);532}533534if (keyValueIndex == -1) {535// create new entry536int oldSize = runAttributes[i].size();537runAttributes[i].addElement(attribute);538try {539runAttributeValues[i].addElement(value);540}541catch (Exception e) {542runAttributes[i].setSize(oldSize);543runAttributeValues[i].setSize(oldSize);544}545} else {546// update existing entry547runAttributeValues[i].set(keyValueIndex, value);548}549}550}551552/**553* Creates an AttributedCharacterIterator instance that provides access to the entire contents of554* this string.555*556* @return An iterator providing access to the text and its attributes.557*/558public AttributedCharacterIterator getIterator() {559return getIterator(null, 0, length());560}561562/**563* Creates an AttributedCharacterIterator instance that provides access to564* selected contents of this string.565* Information about attributes not listed in attributes that the566* implementor may have need not be made accessible through the iterator.567* If the list is null, all available attribute information should be made568* accessible.569*570* @param attributes a list of attributes that the client is interested in571* @return an iterator providing access to the entire text and its selected attributes572*/573public AttributedCharacterIterator getIterator(Attribute[] attributes) {574return getIterator(attributes, 0, length());575}576577/**578* Creates an AttributedCharacterIterator instance that provides access to579* selected contents of this string.580* Information about attributes not listed in attributes that the581* implementor may have need not be made accessible through the iterator.582* If the list is null, all available attribute information should be made583* accessible.584*585* @param attributes a list of attributes that the client is interested in586* @param beginIndex the index of the first character587* @param endIndex the index of the character following the last character588* @return an iterator providing access to the text and its attributes589* @exception IllegalArgumentException if beginIndex is less then 0,590* endIndex is greater than the length of the string, or beginIndex is591* greater than endIndex.592*/593public AttributedCharacterIterator getIterator(Attribute[] attributes, int beginIndex, int endIndex) {594return new AttributedStringIterator(attributes, beginIndex, endIndex);595}596597// all (with the exception of length) reading operations are private,598// since AttributedString instances are accessed through iterators.599600// length is package private so that CharacterIteratorFieldDelegate can601// access it without creating an AttributedCharacterIterator.602int length() {603return text.length();604}605606private char charAt(int index) {607return text.charAt(index);608}609610private synchronized Object getAttribute(Attribute attribute, int runIndex) {611Vector<Attribute> currentRunAttributes = runAttributes[runIndex];612Vector<Object> currentRunAttributeValues = runAttributeValues[runIndex];613if (currentRunAttributes == null) {614return null;615}616int attributeIndex = currentRunAttributes.indexOf(attribute);617if (attributeIndex != -1) {618return currentRunAttributeValues.elementAt(attributeIndex);619}620else {621return null;622}623}624625// gets an attribute value, but returns an annotation only if it's range does not extend outside the range beginIndex..endIndex626private Object getAttributeCheckRange(Attribute attribute, int runIndex, int beginIndex, int endIndex) {627Object value = getAttribute(attribute, runIndex);628if (value instanceof Annotation) {629// need to check whether the annotation's range extends outside the iterator's range630if (beginIndex > 0) {631int currIndex = runIndex;632int runStart = runStarts[currIndex];633while (runStart >= beginIndex &&634valuesMatch(value, getAttribute(attribute, currIndex - 1))) {635currIndex--;636runStart = runStarts[currIndex];637}638if (runStart < beginIndex) {639// annotation's range starts before iterator's range640return null;641}642}643int textLength = length();644if (endIndex < textLength) {645int currIndex = runIndex;646int runLimit = (currIndex < runCount - 1) ? runStarts[currIndex + 1] : textLength;647while (runLimit <= endIndex &&648valuesMatch(value, getAttribute(attribute, currIndex + 1))) {649currIndex++;650runLimit = (currIndex < runCount - 1) ? runStarts[currIndex + 1] : textLength;651}652if (runLimit > endIndex) {653// annotation's range ends after iterator's range654return null;655}656}657// annotation's range is subrange of iterator's range,658// so we can return the value659}660return value;661}662663// returns whether all specified attributes have equal values in the runs with the given indices664private boolean attributeValuesMatch(Set<? extends Attribute> attributes, int runIndex1, int runIndex2) {665Iterator<? extends Attribute> iterator = attributes.iterator();666while (iterator.hasNext()) {667Attribute key = iterator.next();668if (!valuesMatch(getAttribute(key, runIndex1), getAttribute(key, runIndex2))) {669return false;670}671}672return true;673}674675// returns whether the two objects are either both null or equal676private final static boolean valuesMatch(Object value1, Object value2) {677if (value1 == null) {678return value2 == null;679} else {680return value1.equals(value2);681}682}683684/**685* Appends the contents of the CharacterIterator iterator into the686* StringBuffer buf.687*/688private final void appendContents(StringBuffer buf,689CharacterIterator iterator) {690int index = iterator.getBeginIndex();691int end = iterator.getEndIndex();692693while (index < end) {694iterator.setIndex(index++);695buf.append(iterator.current());696}697}698699/**700* Sets the attributes for the range from offset to the next run break701* (typically the end of the text) to the ones specified in attrs.702* This is only meant to be called from the constructor!703*/704private void setAttributes(Map<Attribute, Object> attrs, int offset) {705if (runCount == 0) {706createRunAttributeDataVectors();707}708709int index = ensureRunBreak(offset, false);710int size;711712if (attrs != null && (size = attrs.size()) > 0) {713Vector<Attribute> runAttrs = new Vector<>(size);714Vector<Object> runValues = new Vector<>(size);715Iterator<Map.Entry<Attribute, Object>> iterator = attrs.entrySet().iterator();716717while (iterator.hasNext()) {718Map.Entry<Attribute, Object> entry = iterator.next();719720runAttrs.add(entry.getKey());721runValues.add(entry.getValue());722}723runAttributes[index] = runAttrs;724runAttributeValues[index] = runValues;725}726}727728/**729* Returns true if the attributes specified in last and attrs differ.730*/731private static <K,V> boolean mapsDiffer(Map<K, V> last, Map<K, V> attrs) {732if (last == null) {733return (attrs != null && attrs.size() > 0);734}735return (!last.equals(attrs));736}737738739// the iterator class associated with this string class740741final private class AttributedStringIterator implements AttributedCharacterIterator {742743// note on synchronization:744// we don't synchronize on the iterator, assuming that an iterator is only used in one thread.745// we do synchronize access to the AttributedString however, since it's more likely to be shared between threads.746747// start and end index for our iteration748private int beginIndex;749private int endIndex;750751// attributes that our client is interested in752private Attribute[] relevantAttributes;753754// the current index for our iteration755// invariant: beginIndex <= currentIndex <= endIndex756private int currentIndex;757758// information about the run that includes currentIndex759private int currentRunIndex;760private int currentRunStart;761private int currentRunLimit;762763// constructor764AttributedStringIterator(Attribute[] attributes, int beginIndex, int endIndex) {765766if (beginIndex < 0 || beginIndex > endIndex || endIndex > length()) {767throw new IllegalArgumentException("Invalid substring range");768}769770this.beginIndex = beginIndex;771this.endIndex = endIndex;772this.currentIndex = beginIndex;773updateRunInfo();774if (attributes != null) {775relevantAttributes = attributes.clone();776}777}778779// Object methods. See documentation in that class.780781public boolean equals(Object obj) {782if (this == obj) {783return true;784}785if (!(obj instanceof AttributedStringIterator)) {786return false;787}788789AttributedStringIterator that = (AttributedStringIterator) obj;790791if (AttributedString.this != that.getString())792return false;793if (currentIndex != that.currentIndex || beginIndex != that.beginIndex || endIndex != that.endIndex)794return false;795return true;796}797798public int hashCode() {799return text.hashCode() ^ currentIndex ^ beginIndex ^ endIndex;800}801802public Object clone() {803try {804AttributedStringIterator other = (AttributedStringIterator) super.clone();805return other;806}807catch (CloneNotSupportedException e) {808throw new InternalError(e);809}810}811812// CharacterIterator methods. See documentation in that interface.813814public char first() {815return internalSetIndex(beginIndex);816}817818public char last() {819if (endIndex == beginIndex) {820return internalSetIndex(endIndex);821} else {822return internalSetIndex(endIndex - 1);823}824}825826public char current() {827if (currentIndex == endIndex) {828return DONE;829} else {830return charAt(currentIndex);831}832}833834public char next() {835if (currentIndex < endIndex) {836return internalSetIndex(currentIndex + 1);837}838else {839return DONE;840}841}842843public char previous() {844if (currentIndex > beginIndex) {845return internalSetIndex(currentIndex - 1);846}847else {848return DONE;849}850}851852public char setIndex(int position) {853if (position < beginIndex || position > endIndex)854throw new IllegalArgumentException("Invalid index");855return internalSetIndex(position);856}857858public int getBeginIndex() {859return beginIndex;860}861862public int getEndIndex() {863return endIndex;864}865866public int getIndex() {867return currentIndex;868}869870// AttributedCharacterIterator methods. See documentation in that interface.871872public int getRunStart() {873return currentRunStart;874}875876public int getRunStart(Attribute attribute) {877if (currentRunStart == beginIndex || currentRunIndex == -1) {878return currentRunStart;879} else {880Object value = getAttribute(attribute);881int runStart = currentRunStart;882int runIndex = currentRunIndex;883while (runStart > beginIndex &&884valuesMatch(value, AttributedString.this.getAttribute(attribute, runIndex - 1))) {885runIndex--;886runStart = runStarts[runIndex];887}888if (runStart < beginIndex) {889runStart = beginIndex;890}891return runStart;892}893}894895public int getRunStart(Set<? extends Attribute> attributes) {896if (currentRunStart == beginIndex || currentRunIndex == -1) {897return currentRunStart;898} else {899int runStart = currentRunStart;900int runIndex = currentRunIndex;901while (runStart > beginIndex &&902AttributedString.this.attributeValuesMatch(attributes, currentRunIndex, runIndex - 1)) {903runIndex--;904runStart = runStarts[runIndex];905}906if (runStart < beginIndex) {907runStart = beginIndex;908}909return runStart;910}911}912913public int getRunLimit() {914return currentRunLimit;915}916917public int getRunLimit(Attribute attribute) {918if (currentRunLimit == endIndex || currentRunIndex == -1) {919return currentRunLimit;920} else {921Object value = getAttribute(attribute);922int runLimit = currentRunLimit;923int runIndex = currentRunIndex;924while (runLimit < endIndex &&925valuesMatch(value, AttributedString.this.getAttribute(attribute, runIndex + 1))) {926runIndex++;927runLimit = runIndex < runCount - 1 ? runStarts[runIndex + 1] : endIndex;928}929if (runLimit > endIndex) {930runLimit = endIndex;931}932return runLimit;933}934}935936public int getRunLimit(Set<? extends Attribute> attributes) {937if (currentRunLimit == endIndex || currentRunIndex == -1) {938return currentRunLimit;939} else {940int runLimit = currentRunLimit;941int runIndex = currentRunIndex;942while (runLimit < endIndex &&943AttributedString.this.attributeValuesMatch(attributes, currentRunIndex, runIndex + 1)) {944runIndex++;945runLimit = runIndex < runCount - 1 ? runStarts[runIndex + 1] : endIndex;946}947if (runLimit > endIndex) {948runLimit = endIndex;949}950return runLimit;951}952}953954public Map<Attribute,Object> getAttributes() {955if (runAttributes == null || currentRunIndex == -1 || runAttributes[currentRunIndex] == null) {956// ??? would be nice to return null, but current spec doesn't allow it957// returning Hashtable saves AttributeMap from dealing with emptiness958return new Hashtable<>();959}960return new AttributeMap(currentRunIndex, beginIndex, endIndex);961}962963public Set<Attribute> getAllAttributeKeys() {964// ??? This should screen out attribute keys that aren't relevant to the client965if (runAttributes == null) {966// ??? would be nice to return null, but current spec doesn't allow it967// returning HashSet saves us from dealing with emptiness968return new HashSet<>();969}970synchronized (AttributedString.this) {971// ??? should try to create this only once, then update if necessary,972// and give callers read-only view973Set<Attribute> keys = new HashSet<>();974int i = 0;975while (i < runCount) {976if (runStarts[i] < endIndex && (i == runCount - 1 || runStarts[i + 1] > beginIndex)) {977Vector<Attribute> currentRunAttributes = runAttributes[i];978if (currentRunAttributes != null) {979int j = currentRunAttributes.size();980while (j-- > 0) {981keys.add(currentRunAttributes.get(j));982}983}984}985i++;986}987return keys;988}989}990991public Object getAttribute(Attribute attribute) {992int runIndex = currentRunIndex;993if (runIndex < 0) {994return null;995}996return AttributedString.this.getAttributeCheckRange(attribute, runIndex, beginIndex, endIndex);997}998999// internally used methods10001001private AttributedString getString() {1002return AttributedString.this;1003}10041005// set the current index, update information about the current run if necessary,1006// return the character at the current index1007private char internalSetIndex(int position) {1008currentIndex = position;1009if (position < currentRunStart || position >= currentRunLimit) {1010updateRunInfo();1011}1012if (currentIndex == endIndex) {1013return DONE;1014} else {1015return charAt(position);1016}1017}10181019// update the information about the current run1020private void updateRunInfo() {1021if (currentIndex == endIndex) {1022currentRunStart = currentRunLimit = endIndex;1023currentRunIndex = -1;1024} else {1025synchronized (AttributedString.this) {1026int runIndex = -1;1027while (runIndex < runCount - 1 && runStarts[runIndex + 1] <= currentIndex)1028runIndex++;1029currentRunIndex = runIndex;1030if (runIndex >= 0) {1031currentRunStart = runStarts[runIndex];1032if (currentRunStart < beginIndex)1033currentRunStart = beginIndex;1034}1035else {1036currentRunStart = beginIndex;1037}1038if (runIndex < runCount - 1) {1039currentRunLimit = runStarts[runIndex + 1];1040if (currentRunLimit > endIndex)1041currentRunLimit = endIndex;1042}1043else {1044currentRunLimit = endIndex;1045}1046}1047}1048}10491050}10511052// the map class associated with this string class, giving access to the attributes of one run10531054final private class AttributeMap extends AbstractMap<Attribute,Object> {10551056int runIndex;1057int beginIndex;1058int endIndex;10591060AttributeMap(int runIndex, int beginIndex, int endIndex) {1061this.runIndex = runIndex;1062this.beginIndex = beginIndex;1063this.endIndex = endIndex;1064}10651066public Set<Map.Entry<Attribute, Object>> entrySet() {1067HashSet<Map.Entry<Attribute, Object>> set = new HashSet<>();1068synchronized (AttributedString.this) {1069int size = runAttributes[runIndex].size();1070for (int i = 0; i < size; i++) {1071Attribute key = runAttributes[runIndex].get(i);1072Object value = runAttributeValues[runIndex].get(i);1073if (value instanceof Annotation) {1074value = AttributedString.this.getAttributeCheckRange(key,1075runIndex, beginIndex, endIndex);1076if (value == null) {1077continue;1078}1079}10801081Map.Entry<Attribute, Object> entry = new AttributeEntry(key, value);1082set.add(entry);1083}1084}1085return set;1086}10871088public Object get(Object key) {1089return AttributedString.this.getAttributeCheckRange((Attribute) key, runIndex, beginIndex, endIndex);1090}1091}1092}10931094class AttributeEntry implements Map.Entry<Attribute,Object> {10951096private Attribute key;1097private Object value;10981099AttributeEntry(Attribute key, Object value) {1100this.key = key;1101this.value = value;1102}11031104public boolean equals(Object o) {1105if (!(o instanceof AttributeEntry)) {1106return false;1107}1108AttributeEntry other = (AttributeEntry) o;1109return other.key.equals(key) &&1110(value == null ? other.value == null : other.value.equals(value));1111}11121113public Attribute getKey() {1114return key;1115}11161117public Object getValue() {1118return value;1119}11201121public Object setValue(Object newValue) {1122throw new UnsupportedOperationException();1123}11241125public int hashCode() {1126return key.hashCode() ^ (value==null ? 0 : value.hashCode());1127}11281129public String toString() {1130return key.toString()+"="+value.toString();1131}1132}113311341135