1 /* FormatCharacter.java -- Implementation of AttributedCharacterIterator for 2 formatters. 3 Copyright (C) 1998, 1999, 2000, 2001, 2003 Free Software Foundation, Inc. 4 5 This file is part of GNU Classpath. 6 7 GNU Classpath is free software; you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 2, or (at your option) 10 any later version. 11 12 GNU Classpath is distributed in the hope that it will be useful, but 13 WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with GNU Classpath; see the file COPYING. If not, write to the 19 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 20 02111-1307 USA. 21 22 Linking this library statically or dynamically with other modules is 23 making a combined work based on this library. Thus, the terms and 24 conditions of the GNU General Public License cover the whole 25 combination. 26 27 As a special exception, the copyright holders of this library give you 28 permission to link this library with independent modules to produce an 29 executable, regardless of the license terms of these independent 30 modules, and to copy and distribute the resulting executable under 31 terms of your choice, provided that you also meet, for each linked 32 independent module, the terms and conditions of the license of that 33 module. An independent module is a module which is not derived from 34 or based on this library. If you modify this library, you may extend 35 this exception to your version of the library, but you are not 36 obligated to do so. If you do not wish to do so, delete this 37 exception statement from your version. */ 38 package java.text; 39 40 import java.util.Set; 41 import java.util.HashSet; 42 import java.util.Map; 43 import java.util.HashMap; 44 import java.util.Vector; 45 46 47 /** 48 * This class should not be put public and it is only intended to the 49 * classes of the java.text package. Its aim is to build a segmented 50 * character iterator by appending strings and adding attributes to 51 * portions of strings. The code intends to do some optimization 52 * concerning memory consumption and attribute access but at the 53 * end it is only an AttributedCharacterIterator. 54 * 55 * @author Guilhem Lavaux <guilhem@kaffe.org> 56 * @date November 22, 2003 57 */ 58 class FormatCharacterIterator implements AttributedCharacterIterator 59 { 60 private String formattedString; 61 private int charIndex; 62 private int attributeIndex; 63 private int[] ranges; 64 private HashMap[] attributes; 65 66 /** 67 * This constructor builds an empty iterated strings. The attributes 68 * are empty and so is the string. However you may append strings 69 * and attributes to this iterator. 70 */ FormatCharacterIterator()71 FormatCharacterIterator() 72 { 73 formattedString = ""; 74 ranges = new int[0]; 75 attributes = new HashMap[0]; 76 } 77 78 /** 79 * This constructor take a string <code>s</code>, a set of ranges 80 * and the corresponding attributes. This is used to build an iterator. 81 * The array <code>ranges</code> should be formatted as follow: 82 * each element of <code>ranges</code> specifies the index in the string 83 * until which the corresponding map of attributes at the same position 84 * is applied. For example, if you have: 85 * <pre> 86 * s = "hello"; 87 * ranges = new int[] { 2, 6 }; 88 * attributes = new HashMap[2]; 89 * </pre> 90 * <code>"he"</code> will have the attributes <code>attributes[0]</code>, 91 * <code>"llo"</code> the <code>attributes[1]</code>. 92 */ FormatCharacterIterator(String s, int[] ranges, HashMap[] attributes)93 FormatCharacterIterator (String s, int[] ranges, HashMap[] attributes) 94 { 95 formattedString = s; 96 this.ranges = ranges; 97 this.attributes = attributes; 98 } 99 100 /* 101 * The following methods are inherited from AttributedCharacterIterator, 102 * and thus are already documented. 103 */ 104 getAllAttributeKeys()105 public Set getAllAttributeKeys() 106 { 107 if (attributes != null && attributes[attributeIndex] != null) 108 return attributes[attributeIndex].keySet(); 109 else 110 return new HashSet(); 111 } 112 getAttributes()113 public Map getAttributes() 114 { 115 if (attributes != null && attributes[attributeIndex] != null) 116 return attributes[attributeIndex]; 117 else 118 return new HashMap(); 119 } 120 getAttribute(AttributedCharacterIterator.Attribute attrib)121 public Object getAttribute (AttributedCharacterIterator.Attribute attrib) 122 { 123 if (attributes != null && attributes[attributeIndex] != null) 124 return attributes[attributeIndex].get (attrib); 125 else 126 return null; 127 } 128 getRunLimit(Set reqAttrs)129 public int getRunLimit(Set reqAttrs) 130 { 131 if (attributes == null) 132 return formattedString.length(); 133 134 int currentAttrIndex = attributeIndex; 135 Set newKeys; 136 137 do 138 { 139 currentAttrIndex++; 140 if (currentAttrIndex == attributes.length) 141 return formattedString.length(); 142 if (attributes[currentAttrIndex] == null) 143 break; 144 newKeys = attributes[currentAttrIndex].keySet(); 145 } 146 while (newKeys.containsAll (reqAttrs)); 147 148 return ranges[currentAttrIndex-1]; 149 } 150 getRunLimit(AttributedCharacterIterator.Attribute attribute)151 public int getRunLimit (AttributedCharacterIterator.Attribute attribute) 152 { 153 Set s = new HashSet(); 154 155 s.add (attribute); 156 return getRunLimit (s); 157 } 158 getRunLimit()159 public int getRunLimit() 160 { 161 if (attributes == null) 162 return formattedString.length(); 163 if (attributes[attributeIndex] == null) 164 { 165 for (int i=attributeIndex+1;i<attributes.length;i++) 166 if (attributes[i] != null) 167 return ranges[i-1]; 168 return formattedString.length(); 169 } 170 171 return getRunLimit (attributes[attributeIndex].keySet()); 172 } 173 getRunStart(Set reqAttrs)174 public int getRunStart (Set reqAttrs) 175 { 176 if (attributes == null) 177 return formattedString.length(); 178 179 int currentAttrIndex = attributeIndex; 180 Set newKeys = null; 181 182 do 183 { 184 if (currentAttrIndex == 0) 185 return 0; 186 187 currentAttrIndex--; 188 if (attributes[currentAttrIndex] == null) 189 break; 190 newKeys = attributes[currentAttrIndex].keySet(); 191 } 192 while (newKeys.containsAll (reqAttrs)); 193 194 return (currentAttrIndex > 0) ? ranges[currentAttrIndex-1] : 0; 195 } 196 getRunStart()197 public int getRunStart() 198 { 199 if (attributes == null) 200 return 0; 201 202 if (attributes[attributeIndex] == null) 203 { 204 for (int i=attributeIndex;i>0;i--) 205 if (attributes[i] != null) 206 return ranges[attributeIndex-1]; 207 return 0; 208 } 209 210 return getRunStart (attributes[attributeIndex].keySet()); 211 } 212 getRunStart(AttributedCharacterIterator.Attribute attribute)213 public int getRunStart (AttributedCharacterIterator.Attribute attribute) 214 { 215 Set s = new HashSet(); 216 217 s.add (attribute); 218 return getRunStart (s); 219 } 220 clone()221 public Object clone() 222 { 223 return new FormatCharacterIterator (formattedString, ranges, attributes); 224 } 225 226 /* 227 * The following methods are inherited from CharacterIterator and thus 228 * are already documented. 229 */ 230 current()231 public char current() 232 { 233 return formattedString.charAt (charIndex); 234 } 235 first()236 public char first() 237 { 238 charIndex = 0; 239 attributeIndex = 0; 240 return formattedString.charAt (0); 241 } 242 getBeginIndex()243 public int getBeginIndex() 244 { 245 return 0; 246 } 247 getEndIndex()248 public int getEndIndex() 249 { 250 return formattedString.length(); 251 } 252 getIndex()253 public int getIndex() 254 { 255 return charIndex; 256 } 257 last()258 public char last() 259 { 260 charIndex = formattedString.length()-1; 261 if (attributes != null) 262 attributeIndex = attributes.length-1; 263 return formattedString.charAt (charIndex); 264 } 265 next()266 public char next() 267 { 268 charIndex++; 269 if (charIndex >= formattedString.length()) 270 { 271 charIndex = getEndIndex(); 272 return DONE; 273 } 274 if (attributes != null) 275 { 276 if (charIndex >= ranges[attributeIndex]) 277 attributeIndex++; 278 } 279 return formattedString.charAt (charIndex); 280 } 281 previous()282 public char previous() 283 { 284 charIndex--; 285 if (charIndex < 0) 286 { 287 charIndex = 0; 288 return DONE; 289 } 290 291 if (attributes != null) 292 { 293 if (charIndex < ranges[attributeIndex]) 294 attributeIndex--; 295 } 296 return formattedString.charAt (charIndex); 297 } 298 setIndex(int position)299 public char setIndex (int position) 300 { 301 if (position < 0 || position > formattedString.length()) 302 throw new IllegalArgumentException ("position is out of range"); 303 304 charIndex = position; 305 if (attributes != null) 306 { 307 for (attributeIndex=0;attributeIndex<attributes.length; 308 attributeIndex++) 309 if (ranges[attributeIndex] > charIndex) 310 break; 311 attributeIndex--; 312 } 313 if (charIndex == formattedString.length()) 314 return DONE; 315 else 316 return formattedString.charAt (charIndex); 317 } 318 319 /** 320 * This method merge the specified attributes and ranges with the 321 * internal tables. This method is in charge of the optimization 322 * of tables. Two following sets of attributes are never the same. 323 * 324 * @see #FormatCharacterIterator() 325 * 326 * @param attributes the new array attributes to apply to the string. 327 */ mergeAttributes(HashMap[] attributes, int[] ranges)328 protected void mergeAttributes (HashMap[] attributes, int[] ranges) 329 { 330 Vector new_ranges = new Vector(); 331 Vector new_attributes = new Vector(); 332 int i = 0, j = 0; 333 334 while (i < this.ranges.length && j < ranges.length) 335 { 336 if (this.attributes[i] != null) 337 { 338 new_attributes.add (this.attributes[i]); 339 if (attributes[j] != null) 340 this.attributes[i].putAll (attributes[j]); 341 } 342 else 343 { 344 new_attributes.add (attributes[j]); 345 } 346 if (this.ranges[i] == ranges[j]) 347 { 348 new_ranges.add (new Integer (ranges[j])); 349 i++; 350 j++; 351 } 352 else if (this.ranges[i] < ranges[j]) 353 { 354 new_ranges.add (new Integer (this.ranges[i])); 355 i++; 356 } 357 else 358 { 359 new_ranges.add (new Integer (ranges[j])); 360 j++; 361 } 362 } 363 364 if (i != this.ranges.length) 365 { 366 for (;i<this.ranges.length;i++) 367 { 368 new_attributes.add (this.attributes[i]); 369 new_ranges.add (new Integer (this.ranges[i])); 370 } 371 } 372 if (j != ranges.length) 373 { 374 for (;j<ranges.length;j++) 375 { 376 new_attributes.add (attributes[j]); 377 new_ranges.add (new Integer (ranges[j])); 378 } 379 } 380 381 this.attributes = new HashMap[new_attributes.size()]; 382 this.ranges = new int[new_ranges.size()]; 383 System.arraycopy (new_attributes.toArray(), 0, this.attributes, 384 0, this.attributes.length); 385 386 for (i=0;i<new_ranges.size();i++) 387 { 388 this.ranges[i] = ((Integer)new_ranges.elementAt (i)).intValue(); 389 } 390 391 } 392 393 /** 394 * This method appends to the internal attributed string the attributed 395 * string contained in the specified iterator. 396 * 397 * @param iterator the iterator which contains the attributed string to 398 * append to this iterator. 399 */ append(AttributedCharacterIterator iterator)400 protected void append (AttributedCharacterIterator iterator) 401 { 402 char c = iterator.first(); 403 Vector more_ranges = new Vector(); 404 Vector more_attributes = new Vector(); 405 406 do 407 { 408 formattedString = formattedString + String.valueOf(c); 409 // TODO: Reduce the size of the output array. 410 more_attributes.add (iterator.getAttributes()); 411 more_ranges.add (new Integer (formattedString.length())); 412 // END TOOD 413 c = iterator.next(); 414 } 415 while (c != DONE); 416 417 HashMap[] new_attributes = new HashMap[attributes.length 418 + more_attributes.size()]; 419 int[] new_ranges = new int[ranges.length + more_ranges.size()]; 420 421 System.arraycopy (attributes, 0, new_attributes, 0, attributes.length); 422 System.arraycopy (more_attributes.toArray(), 0, new_attributes, 423 attributes.length, more_attributes.size()); 424 425 System.arraycopy (ranges, 0, new_ranges, 0, ranges.length); 426 Object[] new_ranges_array = more_ranges.toArray(); 427 for (int i=0;i<more_ranges.size();i++) 428 new_ranges[i+ranges.length] = ((Integer)new_ranges_array[i]).intValue(); 429 430 attributes = new_attributes; 431 ranges = new_ranges; 432 } 433 434 /** 435 * This method appends an attributed string which attributes are specified 436 * directly in the calling parameters. 437 * 438 * @param text The string to append. 439 * @param local_attributes The attributes to put on this string in the 440 * iterator. If it is <code>null</code> the string will simply have no 441 * attributes. 442 */ append(String text, HashMap local_attributes)443 protected void append (String text, HashMap local_attributes) 444 { 445 int[] new_ranges = new int[ranges.length+1]; 446 HashMap[] new_attributes = new HashMap[attributes.length+1]; 447 448 formattedString += text; 449 System.arraycopy (attributes, 0, new_attributes, 0, attributes.length); 450 System.arraycopy (ranges, 0, new_ranges, 0, ranges.length); 451 new_ranges[ranges.length] = formattedString.length(); 452 new_attributes[attributes.length] = local_attributes; 453 454 ranges = new_ranges; 455 attributes = new_attributes; 456 } 457 458 /** 459 * This method appends a string without attributes. It is completely 460 * equivalent to call {@link #append(String,HashMap)} with local_attributes 461 * equal to <code>null</code>. 462 * 463 * @param text The string to append to the iterator. 464 */ append(String text)465 protected void append (String text) 466 { 467 append (text, null); 468 } 469 } 470