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" \

Reply via email to