History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/java/text/AttributedString.java (1 of 18352 files). Show all

Unified · Split

java/pkg/java/text/AttributedString.java+1120−0
@@ -0,0 +1,1120 @@
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+}