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