下面列出了怎么用java.util.Formattable的API类实例代码及写法,或者点击链接到github查看源代码。
/**
* Returns a string representation of the user supplied formattable, accounting for any possible
* runtime exceptions.
*
* @param value the value to be formatted.
* @return a best-effort string representation of the given value, even if exceptions were thrown.
*/
private static void safeFormatTo(Formattable value, StringBuilder out, FormatOptions options) {
// Only care about 3 specific flags for Formattable.
int formatFlags = options.getFlags() & (FLAG_LEFT_ALIGN | FLAG_UPPER_CASE | FLAG_SHOW_ALT_FORM);
if (formatFlags != 0) {
// TODO: Maybe re-order the options flags to make this step easier or use a lookup table.
// Note that reordering flags would require a rethink of how they are parsed.
formatFlags = ((formatFlags & FLAG_LEFT_ALIGN) != 0 ? FormattableFlags.LEFT_JUSTIFY : 0)
| ((formatFlags & FLAG_UPPER_CASE) != 0 ? FormattableFlags.UPPERCASE : 0)
| ((formatFlags & FLAG_SHOW_ALT_FORM) != 0 ? FormattableFlags.ALTERNATE : 0);
}
// We may need to undo an arbitrary amount of appending if there is an error.
int originalLength = out.length();
Formatter formatter = new Formatter(out, FORMAT_LOCALE);
try {
value.formatTo(formatter, formatFlags, options.getWidth(), options.getPrecision());
} catch (RuntimeException e) {
out.setLength(originalLength);
// We only use a StringBuilder to create the Formatter instance.
try {
formatter.out().append(getErrorString(value, e));
} catch (IOException impossible) { }
}
}
@Test
public void testFormattable() {
Formattable arg = new Formattable() {
@Override
public void formatTo(Formatter formatter, int flags, int width, int precision) {
try {
formatter.out()
.append(String.format("[f=%d,w=%d,p=%d]", flags, width, precision));
} catch (IOException e) {
// Impossible since the Appendable is a StringBuilder
}
}
};
assertThat(log("%s", arg)).isEqualTo("[f=0,w=-1,p=-1]");
assertThat(log("%100s", arg)).isEqualTo("[f=0,w=100,p=-1]");
assertThat(log("%.25s", arg)).isEqualTo("[f=0,w=-1,p=25]");
assertThat(log("%100.25s", arg)).isEqualTo("[f=0,w=100,p=25]");
assertThat(log("%-100s", arg)).isEqualTo("[f=1,w=100,p=-1]");
assertThat(log("%S", arg)).isEqualTo("[f=2,w=-1,p=-1]");
assertThat(log("%#s", arg)).isEqualTo("[f=4,w=-1,p=-1]");
assertThat(log("%-#32.16S", arg)).isEqualTo("[f=7,w=32,p=16]");
}
@Test
public void testFormattableError() {
Formattable arg = new Formattable() {
@Override
public void formatTo(Formatter formatter, int flags, int width, int precision) {
try {
// This should be deleted if an error occurs.
formatter.out().append("UNEXPECTED");
} catch (IOException e) {
// Impossible since the Appendable is a StringBuilder
}
throw new RuntimeException("Badness!!");
}
};
assertThat(log("%s", arg)).contains("java.lang.RuntimeException: Badness!!");
assertThat(log("%s", arg)).doesNotContain("UNEXPECTED");
}
@Override
public void writeTo(final Formatter f, final Object aValueToWrite, final Appendable aOut) throws IOException {
if (aValueToWrite == null) {
if ((flags & FormattableFlags.UPPERCASE) > 0) {
aOut.append("NULL");
} else {
aOut.append("null");
}
} else if (aValueToWrite instanceof Formattable) {
final Formattable formattable = (Formattable) aValueToWrite;
formattable.formatTo(f, flags, width, precision);
} else {
if ((flags & FormattableFlags.UPPERCASE) > 0) {
aOut.append(aValueToWrite.toString().toUpperCase());
} else {
aOut.append(aValueToWrite.toString());
}
}
}
@Test
public void testUselessWidthAndPrecision() {
final Formattable f = new Formattable() {
@Override
public void formatTo(final Formatter formatter, final int flags, final int width, final int precision) {
final StringBuilder sb = new StringBuilder();
sb.append(flags);
sb.append(":");
sb.append(width);
sb.append(":");
sb.append(precision);
formatter.format("%s", sb);
}
};
final String result = String.format("%10.3s", f);
System.out.println(result);
Assert.assertEquals("0:10:3", result);
}
@Test
public void testUselessPrecision() {
final Formattable f = new Formattable() {
@Override
public void formatTo(final Formatter formatter, final int flags, final int width, final int precision) {
final StringBuilder sb = new StringBuilder();
sb.append(flags);
sb.append(":");
sb.append(width);
sb.append(":");
sb.append(precision);
formatter.format("%s", sb);
}
};
final String result = String.format("%.3s", f);
System.out.println(result);
Assert.assertEquals("0:-1:3", result);
}
/**
* Wraps {@code obj} in a {@link Formatter} that standardizes formatting for certain objects.
*/
static Formattable fmt(Object obj) {
return new Formattable() {
@Override
public void formatTo(Formatter buf, int flags, int width, int precision) {
if (obj instanceof Throwable) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
((Throwable) obj).printStackTrace(new PrintStream(baos));
buf.format("%s", baos.toString());
} else if (obj instanceof StackTraceElement[]) {
for (StackTraceElement e : (StackTraceElement[]) obj) {
buf.format("\t%s%n", e);
}
} else if (obj instanceof JavaMethod) {
buf.format("%s", str((JavaMethod) obj));
} else {
buf.format("%s", obj);
}
}
};
}
@Test
public void testPrintfFormattable() {
AssertingLogger logger = new AssertingLogger();
LoggerBackend backend = newBackend(logger);
Object arg =
new Formattable() {
@Override
public void formatTo(java.util.Formatter fmt, int flags, int width, int precision) {
fmt.format("[f=%d, w=%d, p=%d]", flags, width, precision);
}
@Override
public String toString() {
return "FAILED";
}
};
backend.log(withPrintfStyle("Hello %s World", arg));
backend.log(withPrintfStyle("Hello %#S World", arg));
backend.log(withPrintfStyle("Hello %-10.4s World", arg));
logger.assertLogCount(3);
logger.assertLogEntry(0, INFO, "Hello [f=0, w=-1, p=-1] World");
logger.assertLogEntry(1, INFO, "Hello [f=6, w=-1, p=-1] World");
logger.assertLogEntry(2, INFO, "Hello [f=1, w=10, p=4] World");
}
/**
* Returns a {@link Formattable} object that can be used with {@link String#format(String, Object...)}.
* <p>
* When used as the argument for a {@literal %s} format string element, the {@literal bytes} value
* will be formatted using the current {@link ByteSizeStrings} values, or if the alternative
* flag is set (using the {@literal %#s} format string) it will use the {@link ByteSizeStrings#metric()}
* formatter. Finally, the precision of the formatted value can be adjusted using format string
* argumenbts like {@literal %.6s}.
*
* @see http://docs.oracle.com/javase/7/docs/api/java/util/Formatter.html#syntax
*/
public Formattable formatted(final long bytes) {
return new Formattable() {
@Override
public void formatTo(Formatter formatter, int flags, int width, int precision) {
boolean alternate = (flags & FormattableFlags.ALTERNATE) == FormattableFlags.ALTERNATE;
ByteSizeStrings strings = alternate ? ByteSizeStrings.metric() : ByteSizeStrings.this;
if (precision != -1) {
formatter.format("%s", strings.makeSizeString(bytes, precision));
} else {
formatter.format("%s", strings.makeSizeString(bytes));
}
}
};
}
private static void appendFormatted(
StringBuilder out, Object value, FormatChar format, FormatOptions options) {
// Fast path switch statement for commonest cases (we could handle upper-case as a post
// processing step but it's so uncommon it doesn't seem worth it).
switch (format) {
case STRING:
// String formatting is by far and away the most common case.
if (!(value instanceof Formattable)) {
if (options.isDefault()) {
// %s on a non-Formattable instance is the single most common case by far.
out.append(safeToString(value));
return;
}
break;
}
// Rare but easy to deal with efficiently, and a can support wrapped arguments nicely.
safeFormatTo((Formattable) value, out, options);
return;
// Some other types are really easy when they don't have special format options.
case DECIMAL:
case BOOLEAN:
if (options.isDefault()) {
out.append(value);
return;
}
break;
case HEX:
// Check that if the format options are compatible with "easy" hex formatting. This could
// be expanded to include width, radix and zero padding (relatively common for hex).
if (options.filter(FLAG_UPPER_CASE, false, false).equals(options)) {
// Having called canFormat(), we know the value must be a Number.
appendHex(out, (Number) value, options);
return;
}
break;
case CHAR:
if (options.isDefault()) {
if (value instanceof Character) {
out.append(value);
return;
}
int codePoint = ((Number) value).intValue();
if (Character.isBmpCodePoint(codePoint)) {
out.append((char) codePoint);
return;
}
out.append(Character.toChars(codePoint));
return;
}
break;
default:
// Fall through.
}
// Default handle for rare cases that need non-trivial formatting.
String formatString = format.getDefaultFormatString();
if (!options.isDefault()) {
char chr = format.getChar();
if (options.shouldUpperCase()) {
// Clear 6th bit to convert lower case ASCII to upper case.
chr &= (char) ~0x20;
}
formatString = options.appendPrintfOptions(new StringBuilder("%")).append(chr).toString();
}
out.append(String.format(FORMAT_LOCALE, formatString, value));
}
/**
* Get the default formatted representation of the specified
* {@code Formattable}.
*
* @param formattable the instance to convert to a string, not null
* @return the resulting string, not null
*/
public static String toString(Formattable formattable) {
return String.format(SIMPLEST_FORMAT, formattable);
}
/**
* Get the default formatted representation of the specified
* {@code Formattable}.
*
* @param formattable the instance to convert to a string, not null
* @return the resulting string, not null
*/
public static String toString(final Formattable formattable) {
return String.format(SIMPLEST_FORMAT, formattable);
}
/**
* Get the default formatted representation of the specified
* {@code Formattable}.
*
* @param formattable the instance to convert to a string, not null
* @return the resulting string, not null
*/
public static String toString(Formattable formattable) {
return String.format(SIMPLEST_FORMAT, formattable);
}