blob: 962b58f18224ed8befb3dee8899b7caa2ab33abc [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package java.io;
import java.nio.charset.Charset;
import java.nio.charset.IllegalCharsetNameException;
import java.security.AccessController;
import java.util.Formatter;
import java.util.IllegalFormatException;
import java.util.Locale;
import org.apache.harmony.luni.util.Msg;
import org.apache.harmony.luni.util.PriviAction;
/**
* Wraps an existing {@link OutputStream} and provides convenience methods for
* writing common data types in a human readable format. This is not to be
* confused with DataOutputStream which is used for encoding common data types
* so that they can be read back in. No {@code IOException} is thrown by this
* class. Instead, callers should use {@link #checkError()} to see if a problem
* has occurred in this stream.
*/
public class PrintStream extends FilterOutputStream implements Appendable,
Closeable {
private static final String TOKEN_NULL = "null"; //$NON-NLS-1$
/**
* indicates whether or not this PrintStream has incurred an error.
*/
private boolean ioError;
/**
* indicates whether or not this PrintStream should flush its contents after
* printing a new line.
*/
private boolean autoflush;
private String encoding;
private final String lineSeparator = AccessController
.doPrivileged(new PriviAction<String>("line.separator")); //$NON-NLS-1$
// private Formatter formatter;
/**
* Constructs a new {@code PrintStream} with {@code out} as its target
* stream. By default, the new print stream does not automatically flush its
* contents to the target stream when a newline is encountered.
*
* @param out
* the target output stream.
* @throws NullPointerException
* if {@code out} is {@code null}.
*/
public PrintStream(OutputStream out) {
super(out);
if (out == null) {
throw new NullPointerException();
}
}
/**
* Constructs a new {@code PrintStream} with {@code out} as its target
* stream. The parameter {@code autoflush} determines if the print stream
* automatically flushes its contents to the target stream when a newline is
* encountered.
*
* @param out
* the target output stream.
* @param autoflush
* indicates whether to flush contents upon encountering a
* newline sequence.
* @throws NullPointerException
* if {@code out} is {@code null}.
*/
public PrintStream(OutputStream out, boolean autoflush) {
super(out);
if (out == null) {
throw new NullPointerException();
}
this.autoflush = autoflush;
}
/**
* Constructs a new {@code PrintStream} with {@code out} as its target
* stream and using the character encoding {@code enc} while writing. The
* parameter {@code autoflush} determines if the print stream automatically
* flushes its contents to the target stream when a newline is encountered.
*
* @param out
* the target output stream.
* @param autoflush
* indicates whether or not to flush contents upon encountering a
* newline sequence.
* @param enc
* the non-null string describing the desired character encoding.
* @throws NullPointerException
* if {@code out} or {@code enc} are {@code null}.
* @throws UnsupportedEncodingException
* if the encoding specified by {@code enc} is not supported.
*/
public PrintStream(OutputStream out, boolean autoflush, String enc)
throws UnsupportedEncodingException {
super(out);
if (out == null || enc == null) {
throw new NullPointerException();
}
this.autoflush = autoflush;
try {
if (!Charset.isSupported(enc)) {
throw new UnsupportedEncodingException(enc);
}
} catch (IllegalCharsetNameException e) {
throw new UnsupportedEncodingException(enc);
}
encoding = enc;
}
/**
* Constructs a new {@code PrintStream} with {@code file} as its target. The
* virtual machine's default character set is used for character encoding.
*
* @param file
* the target file. If the file already exists, its contents are
* removed, otherwise a new file is created.
* @throws FileNotFoundException
* if an error occurs while opening or creating the target file.
* @throws SecurityException
* if a security manager exists and it denies writing to the
* target file.
*/
public PrintStream(File file) throws FileNotFoundException {
super(new FileOutputStream(file));
}
/**
* Constructs a new {@code PrintStream} with {@code file} as its target. The
* character set named {@code csn} is used for character encoding.
*
* @param file
* the target file. If the file already exists, its contents are
* removed, otherwise a new file is created.
* @param csn
* the name of the character set used for character encoding.
* @throws FileNotFoundException
* if an error occurs while opening or creating the target file.
* @throws NullPointerException
* if {@code csn} is {@code null}.
* @throws SecurityException
* if a security manager exists and it denies writing to the
* target file.
* @throws UnsupportedEncodingException
* if the encoding specified by {@code csn} is not supported.
*/
public PrintStream(File file, String csn) throws FileNotFoundException,
UnsupportedEncodingException {
super(new FileOutputStream(file));
if (csn == null) {
throw new NullPointerException();
}
if (!Charset.isSupported(csn)) {
throw new UnsupportedEncodingException();
}
encoding = csn;
}
/**
* Constructs a new {@code PrintStream} with the file identified by
* {@code fileName} as its target. The virtual machine's default character
* set is used for character encoding.
*
* @param fileName
* the target file's name. If the file already exists, its
* contents are removed, otherwise a new file is created.
* @throws FileNotFoundException
* if an error occurs while opening or creating the target file.
* @throws SecurityException
* if a security manager exists and it denies writing to the
* target file.
*/
public PrintStream(String fileName) throws FileNotFoundException {
this(new File(fileName));
}
/**
* Constructs a new {@code PrintStream} with the file identified by
* {@code fileName} as its target. The character set named {@code csn} is
* used for character encoding.
*
* @param fileName
* the target file's name. If the file already exists, its
* contents are removed, otherwise a new file is created.
* @param csn
* the name of the character set used for character encoding.
* @throws FileNotFoundException
* if an error occurs while opening or creating the target file.
* @throws NullPointerException
* if {@code csn} is {@code null}.
* @throws SecurityException
* if a security manager exists and it denies writing to the
* target file.
* @throws UnsupportedEncodingException
* if the encoding specified by {@code csn} is not supported.
*/
public PrintStream(String fileName, String csn)
throws FileNotFoundException, UnsupportedEncodingException {
this(new File(fileName), csn);
}
/**
* Flushes this stream and returns the value of the error flag.
*
* @return {@code true} if either an {@code IOException} has been thrown
* previously or if {@code setError()} has been called;
* {@code false} otherwise.
* @see #setError()
*/
public boolean checkError() {
OutputStream delegate = out;
if (delegate == null) {
return ioError;
}
flush();
return ioError || delegate.checkError();
}
/**
* Sets the error state of the stream to false.
* @since 1.6
* @hide
*/
protected void clearError() {
ioError = false;
}
/**
* Closes this print stream. Flushes this stream and then closes the target
* stream. If an I/O error occurs, this stream's error state is set to
* {@code true}.
*/
@Override
public synchronized void close() {
flush();
if (out != null) {
try {
out.close();
out = null;
} catch (IOException e) {
setError();
}
}
}
/**
* Ensures that all pending data is sent out to the target stream. It also
* flushes the target stream. If an I/O error occurs, this stream's error
* state is set to {@code true}.
*/
@Override
public synchronized void flush() {
if (out != null) {
try {
out.flush();
return;
} catch (IOException e) {
// Ignored, fall through to setError
}
}
setError();
}
/**
* Formats {@code args} according to the format string {@code format}, and writes the result
* to this stream. This method uses the user's default locale.
* See "<a href="../util/Locale.html#default_locale">Be wary of the default locale</a>".
*
* @param format the format string (see {@link java.util.Formatter#format})
* @param args
* the list of arguments passed to the formatter. If there are
* more arguments than required by {@code format},
* additional arguments are ignored.
* @return this stream.
* @throws IllegalFormatException
* if the format string is illegal or incompatible with the
* arguments, if there are not enough arguments or if any other
* error regarding the format string or arguments is detected.
* @throws NullPointerException if {@code format == null}
*/
public PrintStream format(String format, Object... args) {
return format(Locale.getDefault(), format, args);
}
/**
* Writes a string formatted by an intermediate {@link Formatter} to this
* stream using the specified locale, format string and arguments.
*
* @param l
* the locale used in the method. No localization will be applied
* if {@code l} is {@code null}.
* @param format the format string (see {@link java.util.Formatter#format})
* @param args
* the list of arguments passed to the formatter. If there are
* more arguments than required by {@code format},
* additional arguments are ignored.
* @return this stream.
* @throws IllegalFormatException
* if the format string is illegal or incompatible with the
* arguments, if there are not enough arguments or if any other
* error regarding the format string or arguments is detected.
* @throws NullPointerException if {@code format == null}
*/
public PrintStream format(Locale l, String format, Object... args) {
if (format == null) {
throw new NullPointerException(Msg.getString("K0351")); //$NON-NLS-1$
}
new Formatter(this, l).format(format, args);
return this;
}
/**
* Prints a formatted string. The behavior of this method is the same as
* this stream's {@code #format(String, Object...)} method. For the locale,
* the default value of the current virtual machine instance is used.
*
* @param format the format string (see {@link java.util.Formatter#format})
* @param args
* the list of arguments passed to the formatter. If there are
* more arguments than required by {@code format},
* additional arguments are ignored.
* @return this stream.
* @throws IllegalFormatException
* if the format string is illegal or incompatible with the
* arguments, if there are not enough arguments or if any other
* error regarding the format string or arguments is detected.
* @throws NullPointerException if {@code format == null}
*/
public PrintStream printf(String format, Object... args) {
return format(format, args);
}
/**
* Prints a formatted string. The behavior of this method is the same as
* this stream's {@code #format(Locale, String, Object...)} method.
*
* @param l
* the locale used in the method. No localization will be applied
* if {@code l} is {@code null}.
* @param format the format string (see {@link java.util.Formatter#format})
* @param args
* the list of arguments passed to the formatter. If there are
* more arguments than required by {@code format},
* additional arguments are ignored.
* @return this stream.
* @throws IllegalFormatException
* if the format string is illegal or incompatible with the
* arguments, if there are not enough arguments or if any other
* error regarding the format string or arguments is detected.
* @throws NullPointerException if {@code format == null}.
*/
public PrintStream printf(Locale l, String format, Object... args) {
return format(l, format, args);
}
/**
* Put the line separator String onto the print stream.
*/
private void newline() {
print(lineSeparator);
}
/**
* Prints the string representation of the specified character array
* to the target stream.
*
* @param charArray
* the character array to print to the target stream.
* @see #print(String)
*/
public void print(char[] charArray) {
print(new String(charArray, 0, charArray.length));
}
/**
* Prints the string representation of the specified character to the target
* stream.
*
* @param ch
* the character to print to the target stream.
* @see #print(String)
*/
public void print(char ch) {
print(String.valueOf(ch));
}
/**
* Prints the string representation of the specified double to the target
* stream.
*
* @param dnum
* the double value to print to the target stream.
* @see #print(String)
*/
public void print(double dnum) {
print(String.valueOf(dnum));
}
/**
* Prints the string representation of the specified float to the target
* stream.
*
* @param fnum
* the float value to print to the target stream.
* @see #print(String)
*/
public void print(float fnum) {
print(String.valueOf(fnum));
}
/**
* Prints the string representation of the specified integer to the target
* stream.
*
* @param inum
* the integer value to print to the target stream.
* @see #print(String)
*/
public void print(int inum) {
print(String.valueOf(inum));
}
/**
* Prints the string representation of the specified long to the target
* stream.
*
* @param lnum
* the long value to print to the target stream.
* @see #print(String)
*/
public void print(long lnum) {
print(String.valueOf(lnum));
}
/**
* Prints the string representation of the specified object to the target
* stream.
*
* @param obj
* the object to print to the target stream.
* @see #print(String)
*/
public void print(Object obj) {
print(String.valueOf(obj));
}
/**
* Prints a string to the target stream. The string is converted to an array
* of bytes using the encoding chosen during the construction of this
* stream. The bytes are then written to the target stream with
* {@code write(int)}.
* <p>
* If an I/O error occurs, this stream's error state is set to {@code true}.
*
* @param str
* the string to print to the target stream.
* @see #write(int)
*/
public synchronized void print(String str) {
if (out == null) {
setError();
return;
}
if (str == null) {
print("null"); //$NON-NLS-1$
return;
}
try {
if (encoding == null) {
write(str.getBytes());
} else {
write(str.getBytes(encoding));
}
} catch (IOException e) {
setError();
}
}
/**
* Prints the string representation of the specified boolean to the target
* stream.
*
* @param bool
* the boolean value to print the target stream.
* @see #print(String)
*/
public void print(boolean bool) {
print(String.valueOf(bool));
}
/**
* Prints the string representation of the system property
* {@code "line.separator"} to the target stream.
*/
public void println() {
newline();
}
/**
* Prints the string representation of the specified character array
* followed by the system property {@code "line.separator"} to the target
* stream.
*
* @param charArray
* the character array to print to the target stream.
* @see #print(String)
*/
public void println(char[] charArray) {
println(new String(charArray, 0, charArray.length));
}
/**
* Prints the string representation of the specified character followed by
* the system property {@code "line.separator"} to the target stream.
*
* @param ch
* the character to print to the target stream.
* @see #print(String)
*/
public void println(char ch) {
println(String.valueOf(ch));
}
/**
* Prints the string representation of the specified double followed by the
* system property {@code "line.separator"} to the target stream.
*
* @param dnum
* the double value to print to the target stream.
* @see #print(String)
*/
public void println(double dnum) {
println(String.valueOf(dnum));
}
/**
* Prints the string representation of the specified float followed by the
* system property {@code "line.separator"} to the target stream.
*
* @param fnum
* the float value to print to the target stream.
* @see #print(String)
*/
public void println(float fnum) {
println(String.valueOf(fnum));
}
/**
* Prints the string representation of the specified integer followed by the
* system property {@code "line.separator"} to the target stream.
*
* @param inum
* the integer value to print to the target stream.
* @see #print(String)
*/
public void println(int inum) {
println(String.valueOf(inum));
}
/**
* Prints the string representation of the specified long followed by the
* system property {@code "line.separator"} to the target stream.
*
* @param lnum
* the long value to print to the target stream.
* @see #print(String)
*/
public void println(long lnum) {
println(String.valueOf(lnum));
}
/**
* Prints the string representation of the specified object followed by the
* system property {@code "line.separator"} to the target stream.
*
* @param obj
* the object to print to the target stream.
* @see #print(String)
*/
public void println(Object obj) {
println(String.valueOf(obj));
}
/**
* Prints a string followed by the system property {@code "line.separator"}
* to the target stream. The string is converted to an array of bytes using
* the encoding chosen during the construction of this stream. The bytes are
* then written to the target stream with {@code write(int)}.
* <p>
* If an I/O error occurs, this stream's error state is set to {@code true}.
* </p>
*
* @param str
* the string to print to the target stream.
* @see #write(int)
*/
public synchronized void println(String str) {
print(str);
newline();
}
/**
* Prints the string representation of the specified boolean followed by the
* system property {@code "line.separator"} to the target stream.
*
* @param bool
* the boolean value to print to the target stream.
* @see #print(String)
*/
public void println(boolean bool) {
println(String.valueOf(bool));
}
/**
* Sets the error flag of this print stream to true.
*/
protected void setError() {
ioError = true;
}
/**
* Writes {@code count} bytes from {@code buffer} starting at {@code offset}
* to the target stream. If autoflush is set, this stream gets flushed after
* writing the buffer.
* <p>
* This stream's error flag is set to {@code true} if this stream is closed
* or an I/O error occurs.
*
* @param buffer
* the buffer to be written.
* @param offset
* the index of the first byte in {@code buffer} to write.
* @param length
* the number of bytes in {@code buffer} to write.
* @throws IndexOutOfBoundsException
* if {@code offset < 0} or {@code count < 0}, or if {@code
* offset + count} is bigger than the length of {@code buffer}.
* @see #flush()
*/
@Override
public void write(byte[] buffer, int offset, int length) {
// Force buffer null check first!
if (offset > buffer.length || offset < 0) {
// K002e=Offset out of bounds \: {0}
throw new ArrayIndexOutOfBoundsException(Msg.getString("K002e", offset)); //$NON-NLS-1$
}
if (length < 0 || length > buffer.length - offset) {
// K0031=Length out of bounds \: {0}
throw new ArrayIndexOutOfBoundsException(Msg.getString("K0031", length)); //$NON-NLS-1$
}
synchronized (this) {
if (out == null) {
setError();
return;
}
try {
out.write(buffer, offset, length);
if (autoflush) {
flush();
}
} catch (IOException e) {
setError();
}
}
}
/**
* Writes one byte to the target stream. Only the least significant byte of
* the integer {@code oneByte} is written. This stream is flushed if
* {@code oneByte} is equal to the character {@code '\n'} and this stream is
* set to autoflush.
* <p>
* This stream's error flag is set to {@code true} if it is closed or an I/O
* error occurs.
*
* @param oneByte
* the byte to be written
*/
@Override
public synchronized void write(int oneByte) {
if (out == null) {
setError();
return;
}
try {
out.write(oneByte);
int b = oneByte & 0xFF;
// 0x0A is ASCII newline, 0x15 is EBCDIC newline.
boolean isNewline = b == 0x0A || b == 0x15;
if (autoflush && isNewline) {
flush();
}
} catch (IOException e) {
setError();
}
}
/**
* Appends the character {@code c} to the target stream. This method works
* the same way as {@link #print(char)}.
*
* @param c
* the character to append to the target stream.
* @return this stream.
*/
public PrintStream append(char c) {
print(c);
return this;
}
/**
* Appends the character sequence {@code csq} to the target stream. This
* method works the same way as {@code PrintStream.print(csq.toString())}.
* If {@code csq} is {@code null}, then the string "null" is written to the
* target stream.
*
* @param csq
* the character sequence appended to the target stream.
* @return this stream.
*/
public PrintStream append(CharSequence csq) {
if (null == csq) {
print(TOKEN_NULL);
} else {
print(csq.toString());
}
return this;
}
/**
* Appends a subsequence of the character sequence {@code csq} to the target
* stream. This method works the same way as {@code
* PrintStream.print(csq.subsequence(start, end).toString())}. If {@code
* csq} is {@code null}, then the specified subsequence of the string "null"
* will be written to the target stream.
*
* @param csq
* the character sequence appended to the target stream.
* @param start
* the index of the first char in the character sequence appended
* to the target stream.
* @param end
* the index of the character following the last character of the
* subsequence appended to the target stream.
* @return this stream.
* @throws IndexOutOfBoundsException
* if {@code start > end}, {@code start < 0}, {@code end < 0} or
* either {@code start} or {@code end} are greater or equal than
* the length of {@code csq}.
*/
public PrintStream append(CharSequence csq, int start, int end) {
if (null == csq) {
print(TOKEN_NULL.substring(start, end));
} else {
print(csq.subSequence(start, end).toString());
}
return this;
}
}