2 * (C) Copyright IBM Corp. 1998-2004. All Rights Reserved.
\r
4 * The program is provided "as is" without any warranty express or
\r
5 * implied, including the warranty of non-infringement and the implied
\r
6 * warranties of merchantibility and fitness for a particular purpose.
\r
7 * IBM will not be liable for any damages suffered by you as a result
\r
8 * of using the Program. In no event will IBM be liable for any
\r
9 * special, indirect or consequential damages or lost profits even if
\r
10 * IBM has been advised of the possibility of their occurrence. IBM
\r
11 * will not be liable for any third party claims against you.
\r
14 package com.ibm.richtext.textformat;
\r
16 import com.ibm.richtext.styledtext.MConstText;
\r
17 import java.text.CharacterIterator;
\r
19 import com.ibm.richtext.textlayout.attributes.AttributeMap;
\r
22 import java.text.AttributedCharacterIterator;
\r
23 import java.util.Map;
\r
24 import java.util.Set;
\r
28 import com.ibm.richtext.textlayout.attributes.AttributedCharacterIterator;
\r
29 import com.ibm.richtext.textlayout.attributes.AttributedCharacterIterator.Attribute;
\r
30 import com.ibm.richtext.textlayout.attributes.Map;
\r
34 * An AttributedCharacterIterator over an MConstText.
\r
36 public final class MTextIterator implements AttributedCharacterIterator,
\r
39 static final String COPYRIGHT =
\r
40 "(C) Copyright IBM Corp. 1998-1999 - All Rights Reserved";
\r
41 // memory leak, since this cache is never flushed
\r
43 private static class Matcher {
\r
45 boolean matches(Map lhs, Map rhs, Object query) {
\r
47 Object lhsVal = lhs.get(query);
\r
48 Object rhsVal = rhs.get(query);
\r
50 if (lhsVal == null) {
\r
51 return rhsVal == null;
\r
54 return lhsVal.equals(rhsVal);
\r
59 private static final Matcher ATTR_MATCHER = new Matcher();
\r
61 // Not quite optimal. Could have a matcher that would decompose
\r
62 // a set once for repeated queries. Of course that would require
\r
65 private static final Matcher SET_MATCHER = new Matcher() {
\r
67 boolean matches(Map lhs, Map rhs, Object query) {
\r
69 // Not using Iterator to simplify 1.1 port.
\r
70 Object[] elements = ((Set)query).toArray();
\r
71 for (int i=0; i < elements.length; i++) {
\r
72 if (!super.matches(lhs, rhs, elements[i])) {
\r
81 private final class StyleCache {
\r
83 private int fRunStart = 0;
\r
84 private int fRunLimit = -1;
\r
85 private int fRangeStart;
\r
86 private int fRangeLimit;
\r
87 private AttributeMap fStyle;
\r
89 StyleCache(MConstText text, int start, int limit) {
\r
91 fRangeStart = start;
\r
92 fRangeLimit = limit;
\r
96 private void update(int pos) {
\r
97 if (pos < fRunStart || pos >= fRunLimit) {
\r
98 AttributeMap style = AttributeMap.EMPTY_ATTRIBUTE_MAP;
\r
99 if (pos < fRangeStart) {
\r
100 fRunLimit = fRangeStart;
\r
101 fRunStart = Integer.MIN_VALUE;
\r
103 else if (pos > fRangeLimit) {
\r
104 fRunStart = fRangeLimit;
\r
105 fRunLimit = Integer.MAX_VALUE;
\r
108 fRunStart = Math.max(fRangeStart, fText.characterStyleStart(pos));
\r
109 fRunStart = Math.max(fRunStart, fText.paragraphStart(pos));
\r
111 fRunLimit = Math.min(fRangeLimit, fText.characterStyleLimit(pos));
\r
112 fRunLimit = Math.min(fRunLimit, fText.paragraphLimit(pos));
\r
113 if (fRunStart < fRunLimit) {
\r
114 style = fText.paragraphStyleAt(pos);
\r
115 style = style.addAttributes(fText.characterStyleAt(pos));
\r
118 fStyle = fFontResolver.applyFont(style);
\r
122 int getRunStart(int pos) {
\r
127 int getRunLimit(int pos) {
\r
132 Map getStyle(int pos) {
\r
138 private MConstText fText;
\r
139 private CharacterIterator fCharIter;
\r
140 private FontResolver fFontResolver;
\r
142 private StyleCache fStyleCache;
\r
145 * Create an MTextIterator over the range [start, limit).
\r
147 public MTextIterator(MConstText text,
\r
148 FontResolver resolver,
\r
153 fFontResolver = resolver;
\r
154 fCharIter = text.createCharacterIterator(start, limit);
\r
156 fStyleCache = new StyleCache(text, start, limit);
\r
160 * Sets the position to getBeginIndex() and returns the character at that
\r
162 * @return the first character in the text, or DONE if the text is empty
\r
163 * @see #getBeginIndex
\r
165 public char first() {
\r
166 return fCharIter.first();
\r
170 * Sets the position to getEndIndex()-1 (getEndIndex() if the text is empty)
\r
171 * and returns the character at that position.
\r
172 * @return the last character in the text, or DONE if the text is empty
\r
173 * @see #getEndIndex
\r
175 public char last() {
\r
176 return fCharIter.last();
\r
180 * Gets the character at the current position (as returned by getIndex()).
\r
181 * @return the character at the current position or DONE if the current
\r
182 * position is off the end of the text.
\r
185 public char current() {
\r
186 return fCharIter.current();
\r
190 * Increments the iterator's index by one and returns the character
\r
191 * at the new index. If the resulting index is greater or equal
\r
192 * to getEndIndex(), the current index is reset to getEndIndex() and
\r
193 * a value of DONE is returned.
\r
194 * @return the character at the new position or DONE if the new
\r
195 * position is off the end of the text range.
\r
197 public char next() {
\r
198 return fCharIter.next();
\r
202 * Decrements the iterator's index by one and returns the character
\r
203 * at the new index. If the current index is getBeginIndex(), the index
\r
204 * remains at getBeginIndex() and a value of DONE is returned.
\r
205 * @return the character at the new position or DONE if the current
\r
206 * position is equal to getBeginIndex().
\r
208 public char previous() {
\r
209 return fCharIter.previous();
\r
213 * Sets the position to the specified position in the text and returns that
\r
215 * @param position the position within the text. Valid values range from
\r
216 * getBeginIndex() to getEndIndex(). An IllegalArgumentException is thrown
\r
217 * if an invalid value is supplied.
\r
218 * @return the character at the specified position or DONE if the specified position is equal to getEndIndex()
\r
220 public char setIndex(int position) {
\r
221 return fCharIter.setIndex(position);
\r
225 * Returns the start index of the text.
\r
226 * @return the index at which the text begins.
\r
228 public int getBeginIndex() {
\r
229 return fCharIter.getBeginIndex();
\r
233 * Returns the end index of the text. This index is the index of the first
\r
234 * character following the end of the text.
\r
235 * @return the index after the last character in the text
\r
237 public int getEndIndex() {
\r
238 return fCharIter.getEndIndex();
\r
242 * Returns the current index.
\r
243 * @return the current index.
\r
245 public int getIndex() {
\r
246 return fCharIter.getIndex();
\r
250 * Returns the index of the first character of the run
\r
251 * with respect to all attributes containing the current character.
\r
253 public int getRunStart() {
\r
254 return fStyleCache.getRunStart(fCharIter.getIndex());
\r
258 * Returns the index of the first character of the run
\r
259 * with respect to the given attribute containing the current character.
\r
261 public int getRunStart(Object attribute) {
\r
263 return getRunStart(attribute, ATTR_MATCHER);
\r
267 * Returns the index of the first character of the run
\r
268 * with respect to the given attribute containing the current character.
\r
271 public int getRunStart(Attribute attribute) {
\r
273 return getRunStart(attribute, ATTR_MATCHER);
\r
278 * Returns the index of the first character of the run
\r
279 * with respect to the given attributes containing the current character.
\r
282 public int getRunStart(Set attributes) {
\r
284 return getRunStart(attributes, SET_MATCHER);
\r
288 private int getRunStart(Object query, Matcher matcher) {
\r
290 int runStart = getRunStart();
\r
291 int rangeStart = getBeginIndex();
\r
292 Map initialStyle = getAttributes();
\r
294 while (runStart > rangeStart) {
\r
295 AttributeMap style = fText.characterStyleAt(runStart-1);
\r
296 if (!matcher.matches(initialStyle, style, query)) {
\r
299 runStart = fText.characterStyleStart(runStart-1);
\r
305 * Returns the index of the first character following the run
\r
306 * with respect to all attributes containing the current character.
\r
308 public int getRunLimit() {
\r
309 return fStyleCache.getRunLimit(fCharIter.getIndex());
\r
313 * Returns the index of the first character following the run
\r
314 * with respect to the given attribute containing the current character.
\r
316 public int getRunLimit(Object attribute) {
\r
318 return getRunLimit(attribute, ATTR_MATCHER);
\r
322 * Returns the index of the first character following the run
\r
323 * with respect to the given attribute containing the current character.
\r
326 public int getRunLimit(Attribute attribute) {
\r
328 return getRunLimit(attribute, ATTR_MATCHER);
\r
333 * Returns the index of the first character following the run
\r
334 * with respect to the given attributes containing the current character.
\r
337 public int getRunLimit(Set attributes) {
\r
339 return getRunLimit(attributes, SET_MATCHER);
\r
343 private int getRunLimit(Object query, Matcher matcher) {
\r
345 int runLimit = getRunLimit();
\r
346 int rangeLimit = getEndIndex();
\r
347 Map initialStyle = getAttributes();
\r
349 while (runLimit < rangeLimit) {
\r
350 AttributeMap style = fText.characterStyleAt(runLimit);
\r
351 if (!matcher.matches(initialStyle, style, query)) {
\r
354 runLimit = fText.characterStyleLimit(runLimit);
\r
360 * Returns a map with the attributes defined on the current
\r
363 public Map getAttributes() {
\r
364 return fStyleCache.getStyle(fCharIter.getIndex());
\r
368 * Returns the value of the named attribute for the current character.
\r
369 * Returns null if the attribute is not defined.
\r
370 * @param attribute the key of the attribute whose value is requested.
\r
372 public Object getAttribute(Object attribute) {
\r
373 return getAttributes().get(attribute);
\r
377 * Returns the value of the named attribute for the current character.
\r
378 * Returns null if the attribute is not defined.
\r
379 * @param attribute the key of the attribute whose value is requested.
\r
381 public Object getAttribute(Attribute attribute) {
\r
382 return getAttributes().get(attribute);
\r
386 * Returns the keys of all attributes defined on the
\r
387 * iterator's text range. The set is empty if no
\r
388 * attributes are defined.
\r
391 public Set getAllAttributeKeys() {
\r
392 throw new Error("Implement this method!");
\r
396 public Object clone() {
\r
397 return new MTextIterator(fText,
\r