Conversions and Java Print Streams - Precision
(Page 5 of 5 )
Floating-point types (%e, %E, %f, %g, and%G) may also specify a precision in the form.3or.5. The precision comes after the width but before the conversion code. This indicates how many places are used after the decimal point. For example, this statement formats the same five constants 15 places wide, with 3 places after the decimal point:
System.out.printf("%15.3f %15.3f %15.3f
%15.3f %15.3f",
Math.PI, Math.E, 1.0, 0.0, Math.sqrt(2));
This is the output:
3.142 2.718 1.000 0.000 1.414
A precision can also be applied to strings and other nonnumeric, nondate types. In these cases, it specifies the maximum number of characters to write to the output.
Precision cannot be set for integral types, however. Attempting to do so throws anIllegalFormatPrecisionException. As usual, this is a subclass ofIllegalFormatExceptionand a runtime exception.
Formattable
You can format your own custom classes by implementing the Formattable interface. In format strings, you use %s and %S to indicate where you want your custom class instances placed. The default formatting for objects matched to %s and %S is simply whatever is returned by toString(). However, more often than not, this is just a debugging string not really meant for display to end users. If your class implements the java.util.Formattable interface, Java will use the return value of the object’s formatTo() method instead. That method has this signature:
public void formatTo(Formatter formatter, int flags, int width, int precision)
The four arguments are:
formatter
TheFormatterthat calledformatTo. More importantly, this is the object onto which the output will be written. Your method will invoke this object’sformat() methods to write to the underlying stream.
flags
A bitmasked constant providing the values of various flags set for this operation:
^,-,#, etc. You interpret these with theFormattableFlagsclass.
width
The minimum number of characters your method must return. If the returned value has fewer characters than the specified minimum, it will be padded with spaces.
precision
The maximum number of characters your method should return.
Earlier, I complained that the uppercasing in theURLclass was too naïve because, when formatted, it changed the case of case-sensitive parts such as the path and the query string as well as case-insensitive parts such as the scheme and the hostname. There’s another problem with the naïve uppercasing: the scheme and hostnames are defined in ASCII, but uppercasing isn’t always. In particular, uppercasing the letter i in Turkey produces the capital dotted I rather than the usual undotted capital I. For instance, www.microsoft.com uppercases as WWW.MICROSOFT.COM, which will not resolve.
Example 7-5 demonstrates aFormattableURLclass that uppercases only those parts of a URL that can be uppercased without changing its meaning. Ideally this would be a subclass ofjava.net.URL, but sinceURL is final, delegation is used instead. In essence,FormattableURLis a wrapper around a URL object that just provides theformatTo()method.
Example 7-5. Implementing Formattable
import java.util.*;
import java.net.*;
public class FormattableURL implements Formattable {
private URL delegate;
public FormattableURL(URL url){
this.delegate = url;
}
public void formatTo(Formatter formatter, int flags, int width,
int precision) {
if (precision < -1) {
throw new IllegalFormatPrecisionException(precision);
}
if (width < -1) {
throw new IllegalFormatWidthException(width);
}
if (precision > width) {
throw new IllegalFormatPrecisionException(precision);
}
// Check to see if we've been asked to use any flags we don't interpret
int recognizedFlags
= FormattableFlags.UPPERCASE | FormattableFlags.LEFT_JUSTIFY;
boolean unsupportedFlags = ((~recognizedFlags) & flags) != 0;
if (unsupportedFlags) {
// We should really pass the flags to this constructor.
// However, Java doesn't offer any reasonable way to get these.
throw new IllegalFormatFlagsException("#");
}
boolean upperCase = (flags & FormattableFlags.UPPERCASE) != 0;
StringBuffer sb = new StringBuffer();
String scheme = delegate.getProtocol();
if (upperCase && scheme != null) {
scheme = scheme.toUpperCase(Locale.ENGLISH);
}
String hostname = delegate.getHost();
if (upperCase && hostname != null) {
hostname = hostname.toUpperCase(Locale.ENGLISH);
}
String userInfo = delegate.getUserInfo();
int port = delegate.getPort();
String path = delegate.getPath();
String query = delegate.getQuery(); String fragment = delegate.getRef();
if (scheme != null) {
sb.append(scheme);
sb.append("://");
}
if (userInfo != null) {
sb.append(userInfo);
sb.append("@");
}
if (hostname != null) {
sb.append(hostname);
}
if (port != -1) {
sb.append(':');
sb.append(port);
}
if (path != null) {
sb.append(path);
}
if (query != null) {
sb.append('?');
sb.append(query);
}
if (fragment != null) {
sb.append('#');
sb.append(fragment);
}
boolean leftJustify = (flags & FormattableFlags.LEFT_JUSTIFY) != 0;
// Truncate on the right if necessary
if (precision < sb.length()) {
sb.setLength(precision);
}
else {// Pad with spaces if necessary
while (sb.length() < width) {
if (leftJustify) sb.append(' ');
else sb.insert(0, ' ');
}
}
formatter.format(sb.toString());
}
}
TheformatTo()method first checks to see if the values passed make sense—that is, that the width is greater than or equal to the precision, and both are greater than or equal to –1. (–1 simply indicates that these values weren’t set.) Assuming these checks pass, it splits the delegate URL into its component parts and uppercases the two case-insensitive parts (the scheme and the hostname) if the uppercase flag is set. It then appends all the other parts without changing their cases at all. Finally, if the precision is less than the string’s length, the formatted string is truncated on the right. If the string’s length is less than the specified width, the string is padded with spaces: on the right by default but on the left if the left-justified flag is set. If any other flags are present, anIllegalFormatFlagsExceptionis thrown. Thus, it becomes possible to format a URL like this:
URL url = new URL(http://www.example.org/index.html?name=value#Fred);
System.out.printf("%60.40S\n", new FormattableURL(url));
* XML is a notable exception here. It treats linefeeds, carriage returns, and carriage return linefeed pairs equally.
| DISCLAIMER: The content provided in this article is not warranted or guaranteed by Developer Shed, Inc. The content provided is intended for entertainment and/or educational purposes in order to introduce to the reader key ideas, concepts, and/or product reviews. As such it is incumbent upon the reader to employ real-world tactics for security and implementation of best practices. We are not liable for any negative consequences that may result from implementing any information covered in our articles or tutorials. If this is a hardware review, it is not recommended to open and/or modify your hardware. |
|
This article is excerpted from chapter seven of Java I/O, Second Edition, written by Elliotte Rusty Harold (O'Reilly, 2006; ISBN: 0596527500). Check it out today at your favorite bookstore. Buy this book now.
|
|