1 /* AttributedStringIterator.java -- Class to iterate over AttributedString
2 Copyright (C) 1998, 1999, 2004, 2005 Free Software Foundation, Inc.
3
4 This file is part of GNU Classpath.
5
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
20
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
25
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
37
38
39 package java.text;
40
41 import java.util.HashMap;
42 import java.util.HashSet;
43 import java.util.Iterator;
44 import java.util.Map;
45 import java.util.Set;
46
47 /**
48 * This class implements the AttributedCharacterIterator interface. It
49 * is used by AttributedString.getIterator().
50 *
51 * @version 0.0
52 *
53 * @author Aaron M. Renn (arenn@urbanophile.com)
54 */
55 class AttributedStringIterator implements AttributedCharacterIterator
56 {
57
58 /*************************************************************************/
59
60 /** The character iterator containing the text */
61 private CharacterIterator ci;
62
63 /** The list of attributes and ranges */
64 private AttributedString.AttributeRange[] attribs;
65
66 /**
67 * The list of attributes that the user is interested in. We may,
68 * at our option, not return any other attributes.
69 */
70 private AttributedCharacterIterator.Attribute[] restricts;
71
72 /*************************************************************************/
73
74 AttributedStringIterator(StringCharacterIterator sci,
75 AttributedString.AttributeRange[] attribs,
76 int begin_index, int end_index,
77 AttributedCharacterIterator.Attribute[] restricts)
78 {
79 this.ci = new StringCharacterIterator(sci, begin_index, end_index);
80 this.attribs = attribs;
81 this.restricts = restricts;
82 }
83
84 /*************************************************************************/
85
86 // First we have a bunch of stupid redirects. If StringCharacterIterator
87 // weren't final, I just would have extended that for this class. Alas, no.
88
89 public Object clone()
90 {
91 return(ci.clone());
92 }
93
94 public char current()
95 {
96 return(ci.current());
97 }
98
99 public char next()
100 {
101 return(ci.next());
102 }
103
104 public char previous()
105 {
106 return(ci.previous());
107 }
108
109 public char first()
110 {
111 return(ci.first());
112 }
113
114 public char last()
115 {
116 return(ci.last());
117 }
118
119 public int getIndex()
120 {
121 return(ci.getIndex());
122 }
123
124 public char setIndex(int index)
125 {
126 return(ci.setIndex(index));
127 }
128
129 public int getBeginIndex()
130 {
131 return(ci.getBeginIndex());
132 }
133
134 public int getEndIndex()
135 {
136 return(ci.getEndIndex());
137 }
138
139 /*
140 * Here is where the AttributedCharacterIterator methods start.
141 */
142
143 /*************************************************************************/
144
145 /**
146 * Returns a list of all the attribute keys that are defined anywhere
147 * on this string.
148 */
149 public Set getAllAttributeKeys()
150 {
151 HashSet s = new HashSet();
152 if (attribs == null)
153 return(s);
154
155 for (int i = 0; i < attribs.length; i++)
156 {
157 if (attribs[i].begin_index > getEndIndex()
158 || attribs[i].end_index <= getBeginIndex())
159 continue;
160
161 Set key_set = attribs[i].attribs.keySet();
162 Iterator iter = key_set.iterator();
163 while (iter.hasNext())
164 {
165 s.add(iter.next());
166 }
167 }
168
169 return(s);
170 }
171
172 /*************************************************************************/
173
174 /**
175 * Various methods that determine how far the run extends for various
176 * attribute combinations.
177 */
178
179 public int getRunLimit()
180 {
181 return(getRunLimit(getAttributes().keySet()));
182 }
183
184 public int getRunLimit(AttributedCharacterIterator.Attribute attrib)
185 {
186 HashSet s = new HashSet();
187 s.add(attrib);
188 return(getRunLimit(s));
189 }
190
191 public synchronized int getRunLimit(Set attributeSet)
192 {
193 if (attributeSet == null)
194 return ci.getEndIndex();
195
196 int current = ci.getIndex();
197 int end = ci.getEndIndex();
198 int limit = current;
199 if (current == end)
200 return end;
201 Map runValues = getAttributes();
202 while (limit < end)
203 {
204 Iterator iterator = attributeSet.iterator();
205 while (iterator.hasNext())
206 {
207 // Qualified name is a workaround for a gcj 4.0 bug.
208 AttributedCharacterIterator.Attribute attributeKey
209 = (AttributedCharacterIterator.Attribute) iterator.next();
210 Object v1 = runValues.get(attributeKey);
211 Object v2 = getAttribute(attributeKey, limit + 1);
212 boolean changed = false;
213 // check for equal or both null, if NO return start
214 if (v1 != null)
215 {
216 changed = !v1.equals(v2);
217 }
218 else
219 {
220 changed = (v2 != null);
221 }
222 if (changed)
223 return limit + 1;
224 }
225 // no differences, so increment limit and next and loop again
226 limit++;
227 }
228 return end;
229 }
230
231 /*************************************************************************/
232
233 /**
234 * Various methods that determine where the run begins for various
235 * attribute combinations.
236 */
237
238 /**
239 * Returns the index of the first character in the run containing the current
240 * character and defined by all the attributes defined for that character
241 * position.
242 *
243 * @return The run start index.
244 */
245 public int getRunStart()
246 {
247 return(getRunStart(getAttributes().keySet()));
248 }
249
250 /**
251 * Returns the index of the first character in the run, defined by the
252 * specified attribute, that contains the current character.
253 *
254 * @param attrib the attribute (<code>null</code> permitted).
255 *
256 * return The index of the first character in the run.
257 */
258 public int getRunStart(AttributedCharacterIterator.Attribute attrib)
259 {
260 if (attrib == null)
261 return ci.getBeginIndex();
262 HashSet s = new HashSet();
263 s.add(attrib);
264 return(getRunStart(s));
265 }
266
267 /**
268 * Returns the index of the first character in the run, defined by the
269 * specified attribute set, that contains the current character.
270 *
271 * @param attributeSet the attribute set (<code>null</code> permitted).
272 *
273 * return The index of the first character in the run.
274 */
275 public int getRunStart(Set attributeSet)
276 {
277 if (attributeSet == null)
278 return ci.getBeginIndex();
279
280 int current = ci.getIndex();
281 int begin = ci.getBeginIndex();
282 int start = current;
283 if (start == begin)
284 return begin;
285 Map runValues = getAttributes();
286 int prev = start - 1;
287 while (start > begin)
288 {
289 Iterator iterator = attributeSet.iterator();
290 while (iterator.hasNext())
291 {
292 // Qualified name is a workaround for a gcj 4.0 bug.
293 AttributedCharacterIterator.Attribute attributeKey
294 = (AttributedCharacterIterator.Attribute) iterator.next();
295 Object v1 = runValues.get(attributeKey);
296 Object v2 = getAttribute(attributeKey, prev);
297 boolean changed = false;
298 // check for equal or both null, if NO return start
299 if (v1 != null)
300 {
301 changed = !v1.equals(v2);
302 }
303 else
304 {
305 changed = (v2 != null);
306 }
307 if (changed)
308 return start;
309 }
310 // no differences, so decrement start and prev and loop again
311 start--;
312 prev--;
313 }
314 return start;
315 }
316
317 /*************************************************************************/
318
319 /**
320 * Returns the value for an attribute at the specified position. If the
321 * attribute key (<code>key</code>) is <code>null</code>, the method returns
322 * <code>null</code>.
323 *
324 * @param key the key (<code>null</code> permitted).
325 * @param pos the character position.
326 *
327 * @return The attribute value (possibly <code>null</code>).
328 */
329 private Object getAttribute(AttributedCharacterIterator.Attribute key,
330 int pos)
331 {
332 if (attribs == null)
333 return null;
334 for (int i = attribs.length - 1; i >= 0; i--)
335 {
336 if (pos >= attribs[i].begin_index && pos < attribs[i].end_index)
337 {
338 Set keys = attribs[i].attribs.keySet();
339 if (keys.contains(key))
340 {
341 return attribs[i].attribs.get(key);
342 }
343 }
344 }
345 return null;
346 }
347
348 /**
349 * Returns the value for an attribute at the current position. If the
350 * attribute key (<code>key</code>) is <code>null</code>, the method returns
351 * <code>null</code>.
352 *
353 * @param key the key (<code>null</code> permitted).
354 *
355 * @return The attribute value (possibly <code>null</code>).
356 */
357 public Object getAttribute(AttributedCharacterIterator.Attribute key)
358 {
359 return getAttribute(key, ci.getIndex());
360 }
361
362 /*************************************************************************/
363
364 /**
365 * Return a list of all the attributes and values defined for this
366 * character
367 */
368 public Map getAttributes()
369 {
370 HashMap m = new HashMap();
371 if (attribs == null)
372 return(m);
373
374 for (int i = 0; i < attribs.length; i++)
375 {
376 if ((ci.getIndex() >= attribs[i].begin_index) &&
377 (ci.getIndex() < attribs[i].end_index))
378 m.putAll(attribs[i].attribs);
379 }
380
381 return(m);
382 }
383
384 } // class AttributedStringIterator