This is an automated email from the ASF dual-hosted git repository.
desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git
The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
new ca43fdb6a7 Wrap the line of log messages in console application, for
making easier to read the warnings.
ca43fdb6a7 is described below
commit ca43fdb6a77653f262c1c6c620cf962900635672
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Mon Dec 4 13:10:16 2023 +0100
Wrap the line of log messages in console application,
for making easier to read the warnings.
---
.../main/org/apache/sis/console/Command.java | 19 ++++-
.../main/org/apache/sis/io/LineAppender.java | 59 ++++++++++-----
.../main/org/apache/sis/io/package-info.java | 2 +-
.../apache/sis/util/logging/MonolineFormatter.java | 83 ++++++++++++++++------
optional/src/org.apache.sis.gui/bundle/bin/sis | 3 +
.../src/org.apache.sis.gui/bundle/bin/sis_shell | 3 +
6 files changed, 130 insertions(+), 39 deletions(-)
diff --git
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/Command.java
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/Command.java
index a1905b42d8..e6062a0e9b 100644
---
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/Command.java
+++
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/Command.java
@@ -223,10 +223,27 @@ public final class Command {
*/
final String handler =
LogManager.getLogManager().getProperty("java.util.logging.ConsoleHandler.formatter");
if (MonolineFormatter.class.getName().equals(handler)) {
-
MonolineFormatter.install().resetLevelColors(X364.isAnsiSupported());
+ MonolineFormatter f = MonolineFormatter.install();
+ f.setMaximalLineLength(Command::terminalSize);
+ f.resetLevelColors(X364.isAnsiSupported());
}
}
+ /**
+ * {@return the terminal size if known, of {@link Integer#MAX_VALUE}
otherwise}.
+ * The returned value may be obsolete, because the {@code COLUMNS}
environment
+ * variable is not updated when the user resizes the terminal window.
+ */
+ private static int terminalSize() {
+ final String n = System.getenv("COLUMNS");
+ if (n != null && !n.isEmpty()) try {
+ return Integer.parseInt(n);
+ } catch (NumberFormatException e) {
+ // Ignore.
+ }
+ return Integer.MAX_VALUE;
+ }
+
/**
* Sets the specified system property if that property is not already set.
* This method returns whether the property has been set.
diff --git
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/io/LineAppender.java
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/io/LineAppender.java
index f63c785b04..899de1153e 100644
--- a/endorsed/src/org.apache.sis.util/main/org/apache/sis/io/LineAppender.java
+++ b/endorsed/src/org.apache.sis.util/main/org/apache/sis/io/LineAppender.java
@@ -54,7 +54,7 @@ import org.apache.sis.util.internal.X364;
* {@link #setTabulationExpanded(boolean)}.</p>
*
* @author Martin Desruisseaux (Geomatys)
- * @version 0.4
+ * @version 1.5
* @since 0.3
*/
public class LineAppender extends Appender implements Flushable {
@@ -63,9 +63,9 @@ public class LineAppender extends Appender implements
Flushable {
* {@link #append(CharSequence, int, int)} method will try to infer it
from the submitted text.
*
* <p>If {@link #isEndOfLineReplaced} is {@code false} (the default), then
this line separator
- * will be used only when this class inserts new line separators as a
consequence of line wraps;
- * line separators found in the texts given by the user will be passed "as
is". If {@code true},
- * then all line separators are replaced.</p>
+ * will be used only when this class inserts new line separators as a
consequence of line wraps.
+ * Line separators found in the texts given by the user will be passed "as
is".
+ * If {@code true}, then all line separators are replaced.</p>
*/
private String lineSeparator;
@@ -81,6 +81,8 @@ public class LineAppender extends Appender implements
Flushable {
* The length of the current line, in units of <em>code points</em> (not
{@code char}).
* It may be greater than the length of {@link #buffer} because the latter
contains only
* the last word.
+ *
+ * @see #getCurrentLineLength()
*/
private int codePointCount;
@@ -214,6 +216,31 @@ public class LineAppender extends Appender implements
Flushable {
maximalLineLength = length;
}
+ /**
+ * (@return the length of the current line, in units of Unicode code
points}.
+ *
+ * @since 1.5
+ */
+ public int getCurrentLineLength() {
+ return codePointCount;
+ }
+
+ /**
+ * Sets the length of the current line. This method usually do not need to
be invoked,
+ * because the value of this property is automatically adjusted when texts
are appended
+ * by this {@code LineAppender}. However, setting an explicit value may be
useful when
+ * the output specified to the constructor was not initially empty, or
when the output
+ * content is modified outside this {@code LineAppender} instance.
+ *
+ * @param lengh the new length of the current line, in units of Unicode
code points.
+ *
+ * @since 1.5
+ */
+ public void setCurrentLineLength(final int length) {
+ ArgumentChecks.ensurePositive("length", length);
+ codePointCount = length;
+ }
+
/**
* Returns the current tabulation width, in unit of Unicode characters
(code point count).
* The default value is 8.
@@ -302,10 +329,9 @@ public class LineAppender extends Appender implements
Flushable {
}
/**
- * Writes pending non-white characters, discards trailing whitespaces, and
resets column
- * position to zero. This method does <strong>not</strong> write the line
separator and
- * does not modify the status of the {@link #skipLF} flag; those tasks are
caller's
- * responsibility.
+ * Writes pending non-white characters, discards trailing whitespaces, and
resets column position to zero.
+ * This method does <strong>not</strong> write the line separator and does
not modify the status of the
+ * {@link #skipLF} flag. Those tasks are caller's responsibility.
*/
private void endOfLine() throws IOException {
buffer.setLength(printableLength); // Reduce the amount of work
for StringBuilder.deleteCharAt(int).
@@ -355,7 +381,6 @@ public class LineAppender extends Appender implements
Flushable {
*/
@SuppressWarnings("fallthrough")
private void write(final int c) throws IOException {
- final StringBuilder buffer = this.buffer;
/*
* If the character to write is a EOL sequence, then:
*
@@ -375,9 +400,9 @@ public class LineAppender extends Appender implements
Flushable {
endOfLine();
}
if (!isEndOfLineReplaced) {
- appendCodePoint(c); // Forward EOL sequences "as-is".
+ appendCodePoint(c); // Forward EOL sequences "as-is".
} else if (!skip) {
- writeLineSeparator(); // Replace EOL sequences by the unique
line separator.
+ writeLineSeparator(); // Replace EOL sequences by the
unique line separator.
}
return;
}
@@ -387,8 +412,8 @@ public class LineAppender extends Appender implements
Flushable {
* the buffer to the underlying appendable since we know that those
characters didn't
* exceeded the line length limit.
*
- * We use Character.isWhitespace(…) instead of
Character.isSpaceChar(…) because
- * the former returns 'true' tabulations (which we want), and returns
'false'
+ * We use `Character.isWhitespace(…)` instead of
`Character.isSpaceChar(…)` because
+ * the former returns `true` tabulations (which we want), and returns
`false`
* for non-breaking spaces (which we also want).
*/
if (Character.isWhitespace(c)) {
@@ -415,7 +440,7 @@ public class LineAppender extends Appender implements
Flushable {
/*
* Special handling of ANSI X3.64 escape sequences. Since they are not
visible
* characters (they are used for controlling the colors), do not count
them in
- * 'codePointCount' (but still count them as "printable" characters,
since we
+ * `codePointCount` (but still count them as "printable" characters,
since we
* don't want to trim them). The sequence pattern is "CSI <digits>
<command>"
* where <command> is a single letter.
*/
@@ -426,9 +451,9 @@ public class LineAppender extends Appender implements
Flushable {
final char previous = buffer.charAt(printableLength - 2);
if (previous != X364.ESCAPE) {
isEscapeSequence = (c >= '0' && c <= '9');
- return; // The letter after the digits will be the last
character to skip.
+ return; // The letter after the digits will be the
last character to skip.
} else if (c == X364.BRACKET) {
- return; // Found the second part of the Control Sequence
Introducer (CSI).
+ return; // Found the second part of the Control
Sequence Introducer (CSI).
}
// [ESC] was not followed by '['. Proceed as a normal character.
isEscapeSequence = false;
@@ -511,7 +536,7 @@ searchHyp: for (int i=buffer.length(); i>0;) {
/*
* Use the line separator found in the submitted document, if
possible.
* If we don't find any line separator in the submitted content,
leave
- * the 'lineSeparator' field to null since the 'write' method will
set
+ * the `lineSeparator` field to null since the `write` method will
set
* it to the default value only if it really needs it.
*/
lineSeparator = lineSeparator(sequence, start, end);
diff --git
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/io/package-info.java
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/io/package-info.java
index d1902c5d80..de6bfe651c 100644
--- a/endorsed/src/org.apache.sis.util/main/org/apache/sis/io/package-info.java
+++ b/endorsed/src/org.apache.sis.util/main/org/apache/sis/io/package-info.java
@@ -41,7 +41,7 @@
* Unicode supplementary characters}.
*
* @author Martin Desruisseaux (IRD, Geomatys)
- * @version 1.3
+ * @version 1.5
* @since 0.3
*/
package org.apache.sis.io;
diff --git
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/logging/MonolineFormatter.java
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/logging/MonolineFormatter.java
index 90d02508f9..e3a9fff1f5 100644
---
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/logging/MonolineFormatter.java
+++
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/logging/MonolineFormatter.java
@@ -32,6 +32,7 @@ import java.util.SortedMap;
import java.util.Comparator;
import java.util.ResourceBundle;
import java.util.logging.*;
+import java.util.function.IntSupplier;
import org.apache.sis.system.Modules;
import org.apache.sis.system.Configuration;
import org.apache.sis.util.ArgumentChecks;
@@ -111,7 +112,7 @@ import static
org.apache.sis.util.internal.StandardDateFormat.UTC;
* from multiple threads.
*
* @author Martin Desruisseaux (Geomatys)
- * @version 1.0
+ * @version 1.5
*
* @see SimpleFormatter
* @see Handler#setFormatter(Formatter)
@@ -229,6 +230,14 @@ public class MonolineFormatter extends Formatter {
*/
private final int levelWidth;
+ /**
+ * Provider of the maximal number of columns in the records to format, or
{@code null} if none.
+ * The value is provided in number of Unicode code points.
+ *
+ * @see #getMaximalLineLength()
+ */
+ private IntSupplier maximalLineLength;
+
/**
* Time of {@code MonolineFormatter} creation, in milliseconds elapsed
since January 1, 1970.
*/
@@ -390,29 +399,27 @@ loop: for (int i=0; ; i++) {
* @return the string to write on the left side of the first line of every
log records, or {@code null} if none.
*/
public String getHeader() {
- final String header;
synchronized (buffer) {
- header = this.header;
+ // All other properties in MonolineFormatter are defined in such a
way
+ // that null means "none", so we do the same here for consistency.
+ return header.isEmpty() ? null : header;
}
- // All other properties in MonolineFormatter are defined in such a way
- // that null means "none", so we do the same here for consistency.
- return header.isEmpty() ? null : header;
}
/**
* Sets the string to write on the left side of the first line of every
log records.
*
- * @param header The string to write on the left side of the first line of
every log records,
+ * @param text the string to write on the left side of the text line of
every log records,
* or {@code null} if none.
*/
- public void setHeader(String header) {
- if (header == null) { // See comment in
getHeader().
- header = "";
+ public void setHeader(String text) {
+ if (text == null) { // See comment in
getHeader().
+ text = "";
}
synchronized (buffer) {
- this.header = header;
+ header = text;
buffer.setLength(0);
- buffer.append(header);
+ buffer.append(text);
}
}
@@ -588,6 +595,7 @@ loop: for (int i=0; ; i++) {
* are supported or not - this check must be done by the caller.
*/
private void resetLevelColors() {
+ @SuppressWarnings("LocalVariableHidesMemberVariable")
final SortedMap<Level,X364> colors = colors();
colors.clear();
colors.put(Level.ALL, X364.BACKGROUND_GRAY);
@@ -644,6 +652,35 @@ loop: for (int i=0; ; i++) {
return colorSequences[i];
}
+ /**
+ * {@return the provider of the maximal number of columns in the records
to format, or {@code null} if none}.
+ * Records longer than this value (in number of Unicode code points) will
be separated one two or more lines.
+ * The value may be a constant, or it may be a value fetched from the
{@code COLUMNS} environment variable.
+ * Advanced applications may also try to get this value by executing an
OS-specific command.
+ *
+ * @since 1.5
+ */
+ public IntSupplier getMaximalLineLength() {
+ synchronized (buffer) {
+ return maximalLineLength;
+ }
+ }
+
+ /**
+ * Sets the provider of the maximal number of columns in the records to
format.
+ * The value will be fetched for each record to format in order to allow
changes.
+ * For example, the user may resize the terminal window.
+ *
+ * @param value the provider of the maximal number of columns, or {@code
null} if none.
+ *
+ * @since 1.5
+ */
+ public void setMaximalLineLength(final IntSupplier value) {
+ synchronized (buffer) {
+ maximalLineLength = value;
+ }
+ }
+
/**
* Formats the given log record and returns the formatted string.
* See the <a href="#overview">class javadoc</a> for information on the
log format.
@@ -657,10 +694,9 @@ loop: for (int i=0; ; i++) {
String emphasisStart = ""; // ANSI escape sequence
for bold text if we use it.
String emphasisEnd = ""; // ANSI escape sequence
for stopping bold text if we use it.
final Level level = record.getLevel();
- final StringBuffer buffer = this.buffer;
synchronized (buffer) {
- final boolean colors = (this.colors != null);
- if (colors && level.intValue() >= LEVEL_THRESHOLD.intValue()) {
+ final boolean colored = (colors != null);
+ if (colored && level.intValue() >= LEVEL_THRESHOLD.intValue()) {
emphasisStart = X364.BOLD.sequence();
emphasisEnd = X364.NORMAL.sequence();
faint = faintSupported;
@@ -668,7 +704,7 @@ loop: for (int i=0; ; i++) {
buffer.setLength(header.length());
/*
* Append the time (e.g. "00:00:12.365"). The time pattern can be
set either
- * programmatically by a call to 'setTimeFormat(…)', or in
logging.properties
+ * programmatically by a call to `setTimeFormat(…)`, or in
logging.properties
* file with the
"org.apache.sis.util.logging.MonolineFormatter.time" property.
*/
if (timeFormat != null) {
@@ -683,7 +719,7 @@ loop: for (int i=0; ; i++) {
int margin = buffer.length();
String levelColor = "", levelReset = "";
if (SHOW_LEVEL) {
- if (colors) {
+ if (colored) {
levelColor = colorAt(level);
levelReset = X364.BACKGROUND_DEFAULT.sequence();
}
@@ -743,15 +779,21 @@ loop: for (int i=0; ; i++) {
}
final Throwable exception = record.getThrown();
String message = formatMessage(record);
+ int maximalLength = Integer.MAX_VALUE;
int length = 0;
if (message != null) {
length = CharSequences.skipTrailingWhitespaces(message, 0,
message.length());
+ if (maximalLineLength != null) {
+ maximalLength = maximalLineLength.getAsInt();
+ }
}
/*
* Up to this point, we wrote directly in the StringBuilder for
performance reasons.
* Now for the message part, we need to use the LineAppender in
order to replace EOL
* and tabulations.
*/
+ writer.setMaximalLineLength(maximalLength);
+ writer.setCurrentLineLength(X364.lengthOfPlain(buffer, 0,
buffer.length()));
try {
if (message != null) {
writer.append(message, 0, length);
@@ -767,15 +809,16 @@ loop: for (int i=0; ; i++) {
record.getSourceClassName(),
record.getSourceMethodName());
}
}
+ writer.clear();
writer.flush();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
/*
* We wrote the main content, but maybe with some extra lines.
Trim the last lines by skipping white spaces
- * and line separator (EOL). If the 'bodyLineSeparator' margin is
found immediately before the white spaces
+ * and line separator (EOL). If the `bodyLineSeparator` margin is
found immediately before the white spaces
* and EOL that we skipped, we repeat this process until we find
at least one non-white character after the
- * 'bodyLineSeparator'.
+ * `bodyLineSeparator`.
*/
int lastMargin = buffer.length();
do {
@@ -904,7 +947,7 @@ loop: for (int i=0; ; i++) {
}
}
/*
- * If the stack trace element pointed by 'logProducer' is the same
one than
+ * If the stack trace element pointed by `logProducer` is the same
one than
* during the previous iteration, it is not worth to print those
elements again.
*/
int stopIndex = trace.length;
diff --git a/optional/src/org.apache.sis.gui/bundle/bin/sis
b/optional/src/org.apache.sis.gui/bundle/bin/sis
index b3f310a39a..bd0f4b6f7c 100755
--- a/optional/src/org.apache.sis.gui/bundle/bin/sis
+++ b/optional/src/org.apache.sis.gui/bundle/bin/sis
@@ -23,6 +23,9 @@ SIS_DATA="${SIS_DATA:-$BASE_DIR/data}"
export SIS_DATA
unset SIS_HOME
+COLUMNS=${COLUMNS:-`tput cols`}
+export COLUMNS
+
# Execute SIS with any optional JAR that the user may put in the 'lib'
directory.
java --module-path
"$BASE_DIR/lib:$BASE_DIR/lib/app/org.apache.sis.console.jar" \
-Djava.util.logging.config.class="org.apache.sis.util.logging.Initializer" \
diff --git a/optional/src/org.apache.sis.gui/bundle/bin/sis_shell
b/optional/src/org.apache.sis.gui/bundle/bin/sis_shell
index 4559adf68f..85f086e5ec 100755
--- a/optional/src/org.apache.sis.gui/bundle/bin/sis_shell
+++ b/optional/src/org.apache.sis.gui/bundle/bin/sis_shell
@@ -23,6 +23,9 @@ SIS_DATA="${SIS_DATA:-$SIS_HOME/data}"
export SIS_DATA
export SIS_HOME
+COLUMNS=${COLUMNS:-`tput cols`}
+export COLUMNS
+
jshell --module-path
"$SIS_HOME/lib:$SIS_HOME/lib/app/org.apache.sis.console.jar" \
--add-module org.apache.sis.console \
--startup "$SIS_HOME/conf/imports.jsh" \