1 /* AttributedStringIterator.java -- Class to iterate over AttributedString 2 Copyright (C) 1998, 1999, 2004, 2005, 2006, 2012 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 import static java.text.AttributedCharacterIterator.Attribute; 48 49 /** 50 * This class implements the AttributedCharacterIterator interface. It 51 * is used by AttributedString.getIterator(). 52 * 53 * @version 0.0 54 * 55 * @author Aaron M. Renn (arenn@urbanophile.com) 56 */ 57 class AttributedStringIterator implements AttributedCharacterIterator 58 { 59 60 /*************************************************************************/ 61 62 /** The character iterator containing the text */ 63 private CharacterIterator ci; 64 65 /** The list of attributes and ranges */ 66 private AttributedString.AttributeRange[] attribs; 67 68 /** 69 * The list of attributes that the user is interested in. We may, 70 * at our option, not return any other attributes. 71 */ 72 private Attribute[] restricts; 73 74 /*************************************************************************/ 75 76 /** 77 * Creates a new instance. 78 * 79 * @param sci an iterator for the string content. 80 * @param attribs the attribute ranges. 81 * @param beginIndex the start index. 82 * @param endIndex the end index. 83 * @param restricts the attributes that the user is interested in. 84 */ AttributedStringIterator(StringCharacterIterator sci, AttributedString.AttributeRange[] attribs, int beginIndex, int endIndex, AttributedCharacterIterator.Attribute[] restricts)85 AttributedStringIterator(StringCharacterIterator sci, 86 AttributedString.AttributeRange[] attribs, 87 int beginIndex, int endIndex, 88 AttributedCharacterIterator.Attribute[] restricts) 89 { 90 this.ci = new StringCharacterIterator(sci, beginIndex, endIndex); 91 this.attribs = attribs; 92 this.restricts = restricts; 93 } 94 95 /*************************************************************************/ 96 97 // First we have a bunch of stupid redirects. If StringCharacterIterator 98 // weren't final, I just would have extended that for this class. Alas, no. 99 clone()100 public Object clone() 101 { 102 return(ci.clone()); 103 } 104 current()105 public char current() 106 { 107 return(ci.current()); 108 } 109 next()110 public char next() 111 { 112 return(ci.next()); 113 } 114 previous()115 public char previous() 116 { 117 return(ci.previous()); 118 } 119 first()120 public char first() 121 { 122 return(ci.first()); 123 } 124 last()125 public char last() 126 { 127 return(ci.last()); 128 } 129 getIndex()130 public int getIndex() 131 { 132 return(ci.getIndex()); 133 } 134 setIndex(int index)135 public char setIndex(int index) 136 { 137 return(ci.setIndex(index)); 138 } 139 getBeginIndex()140 public int getBeginIndex() 141 { 142 return(ci.getBeginIndex()); 143 } 144 getEndIndex()145 public int getEndIndex() 146 { 147 return(ci.getEndIndex()); 148 } 149 150 /* 151 * Here is where the AttributedCharacterIterator methods start. 152 */ 153 154 /*************************************************************************/ 155 156 /** 157 * Returns a list of all the attribute keys that are defined anywhere 158 * on this string. 159 */ getAllAttributeKeys()160 public Set<Attribute> getAllAttributeKeys() 161 { 162 HashSet<Attribute> s = new HashSet<Attribute>(); 163 if (attribs == null) 164 return(s); 165 166 for (int i = 0; i < attribs.length; i++) 167 { 168 if (attribs[i].beginIndex > getEndIndex() 169 || attribs[i].endIndex <= getBeginIndex()) 170 continue; 171 172 Iterator<? extends Attribute> iter = attribs[i].attribs.keySet().iterator(); 173 while (iter.hasNext()) 174 { 175 s.add(iter.next()); 176 } 177 } 178 179 return(s); 180 } 181 182 /*************************************************************************/ 183 184 /** 185 * Various methods that determine how far the run extends for various 186 * attribute combinations. 187 */ 188 getRunLimit()189 public int getRunLimit() 190 { 191 return getRunLimit(getAllAttributeKeys()); 192 } 193 getRunLimit(Attribute attrib)194 public int getRunLimit(Attribute attrib) 195 { 196 HashSet<Attribute> s = new HashSet<Attribute>(); 197 s.add(attrib); 198 return(getRunLimit(s)); 199 } 200 getRunLimit(Set<? extends Attribute> attributeSet)201 public synchronized int getRunLimit(Set<? extends Attribute> attributeSet) 202 { 203 if (attributeSet == null) 204 return ci.getEndIndex(); 205 206 int current = ci.getIndex(); 207 int end = ci.getEndIndex(); 208 int limit = current; 209 if (current == end) 210 return end; 211 Map<Attribute,Object> runValues = getAttributes(); 212 while (limit < end) 213 { 214 Iterator<? extends Attribute> iterator = attributeSet.iterator(); 215 while (iterator.hasNext()) 216 { 217 Attribute attributeKey = iterator.next(); 218 Object v1 = runValues.get(attributeKey); 219 Object v2 = getAttribute(attributeKey, limit + 1); 220 boolean changed = false; 221 // check for equal or both null, if NO return start 222 if (v1 != null) 223 { 224 changed = !v1.equals(v2); 225 } 226 else 227 { 228 changed = (v2 != null); 229 } 230 if (changed) 231 return limit + 1; 232 } 233 // no differences, so increment limit and next and loop again 234 limit++; 235 } 236 return end; 237 } 238 239 /*************************************************************************/ 240 241 /** 242 * Various methods that determine where the run begins for various 243 * attribute combinations. 244 */ 245 246 /** 247 * Returns the index of the first character in the run containing the current 248 * character and defined by all the attributes defined for that character 249 * position. 250 * 251 * @return The run start index. 252 */ getRunStart()253 public int getRunStart() 254 { 255 return(getRunStart(getAttributes().keySet())); 256 } 257 258 /** 259 * Returns the index of the first character in the run, defined by the 260 * specified attribute, that contains the current character. 261 * 262 * @param attrib the attribute (<code>null</code> permitted). 263 * 264 * return The index of the first character in the run. 265 */ getRunStart(Attribute attrib)266 public int getRunStart(Attribute attrib) 267 { 268 if (attrib == null) 269 return ci.getBeginIndex(); 270 HashSet<Attribute> s = new HashSet<Attribute>(); 271 s.add(attrib); 272 return(getRunStart(s)); 273 } 274 275 /** 276 * Returns the index of the first character in the run, defined by the 277 * specified attribute set, that contains the current character. 278 * 279 * @param attributeSet the attribute set (<code>null</code> permitted). 280 * 281 * return The index of the first character in the run. 282 */ getRunStart(Set<? extends Attribute> attributeSet)283 public int getRunStart(Set<? extends Attribute> attributeSet) 284 { 285 if (attributeSet == null) 286 return ci.getBeginIndex(); 287 288 int current = ci.getIndex(); 289 int begin = ci.getBeginIndex(); 290 int start = current; 291 if (start == begin) 292 return begin; 293 Map<Attribute, Object> runValues = getAttributes(); 294 int prev = start - 1; 295 while (start > begin) 296 { 297 Iterator<? extends Attribute> iterator = attributeSet.iterator(); 298 while (iterator.hasNext()) 299 { 300 Attribute attributeKey = iterator.next(); 301 Object v1 = runValues.get(attributeKey); 302 Object v2 = getAttribute(attributeKey, prev); 303 boolean changed = false; 304 // check for equal or both null, if NO return start 305 if (v1 != null) 306 { 307 changed = !v1.equals(v2); 308 } 309 else 310 { 311 changed = (v2 != null); 312 } 313 if (changed) 314 return start; 315 } 316 // no differences, so decrement start and prev and loop again 317 start--; 318 prev--; 319 } 320 return start; 321 } 322 323 /*************************************************************************/ 324 325 /** 326 * Returns the value for an attribute at the specified position. If the 327 * attribute key (<code>key</code>) is <code>null</code>, the method returns 328 * <code>null</code>. 329 * 330 * @param key the key (<code>null</code> permitted). 331 * @param pos the character position. 332 * 333 * @return The attribute value (possibly <code>null</code>). 334 */ getAttribute(AttributedCharacterIterator.Attribute key, int pos)335 private Object getAttribute(AttributedCharacterIterator.Attribute key, 336 int pos) 337 { 338 if (attribs == null) 339 return null; 340 for (int i = attribs.length - 1; i >= 0; i--) 341 { 342 if (pos >= attribs[i].beginIndex && pos < attribs[i].endIndex) 343 { 344 Set<? extends Attribute> keys = attribs[i].attribs.keySet(); 345 if (keys.contains(key)) 346 { 347 return attribs[i].attribs.get(key); 348 } 349 } 350 } 351 return null; 352 } 353 354 /** 355 * Returns the value for an attribute at the current position. If the 356 * attribute key (<code>key</code>) is <code>null</code>, the method returns 357 * <code>null</code>. 358 * 359 * @param key the key (<code>null</code> permitted). 360 * 361 * @return The attribute value (possibly <code>null</code>). 362 */ getAttribute(AttributedCharacterIterator.Attribute key)363 public Object getAttribute(AttributedCharacterIterator.Attribute key) 364 { 365 return getAttribute(key, ci.getIndex()); 366 } 367 368 /*************************************************************************/ 369 370 /** 371 * Return a list of all the attributes and values defined for this 372 * character 373 */ getAttributes()374 public Map<Attribute,Object> getAttributes() 375 { 376 HashMap<Attribute,Object> m = new HashMap<Attribute,Object>(); 377 if (attribs == null) 378 return(m); 379 380 for (int i = 0; i < attribs.length; i++) 381 { 382 if ((ci.getIndex() >= attribs[i].beginIndex) && 383 (ci.getIndex() < attribs[i].endIndex)) 384 m.putAll(attribs[i].attribs); 385 } 386 387 return(m); 388 } 389 390 } // class AttributedStringIterator 391