Skip to content

Commit

Permalink
CompactFormatter precision and surrogate pairs #528 (#543)
Browse files Browse the repository at this point in the history
Signed-off-by: jmehrens <jason_mehrens@hotmail.com>
  • Loading branch information
jmehrens committed Apr 7, 2021
1 parent bca969d commit 9fd37ac
Show file tree
Hide file tree
Showing 3 changed files with 714 additions and 123 deletions.
7 changes: 7 additions & 0 deletions doc/release/CHANGES.txt
Expand Up @@ -20,6 +20,13 @@ Seven digit bug numbers are from the old Sun bug database, which is no
longer available.


CHANGES IN THE 2.0.2 RELEASE
----------------------------
The following bugs have been fixed in the 2.0.2 release.

E 528 CompactFormatter precision and surrogate pairs


CHANGES IN THE 2.0.1 RELEASE
----------------------------
The following bugs have been fixed in the 2.0.1 release.
Expand Down
125 changes: 74 additions & 51 deletions mail/src/main/java/com/sun/mail/util/logging/CompactFormatter.java
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2013, 2019 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 2019 Jason Mehrens. All rights reserved.
* Copyright (c) 2013, 2021 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2013, 2021 Jason Mehrens. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0, which is available at
Expand All @@ -21,8 +21,9 @@

