FastPrintWriter.java revision db4e33f1f1d766afa3218a6bbdbb561e7962c854
1package com.android.internal.util; 2 3import java.io.File; 4import java.io.FileNotFoundException; 5import java.io.IOException; 6import java.io.OutputStream; 7import java.io.PrintWriter; 8import java.io.UnsupportedEncodingException; 9import java.io.Writer; 10import java.nio.ByteBuffer; 11import java.nio.CharBuffer; 12import java.nio.charset.Charset; 13import java.nio.charset.CharsetEncoder; 14import java.nio.charset.CoderResult; 15import java.nio.charset.CodingErrorAction; 16 17public class FastPrintWriter extends PrintWriter { 18 private static final int BUFFER_LEN = 8192; 19 20 private final char[] mText = new char[BUFFER_LEN]; 21 private int mPos; 22 23 final private OutputStream mOutputStream; 24 private CharsetEncoder mCharset; 25 final private ByteBuffer mBytes = ByteBuffer.allocate(BUFFER_LEN); 26 27 /** 28 * Constructs a new {@code PrintWriter} with {@code out} as its target 29 * stream. By default, the new print writer does not automatically flush its 30 * contents to the target stream when a newline is encountered. 31 * 32 * @param out 33 * the target output stream. 34 * @throws NullPointerException 35 * if {@code out} is {@code null}. 36 */ 37 public FastPrintWriter(OutputStream out) { 38 super(out); 39 mOutputStream = out; 40 initDefaultEncoder(); 41 } 42 43 /** 44 * Constructs a new {@code PrintWriter} with {@code out} as its target 45 * stream. The parameter {@code autoFlush} determines if the print writer 46 * automatically flushes its contents to the target stream when a newline is 47 * encountered. 48 * 49 * @param out 50 * the target output stream. 51 * @param autoFlush 52 * indicates whether contents are flushed upon encountering a 53 * newline sequence. 54 * @throws NullPointerException 55 * if {@code out} is {@code null}. 56 */ 57 public FastPrintWriter(OutputStream out, boolean autoFlush) { 58 super(out, autoFlush); 59 mOutputStream = out; 60 initDefaultEncoder(); 61 } 62 63 /** 64 * Constructs a new {@code PrintWriter} with {@code wr} as its target 65 * writer. By default, the new print writer does not automatically flush its 66 * contents to the target writer when a newline is encountered. 67 * 68 * @param wr 69 * the target writer. 70 * @throws NullPointerException 71 * if {@code wr} is {@code null}. 72 */ 73 public FastPrintWriter(Writer wr) { 74 super(wr); 75 mOutputStream = null; 76 initDefaultEncoder(); 77 } 78 79 /** 80 * Constructs a new {@code PrintWriter} with {@code out} as its target 81 * writer. The parameter {@code autoFlush} determines if the print writer 82 * automatically flushes its contents to the target writer when a newline is 83 * encountered. 84 * 85 * @param wr 86 * the target writer. 87 * @param autoFlush 88 * indicates whether to flush contents upon encountering a 89 * newline sequence. 90 * @throws NullPointerException 91 * if {@code out} is {@code null}. 92 */ 93 public FastPrintWriter(Writer wr, boolean autoFlush) { 94 super(wr, autoFlush); 95 mOutputStream = null; 96 initDefaultEncoder(); 97 } 98 99 /** 100 * Constructs a new {@code PrintWriter} with {@code file} as its target. The 101 * VM's default character set is used for character encoding. 102 * The print writer does not automatically flush its contents to the target 103 * file when a newline is encountered. The output to the file is buffered. 104 * 105 * @param file 106 * the target file. If the file already exists, its contents are 107 * removed, otherwise a new file is created. 108 * @throws java.io.FileNotFoundException 109 * if an error occurs while opening or creating the target file. 110 */ 111 public FastPrintWriter(File file) throws FileNotFoundException { 112 super(file); 113 mOutputStream = null; 114 initDefaultEncoder(); 115 } 116 117 /** 118 * Constructs a new {@code PrintWriter} with {@code file} as its target. The 119 * character set named {@code csn} is used for character encoding. 120 * The print writer does not automatically flush its contents to the target 121 * file when a newline is encountered. The output to the file is buffered. 122 * 123 * @param file 124 * the target file. If the file already exists, its contents are 125 * removed, otherwise a new file is created. 126 * @param csn 127 * the name of the character set used for character encoding. 128 * @throws FileNotFoundException 129 * if an error occurs while opening or creating the target file. 130 * @throws NullPointerException 131 * if {@code csn} is {@code null}. 132 * @throws java.io.UnsupportedEncodingException 133 * if the encoding specified by {@code csn} is not supported. 134 */ 135 public FastPrintWriter(File file, String csn) throws FileNotFoundException, 136 UnsupportedEncodingException { 137 super(file, csn); 138 mOutputStream = null; 139 initEncoder(csn); 140 } 141 142 /** 143 * Constructs a new {@code PrintWriter} with the file identified by {@code 144 * fileName} as its target. The VM's default character set is 145 * used for character encoding. The print writer does not automatically 146 * flush its contents to the target file when a newline is encountered. The 147 * output to the file is buffered. 148 * 149 * @param fileName 150 * the target file's name. If the file already exists, its 151 * contents are removed, otherwise a new file is created. 152 * @throws FileNotFoundException 153 * if an error occurs while opening or creating the target file. 154 */ 155 public FastPrintWriter(String fileName) throws FileNotFoundException { 156 super(fileName); 157 mOutputStream = null; 158 initDefaultEncoder(); 159 } 160 161 /** 162 * Constructs a new {@code PrintWriter} with the file identified by {@code 163 * fileName} as its target. The character set named {@code csn} is used for 164 * character encoding. The print writer does not automatically flush its 165 * contents to the target file when a newline is encountered. The output to 166 * the file is buffered. 167 * 168 * @param fileName 169 * the target file's name. If the file already exists, its 170 * contents are removed, otherwise a new file is created. 171 * @param csn 172 * the name of the character set used for character encoding. 173 * @throws FileNotFoundException 174 * if an error occurs while opening or creating the target file. 175 * @throws NullPointerException 176 * if {@code csn} is {@code null}. 177 * @throws UnsupportedEncodingException 178 * if the encoding specified by {@code csn} is not supported. 179 */ 180 public FastPrintWriter(String fileName, String csn) 181 throws FileNotFoundException, UnsupportedEncodingException { 182 super(fileName, csn); 183 mOutputStream = null; 184 initEncoder(csn); 185 } 186 187 private final void initEncoder(String csn) throws UnsupportedEncodingException { 188 try { 189 mCharset = Charset.forName(csn).newEncoder(); 190 } catch (Exception e) { 191 throw new UnsupportedEncodingException(csn); 192 } 193 mCharset.onMalformedInput(CodingErrorAction.REPLACE); 194 mCharset.onUnmappableCharacter(CodingErrorAction.REPLACE); 195 } 196 197 private final void initDefaultEncoder() { 198 mCharset = Charset.defaultCharset().newEncoder(); 199 mCharset.onMalformedInput(CodingErrorAction.REPLACE); 200 mCharset.onUnmappableCharacter(CodingErrorAction.REPLACE); 201 } 202 203 private void appendInner(char c) throws IOException { 204 int pos = mPos; 205 if (pos >= (BUFFER_LEN-1)) { 206 flush(); 207 pos = mPos; 208 } 209 mText[pos] = c; 210 mPos = pos+1; 211 } 212 213 private void appendInner(String str, int i, final int length) throws IOException { 214 if (length > BUFFER_LEN) { 215 final int end = i + length; 216 while (i < end) { 217 int next = i + BUFFER_LEN; 218 appendInner(str, i, next<end ? BUFFER_LEN : (end-i)); 219 i = next; 220 } 221 return; 222 } 223 int pos = mPos; 224 if ((pos+length) > BUFFER_LEN) { 225 flush(); 226 pos = mPos; 227 } 228 str.getChars(i, i + length, mText, pos); 229 mPos = pos + length; 230 } 231 232 private void appendInner(char[] buf, int i, final int length) throws IOException { 233 if (length > BUFFER_LEN) { 234 final int end = i + length; 235 while (i < end) { 236 int next = i + BUFFER_LEN; 237 appendInner(buf, i, next < end ? BUFFER_LEN : (end - i)); 238 i = next; 239 } 240 return; 241 } 242 int pos = mPos; 243 if ((pos+length) > BUFFER_LEN) { 244 flush(); 245 pos = mPos; 246 } 247 System.arraycopy(buf, i, mText, pos, length); 248 mPos = pos + length; 249 } 250 251 private void flushBytesInner() throws IOException { 252 int position; 253 if ((position = mBytes.position()) > 0) { 254 mBytes.flip(); 255 mOutputStream.write(mBytes.array(), 0, position); 256 mBytes.clear(); 257 } 258 } 259 260 private void flushInner() throws IOException { 261 //Log.i("PackageManager", "flush mPos=" + mPos); 262 if (mPos > 0) { 263 if (mOutputStream != null) { 264 CharBuffer charBuffer = CharBuffer.wrap(mText, 0, mPos); 265 CoderResult result = mCharset.encode(charBuffer, mBytes, true); 266 while (true) { 267 if (result.isError()) { 268 throw new IOException(result.toString()); 269 } else if (result.isOverflow()) { 270 flushBytesInner(); 271 result = mCharset.encode(charBuffer, mBytes, true); 272 continue; 273 } 274 break; 275 } 276 flushBytesInner(); 277 mOutputStream.flush(); 278 } else { 279 out.write(mText, 0, mPos); 280 out.flush(); 281 } 282 mPos = 0; 283 } 284 } 285 286 /** 287 * Ensures that all pending data is sent out to the target. It also 288 * flushes the target. If an I/O error occurs, this writer's error 289 * state is set to {@code true}. 290 */ 291 @Override 292 public void flush() { 293 try { 294 flushInner(); 295 } catch (IOException e) { 296 } 297 super.flush(); 298 } 299 300 /** 301 * Prints the string representation of the specified character array 302 * to the target. 303 * 304 * @param charArray 305 * the character array to print to the target. 306 * @see #print(String) 307 */ 308 public void print(char[] charArray) { 309 try { 310 appendInner(charArray, 0, charArray.length); 311 } catch (IOException e) { 312 } 313 } 314 315 /** 316 * Prints the string representation of the specified character to the 317 * target. 318 * 319 * @param ch 320 * the character to print to the target. 321 * @see #print(String) 322 */ 323 public void print(char ch) { 324 try { 325 appendInner(ch); 326 } catch (IOException e) { 327 } 328 } 329 330 /** 331 * Prints a string to the target. The string is converted to an array of 332 * bytes using the encoding chosen during the construction of this writer. 333 * The bytes are then written to the target with {@code write(int)}. 334 * <p> 335 * If an I/O error occurs, this writer's error flag is set to {@code true}. 336 * 337 * @param str 338 * the string to print to the target. 339 * @see #write(int) 340 */ 341 public void print(String str) { 342 if (str == null) { 343 str = String.valueOf((Object) null); 344 } 345 try { 346 appendInner(str, 0, str.length()); 347 } catch (IOException e) { 348 } 349 } 350 351 /** 352 * Prints the string representation of the character array {@code chars} followed by a newline. 353 * Flushes this writer if the autoFlush flag is set to {@code true}. 354 */ 355 public void println(char[] chars) { 356 print(chars); 357 println(); 358 } 359 360 /** 361 * Prints the string representation of the char {@code c} followed by a newline. 362 * Flushes this writer if the autoFlush flag is set to {@code true}. 363 */ 364 public void println(char c) { 365 print(c); 366 println(); 367 } 368 369 /** 370 * Writes {@code count} characters from {@code buffer} starting at {@code 371 * offset} to the target. 372 * <p> 373 * This writer's error flag is set to {@code true} if this writer is closed 374 * or an I/O error occurs. 375 * 376 * @param buf 377 * the buffer to write to the target. 378 * @param offset 379 * the index of the first character in {@code buffer} to write. 380 * @param count 381 * the number of characters in {@code buffer} to write. 382 * @throws IndexOutOfBoundsException 383 * if {@code offset < 0} or {@code count < 0}, or if {@code 384 * offset + count} is greater than the length of {@code buf}. 385 */ 386 @Override 387 public void write(char[] buf, int offset, int count) { 388 try { 389 appendInner(buf, offset, count); 390 } catch (IOException e) { 391 } 392 } 393 394 /** 395 * Writes one character to the target. Only the two least significant bytes 396 * of the integer {@code oneChar} are written. 397 * <p> 398 * This writer's error flag is set to {@code true} if this writer is closed 399 * or an I/O error occurs. 400 * 401 * @param oneChar 402 * the character to write to the target. 403 */ 404 @Override 405 public void write(int oneChar) { 406 try { 407 appendInner((char) oneChar); 408 } catch (IOException e) { 409 } 410 } 411 412 /** 413 * Writes the characters from the specified string to the target. 414 * 415 * @param str 416 * the non-null string containing the characters to write. 417 */ 418 @Override 419 public void write(String str) { 420 try { 421 appendInner(str, 0, str.length()); 422 } catch (IOException e) { 423 } 424 } 425 426 /** 427 * Writes {@code count} characters from {@code str} starting at {@code 428 * offset} to the target. 429 * 430 * @param str 431 * the non-null string containing the characters to write. 432 * @param offset 433 * the index of the first character in {@code str} to write. 434 * @param count 435 * the number of characters from {@code str} to write. 436 * @throws IndexOutOfBoundsException 437 * if {@code offset < 0} or {@code count < 0}, or if {@code 438 * offset + count} is greater than the length of {@code str}. 439 */ 440 @Override 441 public void write(String str, int offset, int count) { 442 try { 443 appendInner(str, offset, count); 444 } catch (IOException e) { 445 } 446 } 447 448 /** 449 * Appends a subsequence of the character sequence {@code csq} to the 450 * target. This method works the same way as {@code 451 * PrintWriter.print(csq.subsequence(start, end).toString())}. If {@code 452 * csq} is {@code null}, then the specified subsequence of the string "null" 453 * will be written to the target. 454 * 455 * @param csq 456 * the character sequence appended to the target. 457 * @param start 458 * the index of the first char in the character sequence appended 459 * to the target. 460 * @param end 461 * the index of the character following the last character of the 462 * subsequence appended to the target. 463 * @return this writer. 464 * @throws StringIndexOutOfBoundsException 465 * if {@code start > end}, {@code start < 0}, {@code end < 0} or 466 * either {@code start} or {@code end} are greater or equal than 467 * the length of {@code csq}. 468 */ 469 @Override 470 public PrintWriter append(CharSequence csq, int start, int end) { 471 if (csq == null) { 472 csq = "null"; 473 } 474 String output = csq.subSequence(start, end).toString(); 475 write(output, 0, output.length()); 476 return this; 477 } 478} 479