1/* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18package java.io; 19 20import java.nio.charset.Charset; 21import java.nio.charset.IllegalCharsetNameException; 22import java.util.Arrays; 23import java.util.Formatter; 24import java.util.IllegalFormatException; 25import java.util.Locale; 26 27/** 28 * Wraps an existing {@link OutputStream} and provides convenience methods for 29 * writing common data types in a human readable format. This is not to be 30 * confused with DataOutputStream which is used for encoding common data types 31 * so that they can be read back in. No {@code IOException} is thrown by this 32 * class. Instead, callers should use {@link #checkError()} to see if a problem 33 * has occurred in this stream. 34 */ 35public class PrintStream extends FilterOutputStream implements Appendable, Closeable { 36 /** 37 * indicates whether or not this PrintStream has incurred an error. 38 */ 39 private boolean ioError; 40 41 /** 42 * indicates whether or not this PrintStream should flush its contents after 43 * printing a new line. 44 */ 45 private boolean autoFlush; 46 47 private String encoding; 48 49 /** 50 * Constructs a new {@code PrintStream} with {@code out} as its target 51 * stream. By default, the new print stream does not automatically flush its 52 * contents to the target stream when a newline is encountered. 53 * 54 * @param out 55 * the target output stream. 56 * @throws NullPointerException 57 * if {@code out} is {@code null}. 58 */ 59 public PrintStream(OutputStream out) { 60 super(out); 61 if (out == null) { 62 throw new NullPointerException("out == null"); 63 } 64 } 65 66 /** 67 * Constructs a new {@code PrintStream} with {@code out} as its target 68 * stream. The parameter {@code autoFlush} determines if the print stream 69 * automatically flushes its contents to the target stream when a newline is 70 * encountered. 71 * 72 * @param out 73 * the target output stream. 74 * @param autoFlush 75 * indicates whether to flush contents upon encountering a 76 * newline sequence. 77 * @throws NullPointerException 78 * if {@code out} is {@code null}. 79 */ 80 public PrintStream(OutputStream out, boolean autoFlush) { 81 super(out); 82 if (out == null) { 83 throw new NullPointerException("out == null"); 84 } 85 this.autoFlush = autoFlush; 86 } 87 88 /** 89 * Constructs a new {@code PrintStream} with {@code out} as its target 90 * stream and using the character encoding {@code charsetName} while writing. The 91 * parameter {@code autoFlush} determines if the print stream automatically 92 * flushes its contents to the target stream when a newline is encountered. 93 * 94 * @param out 95 * the target output stream. 96 * @param autoFlush 97 * indicates whether or not to flush contents upon encountering a 98 * newline sequence. 99 * @param charsetName 100 * the non-null string describing the desired character encoding. 101 * @throws NullPointerException 102 * if {@code out} or {@code charsetName} are {@code null}. 103 * @throws UnsupportedEncodingException 104 * if the encoding specified by {@code charsetName} is not supported. 105 */ 106 public PrintStream(OutputStream out, boolean autoFlush, String charsetName) 107 throws UnsupportedEncodingException { 108 super(out); 109 if (out == null) { 110 throw new NullPointerException("out == null"); 111 } else if (charsetName == null) { 112 throw new NullPointerException("charsetName == null"); 113 } 114 this.autoFlush = autoFlush; 115 try { 116 if (!Charset.isSupported(charsetName)) { 117 throw new UnsupportedEncodingException(charsetName); 118 } 119 } catch (IllegalCharsetNameException e) { 120 throw new UnsupportedEncodingException(charsetName); 121 } 122 encoding = charsetName; 123 } 124 125 /** 126 * Constructs a new {@code PrintStream} with {@code file} as its target. The 127 * VM's default character set is used for character encoding. 128 * 129 * @param file 130 * the target file. If the file already exists, its contents are 131 * removed, otherwise a new file is created. 132 * @throws FileNotFoundException 133 * if an error occurs while opening or creating the target file. 134 */ 135 public PrintStream(File file) throws FileNotFoundException { 136 super(new FileOutputStream(file)); 137 } 138 139 /** 140 * Constructs a new {@code PrintStream} with {@code file} as its target. The 141 * character set named {@code charsetName} is used for character encoding. 142 * 143 * @param file 144 * the target file. If the file already exists, its contents are 145 * removed, otherwise a new file is created. 146 * @param charsetName 147 * the name of the character set used for character encoding. 148 * @throws FileNotFoundException 149 * if an error occurs while opening or creating the target file. 150 * @throws NullPointerException 151 * if {@code charsetName} is {@code null}. 152 * @throws UnsupportedEncodingException 153 * if the encoding specified by {@code charsetName} is not supported. 154 */ 155 public PrintStream(File file, String charsetName) throws FileNotFoundException, 156 UnsupportedEncodingException { 157 super(new FileOutputStream(file)); 158 if (charsetName == null) { 159 throw new NullPointerException("charsetName == null"); 160 } 161 if (!Charset.isSupported(charsetName)) { 162 throw new UnsupportedEncodingException(charsetName); 163 } 164 encoding = charsetName; 165 } 166 167 /** 168 * Constructs a new {@code PrintStream} with the file identified by 169 * {@code fileName} as its target. The VM's default character 170 * set is used for character encoding. 171 * 172 * @param fileName 173 * the target file's name. If the file already exists, its 174 * contents are removed, otherwise a new file is created. 175 * @throws FileNotFoundException 176 * if an error occurs while opening or creating the target file. 177 */ 178 public PrintStream(String fileName) throws FileNotFoundException { 179 this(new File(fileName)); 180 } 181 182 /** 183 * Constructs a new {@code PrintStream} with the file identified by 184 * {@code fileName} as its target. The character set named {@code charsetName} is 185 * used for character encoding. 186 * 187 * @param fileName 188 * the target file's name. If the file already exists, its 189 * contents are removed, otherwise a new file is created. 190 * @param charsetName 191 * the name of the character set used for character encoding. 192 * @throws FileNotFoundException 193 * if an error occurs while opening or creating the target file. 194 * @throws NullPointerException 195 * if {@code charsetName} is {@code null}. 196 * @throws UnsupportedEncodingException 197 * if the encoding specified by {@code charsetName} is not supported. 198 */ 199 public PrintStream(String fileName, String charsetName) 200 throws FileNotFoundException, UnsupportedEncodingException { 201 this(new File(fileName), charsetName); 202 } 203 204 /** 205 * Flushes this stream and returns the value of the error flag. 206 * 207 * @return {@code true} if either an {@code IOException} has been thrown 208 * previously or if {@code setError()} has been called; 209 * {@code false} otherwise. 210 * @see #setError() 211 */ 212 public boolean checkError() { 213 OutputStream delegate = out; 214 if (delegate == null) { 215 return ioError; 216 } 217 218 flush(); 219 return ioError || delegate.checkError(); 220 } 221 222 /** 223 * Sets the error state of the stream to false. 224 * @since 1.6 225 */ 226 protected void clearError() { 227 ioError = false; 228 } 229 230 /** 231 * Closes this print stream. Flushes this stream and then closes the target 232 * stream. If an I/O error occurs, this stream's error state is set to 233 * {@code true}. 234 */ 235 @Override 236 public synchronized void close() { 237 flush(); 238 if (out != null) { 239 try { 240 out.close(); 241 out = null; 242 } catch (IOException e) { 243 setError(); 244 } 245 } 246 } 247 248 /** 249 * Ensures that all pending data is sent out to the target stream. It also 250 * flushes the target stream. If an I/O error occurs, this stream's error 251 * state is set to {@code true}. 252 */ 253 @Override 254 public synchronized void flush() { 255 if (out != null) { 256 try { 257 out.flush(); 258 return; 259 } catch (IOException e) { 260 // Ignored, fall through to setError 261 } 262 } 263 setError(); 264 } 265 266 /** 267 * Formats {@code args} according to the format string {@code format}, and writes the result 268 * to this stream. This method uses the user's default locale. 269 * See "<a href="../util/Locale.html#default_locale">Be wary of the default locale</a>". 270 * 271 * @param format the format string (see {@link java.util.Formatter#format}) 272 * @param args 273 * the list of arguments passed to the formatter. If there are 274 * more arguments than required by {@code format}, 275 * additional arguments are ignored. 276 * @return this stream. 277 * @throws IllegalFormatException 278 * if the format string is illegal or incompatible with the 279 * arguments, if there are not enough arguments or if any other 280 * error regarding the format string or arguments is detected. 281 * @throws NullPointerException if {@code format == null} 282 */ 283 public PrintStream format(String format, Object... args) { 284 return format(Locale.getDefault(), format, args); 285 } 286 287 /** 288 * Writes a string formatted by an intermediate {@link Formatter} to this 289 * stream using the specified locale, format string and arguments. 290 * 291 * @param l 292 * the locale used in the method. No localization will be applied 293 * if {@code l} is {@code null}. 294 * @param format the format string (see {@link java.util.Formatter#format}) 295 * @param args 296 * the list of arguments passed to the formatter. If there are 297 * more arguments than required by {@code format}, 298 * additional arguments are ignored. 299 * @return this stream. 300 * @throws IllegalFormatException 301 * if the format string is illegal or incompatible with the 302 * arguments, if there are not enough arguments or if any other 303 * error regarding the format string or arguments is detected. 304 * @throws NullPointerException if {@code format == null} 305 */ 306 public PrintStream format(Locale l, String format, Object... args) { 307 if (format == null) { 308 throw new NullPointerException("format == null"); 309 } 310 new Formatter(this, l).format(format, args); 311 return this; 312 } 313 314 /** 315 * Prints a formatted string. The behavior of this method is the same as 316 * this stream's {@code #format(String, Object...)} method. 317 * 318 * <p>The {@code Locale} used is the user's default locale. 319 * See "<a href="../util/Locale.html#default_locale">Be wary of the default locale</a>". 320 * 321 * @param format the format string (see {@link java.util.Formatter#format}) 322 * @param args 323 * the list of arguments passed to the formatter. If there are 324 * more arguments than required by {@code format}, 325 * additional arguments are ignored. 326 * @return this stream. 327 * @throws IllegalFormatException 328 * if the format string is illegal or incompatible with the 329 * arguments, if there are not enough arguments or if any other 330 * error regarding the format string or arguments is detected. 331 * @throws NullPointerException if {@code format == null} 332 */ 333 public PrintStream printf(String format, Object... args) { 334 return format(format, args); 335 } 336 337 /** 338 * Prints a formatted string. The behavior of this method is the same as 339 * this stream's {@code #format(Locale, String, Object...)} method. 340 * 341 * @param l 342 * the locale used in the method. No localization will be applied 343 * if {@code l} is {@code null}. 344 * @param format the format string (see {@link java.util.Formatter#format}) 345 * @param args 346 * the list of arguments passed to the formatter. If there are 347 * more arguments than required by {@code format}, 348 * additional arguments are ignored. 349 * @return this stream. 350 * @throws IllegalFormatException 351 * if the format string is illegal or incompatible with the 352 * arguments, if there are not enough arguments or if any other 353 * error regarding the format string or arguments is detected. 354 * @throws NullPointerException if {@code format == null}. 355 */ 356 public PrintStream printf(Locale l, String format, Object... args) { 357 return format(l, format, args); 358 } 359 360 /** 361 * Put the line separator String onto the print stream. 362 */ 363 private void newline() { 364 print(System.lineSeparator()); 365 } 366 367 /** 368 * Prints the string representation of the character array {@code chars}. 369 */ 370 public void print(char[] chars) { 371 print(new String(chars, 0, chars.length)); 372 } 373 374 /** 375 * Prints the string representation of the char {@code c}. 376 */ 377 public void print(char c) { 378 print(String.valueOf(c)); 379 } 380 381 /** 382 * Prints the string representation of the double {@code d}. 383 */ 384 public void print(double d) { 385 print(String.valueOf(d)); 386 } 387 388 /** 389 * Prints the string representation of the float {@code f}. 390 */ 391 public void print(float f) { 392 print(String.valueOf(f)); 393 } 394 395 /** 396 * Prints the string representation of the int {@code i}. 397 */ 398 public void print(int i) { 399 print(String.valueOf(i)); 400 } 401 402 /** 403 * Prints the string representation of the long {@code l}. 404 */ 405 public void print(long l) { 406 print(String.valueOf(l)); 407 } 408 409 /** 410 * Prints the string representation of the Object {@code o}, or {@code "null"}. 411 */ 412 public void print(Object o) { 413 print(String.valueOf(o)); 414 } 415 416 /** 417 * Prints a string to the target stream. The string is converted to an array 418 * of bytes using the encoding chosen during the construction of this 419 * stream. The bytes are then written to the target stream with 420 * {@code write(int)}. 421 * 422 * <p>If an I/O error occurs, this stream's error state is set to {@code true}. 423 * 424 * @param str 425 * the string to print to the target stream. 426 * @see #write(int) 427 */ 428 public synchronized void print(String str) { 429 if (out == null) { 430 setError(); 431 return; 432 } 433 if (str == null) { 434 print("null"); 435 return; 436 } 437 438 try { 439 if (encoding == null) { 440 write(str.getBytes()); 441 } else { 442 write(str.getBytes(encoding)); 443 } 444 } catch (IOException e) { 445 setError(); 446 } 447 } 448 449 /** 450 * Prints the string representation of the boolean {@code b}. 451 */ 452 public void print(boolean b) { 453 print(String.valueOf(b)); 454 } 455 456 /** 457 * Prints a newline. 458 */ 459 public void println() { 460 newline(); 461 } 462 463 /** 464 * Prints the string representation of the character array {@code chars} followed by a newline. 465 */ 466 public void println(char[] chars) { 467 println(new String(chars, 0, chars.length)); 468 } 469 470 /** 471 * Prints the string representation of the char {@code c} followed by a newline. 472 */ 473 public void println(char c) { 474 println(String.valueOf(c)); 475 } 476 477 /** 478 * Prints the string representation of the double {@code d} followed by a newline. 479 */ 480 public void println(double d) { 481 println(String.valueOf(d)); 482 } 483 484 /** 485 * Prints the string representation of the float {@code f} followed by a newline. 486 */ 487 public void println(float f) { 488 println(String.valueOf(f)); 489 } 490 491 /** 492 * Prints the string representation of the int {@code i} followed by a newline. 493 */ 494 public void println(int i) { 495 println(String.valueOf(i)); 496 } 497 498 /** 499 * Prints the string representation of the long {@code l} followed by a newline. 500 */ 501 public void println(long l) { 502 println(String.valueOf(l)); 503 } 504 505 /** 506 * Prints the string representation of the Object {@code o}, or {@code "null"}, 507 * followed by a newline. 508 */ 509 public void println(Object o) { 510 println(String.valueOf(o)); 511 } 512 513 /** 514 * Prints a string followed by a newline. The string is converted to an array of bytes using 515 * the encoding chosen during the construction of this stream. The bytes are 516 * then written to the target stream with {@code write(int)}. 517 * 518 * <p>If an I/O error occurs, this stream's error state is set to {@code true}. 519 * 520 * @param str 521 * the string to print to the target stream. 522 * @see #write(int) 523 */ 524 public synchronized void println(String str) { 525 print(str); 526 newline(); 527 } 528 529 /** 530 * Prints the string representation of the boolean {@code b} followed by a newline. 531 */ 532 public void println(boolean b) { 533 println(String.valueOf(b)); 534 } 535 536 /** 537 * Sets the error flag of this print stream to true. 538 */ 539 protected void setError() { 540 ioError = true; 541 } 542 543 /** 544 * Writes {@code count} bytes from {@code buffer} starting at {@code offset} 545 * to the target stream. If autoFlush is set, this stream gets flushed after 546 * writing the buffer. 547 * 548 * <p>This stream's error flag is set to {@code true} if this stream is closed 549 * or an I/O error occurs. 550 * 551 * @param buffer 552 * the buffer to be written. 553 * @param offset 554 * the index of the first byte in {@code buffer} to write. 555 * @param length 556 * the number of bytes in {@code buffer} to write. 557 * @throws IndexOutOfBoundsException 558 * if {@code offset < 0} or {@code count < 0}, or if {@code 559 * offset + count} is bigger than the length of {@code buffer}. 560 * @see #flush() 561 */ 562 @Override 563 public void write(byte[] buffer, int offset, int length) { 564 Arrays.checkOffsetAndCount(buffer.length, offset, length); 565 synchronized (this) { 566 if (out == null) { 567 setError(); 568 return; 569 } 570 try { 571 out.write(buffer, offset, length); 572 if (autoFlush) { 573 flush(); 574 } 575 } catch (IOException e) { 576 setError(); 577 } 578 } 579 } 580 581 /** 582 * Writes one byte to the target stream. Only the least significant byte of 583 * the integer {@code oneByte} is written. This stream is flushed if 584 * {@code oneByte} is equal to the character {@code '\n'} and this stream is 585 * set to autoFlush. 586 * <p> 587 * This stream's error flag is set to {@code true} if it is closed or an I/O 588 * error occurs. 589 * 590 * @param oneByte 591 * the byte to be written 592 */ 593 @Override 594 public synchronized void write(int oneByte) { 595 if (out == null) { 596 setError(); 597 return; 598 } 599 try { 600 out.write(oneByte); 601 int b = oneByte & 0xFF; 602 // 0x0A is ASCII newline, 0x15 is EBCDIC newline. 603 boolean isNewline = b == 0x0A || b == 0x15; 604 if (autoFlush && isNewline) { 605 flush(); 606 } 607 } catch (IOException e) { 608 setError(); 609 } 610 } 611 612 /** 613 * Appends the char {@code c}. 614 * @return this stream. 615 */ 616 public PrintStream append(char c) { 617 print(c); 618 return this; 619 } 620 621 /** 622 * Appends the CharSequence {@code charSequence}, or {@code "null"}. 623 * @return this stream. 624 */ 625 public PrintStream append(CharSequence charSequence) { 626 if (charSequence == null) { 627 print("null"); 628 } else { 629 print(charSequence.toString()); 630 } 631 return this; 632 } 633 634 /** 635 * Appends a subsequence of CharSequence {@code charSequence}, or {@code "null"}. 636 * 637 * @param charSequence 638 * the character sequence appended to the target stream. 639 * @param start 640 * the index of the first char in the character sequence appended 641 * to the target stream. 642 * @param end 643 * the index of the character following the last character of the 644 * subsequence appended to the target stream. 645 * @return this stream. 646 * @throws IndexOutOfBoundsException 647 * if {@code start > end}, {@code start < 0}, {@code end < 0} or 648 * either {@code start} or {@code end} are greater or equal than 649 * the length of {@code charSequence}. 650 */ 651 public PrintStream append(CharSequence charSequence, int start, int end) { 652 if (charSequence == null) { 653 charSequence = "null"; 654 } 655 print(charSequence.subSequence(start, end).toString()); 656 return this; 657 } 658} 659