/**
* A plain text formatter that can produce fixed width output. By default this
* formatter will produce output no greater than 160 characters wide plus the
* separator and newline characters. Only specified fields support an
* formatter will produce output no greater than 160 characters
* (Unicode code points) wide plus the separator and newline characters. Only
* specified fields support an
* {@linkplain #toAlternate(java.lang.String) alternate} fixed width format.
* <p>
* By default each <code>CompactFormatter</code> is initialized using the
Expand All @@ -33,8 +34,10 @@
* used.
* <ul>
* <li>&lt;formatter-name&gt;.format - the {@linkplain java.util.Formatter
* format} string used to transform the output. The format string can be
* used to fix the output size. (defaults to <code>%7$#.160s%n</code>)</li>
* format} string used to transform the output. The arguments are explained
* in detail in the {@linkplain #format(java.util.logging.LogRecord) format}
* documentation. The format string can be used to fix the output size.
* (defaults to <code>%7$#.160s%n</code>)</li>
* </ul>
*
* @author Jason Mehrens
Expand Down Expand Up @@ -112,10 +115,12 @@ public CompactFormatter(final String format) {
* and a relevant stack trace element if available; otherwise, an empty
* string is used.</li>
* <li>{@code message|thrown} The message and the thrown properties joined
* as one parameter. This parameter supports
* as one parameter. Width and precision are by Unicode code points. This
* parameter supports
* {@linkplain #toAlternate(java.lang.String) alternate} form.</li>
* <li>{@code thrown|message} The thrown and message properties joined as
* one parameter. This parameter supports
* one parameter. Width and precision are by Unicode code points. This
* parameter supports
* {@linkplain #toAlternate(java.lang.String) alternate} form.</li>
* <li>{@code sequence} the
* {@linkplain LogRecord#getSequenceNumber() sequence number} if the given
Expand All @@ -128,10 +133,12 @@ public CompactFormatter(final String format) {
* {@linkplain #formatError(LogRecord) error message} without any stack
* trace.</li>
* <li>{@code message|error} The message and error properties joined as one
* parameter. This parameter supports
* parameter. Width and precision are by Unicode code points. This parameter
* supports
* {@linkplain #toAlternate(java.lang.String) alternate} form.</li>
* <li>{@code error|message} The error and message properties joined as one
* parameter. This parameter supports
* parameter. Width and precision are by Unicode code points. This parameter
* supports
* {@linkplain #toAlternate(java.lang.String) alternate} form.</li>
* <li>{@code backtrace} only the
* {@linkplain #formatBackTrace(LogRecord) stack trace} of the given
Expand All @@ -148,17 +155,19 @@ public CompactFormatter(final String format) {
* <ul>
* <li>{@code com.sun.mail.util.logging.CompactFormatter.format=%7$#.160s%n}
* <p>
* This prints only 160 characters of the message|thrown ({@code 7$}) using
* the {@linkplain #toAlternate(java.lang.String) alternate} form. The
* separator is not included as part of the total width.
* This prints only 160 characters (Unicode code points) of the
* message|thrown ({@code 7$}) using the
* {@linkplain #toAlternate(java.lang.String) alternate} form. The separator
* is not included as part of the total width.
* <pre>
* Encoding failed.|NullPointerException: null String.getBytes(:913)
* </pre>
*
* <li>{@code com.sun.mail.util.logging.CompactFormatter.format=%7$#.20s%n}
* <p>
* This prints only 20 characters of the message|thrown ({@code 7$}) using
* the {@linkplain #toAlternate(java.lang.String) alternate} form. This will
* This prints only 20 characters (Unicode code points) of the
* message|thrown ({@code 7$}) using the
* {@linkplain #toAlternate(java.lang.String) alternate} form. This will
* perform a weighted truncation of both the message and thrown properties
* of the log record. The separator is not included as part of the total
* width.
Expand All @@ -179,8 +188,9 @@ public CompactFormatter(final String format) {
*
* <li>{@code com.sun.mail.util.logging.CompactFormatter.format=%4$s: %12$#.160s%n}
* <p>
* This prints the log level ({@code 4$}) and only 160 characters of the
* message|error ({@code 12$}) using the alternate form.
* This prints the log level ({@code 4$}) and only 160 characters
* (Unicode code points) of the message|error ({@code 12$}) using the
* alternate form.
* <pre>
* SEVERE: Unable to send notification.|SocketException: Permission denied: connect
* </pre>
Expand Down Expand Up @@ -474,12 +484,7 @@ private String findAndFormat(final StackTraceElement[] trace) {
*/
private String formatStackTraceElement(final StackTraceElement s) {
String v = simpleClassName(s.getClassName());
String result;
if (v != null) {
result = s.toString().replace(s.getClassName(), v);
} else {
result = s.toString();
}
String result = s.toString().replace(s.getClassName(), v);

//If the class name contains the simple file name then remove file name.
v = simpleFileName(s.getFileName());
Expand Down Expand Up @@ -753,7 +758,7 @@ private static String simpleFileName(String name) {
* @return true if null or spaces.
*/
private static boolean isNullOrSpaces(final String s) {
return s == null || s.trim().length() == 0;
return s == null || s.trim().isEmpty();
}

/**
Expand Down Expand Up @@ -799,54 +804,72 @@ public void formatTo(java.util.Formatter formatter, int flags,
r = toAlternate(r);
}

if (precision <= 0) {
precision = Integer.MAX_VALUE;
}
int lc = 0;
int rc = 0;
if (precision >= 0) {
lc = minCodePointCount(l, precision);
rc = minCodePointCount(r, precision);

int fence = Math.min(l.length(), precision);
if (fence > (precision >> 1)) {
fence = Math.max(fence - r.length(), fence >> 1);
}

if (fence > 0) {
if (fence > l.length()
&& Character.isHighSurrogate(l.charAt(fence - 1))) {
--fence;
if (lc > (precision >> 1)) {
lc = Math.max(lc - rc, lc >> 1);
}
l = l.substring(0, fence);
rc = Math.min(precision - lc, rc);

l = l.substring(0, l.offsetByCodePoints(0, lc));
r = r.substring(0, r.offsetByCodePoints(0, rc));
}
r = r.substring(0, Math.min(precision - fence, r.length()));

if (width > 0) {
if (precision < 0) {
lc = minCodePointCount(l, width);
rc = minCodePointCount(r, width);
}

final int half = width >> 1;
if (l.length() < half) {
l = pad(flags, l, half);
if (lc < half) {
l = pad(flags, l, half - lc);
}

if (r.length() < half) {
r = pad(flags, r, half);
if (rc < half) {
r = pad(flags, r, half - rc);
}
}

Object[] empty = Collections.emptySet().toArray();
formatter.format(l, empty);
if (l.length() != 0 && r.length() != 0) {
formatter.format("|", empty);
formatter.format(l);
if (!l.isEmpty() && !r.isEmpty()) {
formatter.format("|");
}
formatter.format(r);
}

/**
* Counts the number code points with an upper bound.
*
* @param s the string to count, never null.
* @param limit the max number of code points needed.
* @return the number of code points, never greater than the limit.
*/
private int minCodePointCount(String s, final int limit) {
//assert limit >= 0 : limit;
final int len = s.length();
if ((len - limit) >= limit) {
return limit;
}
formatter.format(r, empty);
return Math.min(s.codePointCount(0, len), limit);
}

/**
* Pad the given input string.
*
* @param flags the formatter flags.
* @param s the string to pad.
* @param length the final string length.
* @param padding the number of spaces to add.
* @return the padded string.
*/
private String pad(int flags, String s, int length) {
final int padding = length - s.length();
final StringBuilder b = new StringBuilder(length);
private String pad(int flags, String s, int padding) {
//assert padding >= 0 : padding;
final StringBuilder b = new StringBuilder(
Math.max(s.length() + padding, padding));
if ((flags & java.util.FormattableFlags.LEFT_JUSTIFY)
== java.util.FormattableFlags.LEFT_JUSTIFY) {
for (int i = 0; i < padding; ++i) {
Expand Down

0 comments on commit 9fd37ac

Please sign in to comment.