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 1a72af035d Fix more character encoding and ANSI color problems when 
using SIS from command line or from JShell.
1a72af035d is described below

commit 1a72af035d9ee83abec8aa68e8f2da1cf005e7c4
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Thu Dec 14 17:02:31 2023 +0100

    Fix more character encoding and ANSI color problems
    when using SIS from command line or from JShell.
---
 .../main/org/apache/sis/console/AboutCommand.java  | 10 +--
 .../main/org/apache/sis/console/Command.java       |  7 +-
 .../main/org/apache/sis/console/CommandRunner.java | 68 +++++++----------
 .../apache/sis/console/FormattedOutputCommand.java |  3 +
 .../org/apache/sis/console/IdentifierCommand.java  | 12 +--
 .../apache/sis/console/ResourcesDownloader.java    |  3 +-
 .../main/org/apache/sis/console/SIS.java           | 29 ++------
 .../org/apache/sis/console/TransformCommand.java   | 45 +++++------
 .../main/org/apache/sis/io/wkt/AbstractParser.java |  3 +-
 .../org/apache/sis/io/wkt/FormattableObject.java   | 17 ++---
 .../main/org/apache/sis/io/wkt/Formatter.java      | 21 +-----
 .../org/apache/sis/parameter/ParameterFormat.java  | 11 +--
 .../main/org/apache/sis/parameter/Parameters.java  |  6 +-
 .../sis/referencing/factory/CacheRecord.java       |  5 +-
 .../factory/ConcurrentAuthorityFactory.java        | 24 ++++--
 .../referencing/util/j2d/ShapeUtilitiesViewer.java |  5 +-
 .../main/org/apache/sis/system/Environment.java    | 87 ++++++++++++++++++++++
 .../main/org/apache/sis/util/Printable.java        | 42 +++++++++++
 18 files changed, 246 insertions(+), 152 deletions(-)

diff --git 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/AboutCommand.java
 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/AboutCommand.java
index 115fbec3b0..fd83a21a7e 100644
--- 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/AboutCommand.java
+++ 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/AboutCommand.java
@@ -161,24 +161,22 @@ final class AboutCommand extends CommandRunner {
         if (warnings != null) {
             out.println();
             if (colors) {
-                out.print(X364.BACKGROUND_RED.sequence());
-                out.print(X364.BOLD.sequence());
+                color(X364.BACKGROUND_RED);
+                color(X364.BOLD);
                 out.print(' ');
             }
             
Vocabulary.getResources(locale).appendLabel(Vocabulary.Keys.Warnings, out);
             if (colors) {
                 out.print(' ');
                 out.println(X364.RESET.sequence());
-                out.print(X364.FOREGROUND_RED.sequence());
+                color(X364.FOREGROUND_RED);
             } else {
                 out.println();
             }
             for (final String warning : warnings) {
                 out.println(warning);
             }
-            if (colors) {
-                out.print(X364.FOREGROUND_DEFAULT.sequence());
-            }
+            color(X364.FOREGROUND_DEFAULT);
         }
         out.flush();
         return 0;
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 2d9d882e3b..4eae489efa 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
@@ -31,6 +31,7 @@ import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.logging.Logging;
 import org.apache.sis.util.logging.Initializer;
 import org.apache.sis.util.logging.MonolineFormatter;
+import org.apache.sis.system.Environment;
 
 
 /**
@@ -278,9 +279,7 @@ public final class Command {
      * @param  faint  whether to turn on the faint output.
      */
     final void setFaintOutput(final boolean faint) {
-        if (command.colors) {
-            command.out.write((faint ? X364.FAINT : X364.NORMAL).sequence());
-        }
+        command.color(faint ? X364.FAINT : X364.NORMAL);
     }
 
     /**
@@ -340,7 +339,7 @@ public final class Command {
     @SuppressWarnings("UseOfSystemOutOrSystemErr")
     private static void error(final String[] args, final Exception e) {
         final boolean debug = ArraysExt.containsIgnoreCase(args, Option.PREFIX 
+ "debug");
-        final PrintWriter err = CommandRunner.writer(System.console(), 
System.err);
+        final PrintWriter err = Environment.writer(System.console(), 
System.err);
         if (debug) {
             e.printStackTrace(err);
         } else {
diff --git 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/CommandRunner.java
 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/CommandRunner.java
index 8de71c01f9..f61aea42fa 100644
--- 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/CommandRunner.java
+++ 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/CommandRunner.java
@@ -24,19 +24,17 @@ import java.util.EnumMap;
 import java.util.TimeZone;
 import java.io.Console;
 import java.io.IOException;
-import java.io.PrintStream;
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.io.OutputStreamWriter;
 import java.nio.charset.Charset;
 import java.nio.file.Path;
 import java.nio.file.InvalidPathException;
+import org.apache.sis.system.Environment;
 import org.apache.sis.util.Locales;
 import org.apache.sis.util.Exceptions;
-import org.apache.sis.util.Workaround;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.internal.X364;
-import org.apache.sis.pending.jdk.JDK17;
 import org.apache.sis.storage.DataOptionKey;
 import org.apache.sis.storage.StorageConnector;
 
@@ -57,25 +55,6 @@ abstract class CommandRunner {
      */
     static final String TEST = "TEST";
 
-    /**
-     * Whether the use of the console writer should be avoided.
-     * In our tests with Java 21, sending non ASCII characters to the console 
writer in a Linux system
-     * from a JShell session resulted in wrong characters being printed, 
sometime followed by JShell errors.
-     * This flag is set only if the commands are run from JShell.
-     *
-     * @see #writer(Console, PrintStream)
-     */
-    @Workaround(library="jshell", version="21")
-    private static boolean avoidConsoleWriter;
-
-    /**
-     * Notifies the command runners that the use of console writer should be 
avoided.
-     * This method should be invoked in JShell environment only.
-     */
-    static void avoidConsoleWriter() {
-        avoidConsoleWriter = true;
-    }
-
     /**
      * The instance, used by {@link ResourcesDownloader} only.
      * We use this static field as a workaround for the fact that {@code 
ResourcesDownloader} is not
@@ -267,32 +246,15 @@ abstract class CommandRunner {
             err = out;
         } else {
             outputBuffer = null;
-            err = writer(console, System.err);
+            err = Environment.writer(console, System.err);
             if (explicitEncoding) {
                 out = new PrintWriter(new OutputStreamWriter(System.out, 
encoding), true);
             } else {
-                out = writer(console, System.out);
+                out = Environment.writer(console, System.out);
             }
         }
     }
 
-    /**
-     * Returns the console print writer, or the given alternative if the 
console cannot be used.
-     *
-     * @param  console   the value of {@link System#console()}, potentially 
null.
-     * @param  fallback  the fallback to use if the console cannot be used.
-     * @return the writer.
-     */
-    static PrintWriter writer(final Console console, final PrintStream 
fallback) {
-        if (console == null) {
-            return new PrintWriter(fallback, true);
-        } else if (avoidConsoleWriter) {
-            return new PrintWriter(new OutputStreamWriter(fallback, 
JDK17.charset(console)), true);
-        } else {
-            return console.writer();
-        }
-    }
-
     /**
      * Returns the value of the specified option as a character string.
      *
@@ -394,6 +356,30 @@ abstract class CommandRunner {
         return true;
     }
 
+    /**
+     * Prints the given color to the standard output stream if ANSI X3.64
+     * escape sequences are enabled, or does nothing otherwise.
+     *
+     * @param  code  the ANSI X3.64 color to print.
+     */
+    final void color(final X364 code) {
+        color(colors, out, code);
+    }
+
+    /**
+     * Prints the given color to the given stream if ANSI X3.64
+     * escape sequences are enabled, or does nothing otherwise.
+     *
+     * @param  colors  the condition for printing the color. Usually {@link 
#colors}.
+     * @param  out     where to print the ANSI sequence. Usually {@link #out} 
or {@link #err}.
+     * @param  code    the ANSI X3.64 color to print.
+     */
+    static void color(final boolean colors, final PrintWriter out, final X364 
code) {
+        if (colors) {
+            out.print(code.sequence());
+        }
+    }
+
     /**
      * Returns {@code true} if the command should use the standard input.
      */
diff --git 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/FormattedOutputCommand.java
 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/FormattedOutputCommand.java
index 3f15750b64..4bda2c2de6 100644
--- 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/FormattedOutputCommand.java
+++ 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/FormattedOutputCommand.java
@@ -45,6 +45,7 @@ import org.apache.sis.util.collection.TableColumn;
 import org.apache.sis.util.collection.TreeTable;
 import org.apache.sis.util.collection.TreeTableFormat;
 import org.apache.sis.util.resources.Errors;
+import org.apache.sis.util.internal.X364;
 import org.apache.sis.measure.Range;
 import org.apache.sis.setup.OptionKey;
 import org.apache.sis.xml.MarshallerPool;
@@ -241,7 +242,9 @@ abstract class FormattedOutputCommand extends CommandRunner 
{
                 if (warnings != null) {
                     out.flush();
                     err.println();
+                    color(colors, err, X364.FOREGROUND_YELLOW);
                     err.println(warnings.toString(locale));
+                    color(colors, err, X364.RESET);
                 }
                 break;
             }
diff --git 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/IdentifierCommand.java
 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/IdentifierCommand.java
index 60ad797a9a..7aa7e1dd01 100644
--- 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/IdentifierCommand.java
+++ 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/IdentifierCommand.java
@@ -226,16 +226,16 @@ final class IdentifierCommand extends 
FormattedOutputCommand {
             if (row != null) {
                 states.add(row.state);
                 final boolean warning = colors && 
row.state.text.startsWith("!");
-                if (warning) out.print(X364.FOREGROUND_RED.sequence());
+                color(warning, out, X364.FOREGROUND_RED);
                 out.print(row.state.text);
                 out.print(' ');
                 out.print(row.identifier);
-                if (warning) out.print(X364.FOREGROUND_DEFAULT.sequence());
-                if (colors)  out.print(X364.FOREGROUND_GRAY.sequence());
+                color(warning, out, X364.FOREGROUND_DEFAULT);
+                color(X364.FOREGROUND_GRAY);
                 out.print(CharSequences.spaces(width - 
row.identifier.length()));
                 out.print("| ");
                 out.println(row.description);
-                if (colors) out.print(X364.FOREGROUND_DEFAULT.sequence());
+                color(X364.FOREGROUND_DEFAULT);
             }
         }
         states.remove(State.VALID);
@@ -246,9 +246,9 @@ final class IdentifierCommand extends 
FormattedOutputCommand {
             final ResourceBundle resources = 
ResourceBundle.getBundle("org.apache.sis.console.IdentifierState", locale);
             for (final State state : states) {
                 final boolean warning = colors && state.text.startsWith("!");
-                if (warning) out.print(X364.FOREGROUND_RED.sequence());
+                color(warning, out, X364.FOREGROUND_RED);
                 out.print(state.text);
-                if (warning) out.print(X364.FOREGROUND_DEFAULT.sequence());
+                color(warning, out, X364.FOREGROUND_DEFAULT);
                 out.print(' ');
                 out.println(resources.getString(state.name()));
             }
diff --git 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/ResourcesDownloader.java
 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/ResourcesDownloader.java
index f33658b8f1..9e7ffc824b 100644
--- 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/ResourcesDownloader.java
+++ 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/ResourcesDownloader.java
@@ -25,6 +25,7 @@ import java.io.Console;
 import java.io.PrintWriter;
 import org.apache.sis.util.internal.X364;
 import org.apache.sis.system.Fallback;
+import org.apache.sis.system.Environment;
 import org.apache.sis.setup.OptionalInstallations;
 
 
@@ -85,7 +86,7 @@ public class ResourcesDownloader extends 
OptionalInstallations {
             colors = false;
         }
         console = System.console();
-        out = CommandRunner.writer(console, System.out);
+        out = Environment.writer(console, System.out);
     }
 
     /**
diff --git 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/SIS.java 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/SIS.java
index 1f864f03d9..f46c416759 100644
--- a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/SIS.java
+++ b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/SIS.java
@@ -18,13 +18,11 @@ package org.apache.sis.console;
 
 import java.util.EnumMap;
 import java.io.PrintWriter;
-import org.apache.sis.io.wkt.Colors;
 import org.apache.sis.util.Static;
+import org.apache.sis.util.Printable;
 import org.apache.sis.util.CharSequences;
 import org.apache.sis.util.resources.Errors;
-import org.apache.sis.util.internal.X364;
-import org.apache.sis.io.wkt.FormattableObject;
-import org.apache.sis.io.wkt.WKTFormat;
+import org.apache.sis.system.Environment;
 
 
 /**
@@ -49,7 +47,7 @@ public final class SIS extends Static {
      * Problems observed with Java 21 on Linux when printing non-ASCII 
characters.
      */
     static {
-        CommandRunner.avoidConsoleWriter();
+        Environment.avoidConsoleWriter();
     }
 
     /**
@@ -751,11 +749,6 @@ public final class SIS extends Static {
 
 
 
-    /**
-     * The object to use for formatting WKT objects, created when first needed.
-     */
-    private static WKTFormat wktFormat;
-
     /**
      * Prints the given object to the standard output stream.
      *
@@ -763,20 +756,12 @@ public final class SIS extends Static {
      */
     @SuppressWarnings("UseOfSystemOutOrSystemErr")
     public static void print(final Object value) {
-        final PrintWriter out = CommandRunner.writer(System.console(), 
System.out);
-        if (value instanceof FormattableObject) {
-            synchronized (SIS.class) {
-                if (wktFormat == null) {
-                    wktFormat = new WKTFormat(null, null);
-                    if (X364.isAnsiSupported()) {
-                        wktFormat.setColors(Colors.DEFAULT);
-                    }
-                }
-                out.print(wktFormat.format(value));
-            }
+        if (value instanceof Printable) {
+            ((Printable) value).print();
         } else {
+            final PrintWriter out = Environment.writer(System.console(), 
System.out);
             out.println(value);
+            out.flush();
         }
-        out.flush();
     }
 }
diff --git 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/TransformCommand.java
 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/TransformCommand.java
index 698ee27bfa..8b4b6aff11 100644
--- 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/TransformCommand.java
+++ 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/TransformCommand.java
@@ -272,16 +272,21 @@ final class TransformCommand extends 
FormattedOutputCommand {
     }
 
     /**
-     * Prints the character for commented lines.
+     * Appends the ANSI X3.64 sequence if colors are enabled, otherwise does 
nothing.
      */
-    private void printCommentLinePrefix() {
+    private void outHeader(final X364 code) {
         if (colors) {
-            outHeader.append(X364.FOREGROUND_GRAY.sequence());
+            outHeader.append(code.sequence());
         }
+    }
+
+    /**
+     * Prints the character for commented lines.
+     */
+    private void printCommentLinePrefix() {
+        outHeader(X364.FOREGROUND_GRAY);
         outHeader.append("# ");
-        if (colors) {
-            outHeader.append(X364.FOREGROUND_DEFAULT.sequence());
-        }
+        outHeader(X364.FOREGROUND_DEFAULT);
     }
 
     /**
@@ -311,15 +316,11 @@ final class TransformCommand extends 
FormattedOutputCommand {
         outHeader.append(object.getName().getCode());
         if (identifier != null) {
             outHeader.append(' ');
-            if (colors) {
-                outHeader.append(X364.FOREGROUND_CYAN.sequence());
-            }
+            outHeader(X364.FOREGROUND_CYAN);
             outHeader.append('(');
             outHeader.append(identifier);
             outHeader.append(')');
-            if (colors) {
-                outHeader.append(X364.FOREGROUND_DEFAULT.sequence());
-            }
+            outHeader(X364.FOREGROUND_DEFAULT);
         }
         if (!idRequired) {
             outHeader.nextLine();
@@ -338,13 +339,9 @@ final class TransformCommand extends 
FormattedOutputCommand {
     private void printOperations(final CoordinateOperation step, boolean 
isNext) {
         if (isNext) {
             isNext = false;
-            if (colors) {
-                outHeader.append(X364.FOREGROUND_GREEN.sequence());
-            }
+            outHeader(X364.FOREGROUND_GREEN);
             outHeader.append(" → ");
-            if (colors) {
-                outHeader.append(X364.FOREGROUND_DEFAULT.sequence());
-            }
+            outHeader(X364.FOREGROUND_DEFAULT);
         }
         if (!printNameAndIdentifier(step, true)) {
             if (step instanceof ConcatenatedOperation) {
@@ -369,13 +366,9 @@ final class TransformCommand extends 
FormattedOutputCommand {
                 accuracy = Formulas.LINEAR_TOLERANCE;
             }
             printHeader(Vocabulary.Keys.Accuracy);
-            if (colors) {
-                outHeader.append(X364.FOREGROUND_YELLOW.sequence());    // 
Same as Colors.DEFAULT for ElementKind.NUMBER
-            }
+            outHeader(X364.FOREGROUND_YELLOW);              // Same as 
Colors.DEFAULT for ElementKind.NUMBER
             outHeader.append(Double.toString(accuracy));
-            if (colors) {
-                outHeader.append(X364.FOREGROUND_DEFAULT.sequence());
-            }
+            outHeader(X364.FOREGROUND_DEFAULT);
             outHeader.append(" metres");
             outHeader.nextLine();
         }
@@ -461,11 +454,11 @@ final class TransformCommand extends 
FormattedOutputCommand {
         }
         if (quoted) fieldWidth -= 2;
         out.print(CharSequences.spaces(fieldWidth - text.length()));
-        if (colors) out.print(color.sequence());
+        color(color);
         if (quoted) out.print('"');
         out.print(text);
         if (quoted) out.print('"');
-        if (colors) out.print(X364.FOREGROUND_DEFAULT.sequence());
+        color(X364.FOREGROUND_DEFAULT);
     }
 
     /*
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/AbstractParser.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/AbstractParser.java
index fce3cbff30..439d33516c 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/AbstractParser.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/AbstractParser.java
@@ -239,8 +239,9 @@ abstract class AbstractParser implements Parser {
     @Override
     public final Object createFromWKT(final String wkt) throws 
FactoryException {
         final ParsePosition position = new ParsePosition(0);
+        @SuppressWarnings("LocalVariableHidesMemberVariable")
+        final Warnings warnings;
         Object result = null;
-        Warnings warnings;
         try {
             result = createFromWKT(wkt, position);
         } catch (ParseException exception) {
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/FormattableObject.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/FormattableObject.java
index ee68bd05db..893cf1ccf3 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/FormattableObject.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/FormattableObject.java
@@ -22,9 +22,11 @@ import java.io.PrintWriter;
 import java.util.concurrent.atomic.AtomicReference;
 import jakarta.xml.bind.annotation.XmlTransient;
 import org.apache.sis.util.Debug;
+import org.apache.sis.util.Printable;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.internal.X364;
 import org.apache.sis.util.internal.Constants;
+import org.apache.sis.system.Environment;
 
 
 /**
@@ -60,7 +62,7 @@ import org.apache.sis.util.internal.Constants;
  * </ul>
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
- * @version 1.4
+ * @version 1.5
  *
  * @see <a href="http://docs.opengeospatial.org/is/12-063r5/12-063r5.html";>WKT 
2 specification</a>
  * @see <a 
href="http://www.geoapi.org/3.0/javadoc/org/opengis/referencing/doc-files/WKT.html";>Legacy
 WKT 1</a>
@@ -68,7 +70,7 @@ import org.apache.sis.util.internal.Constants;
  * @since 0.4
  */
 @XmlTransient
-public abstract class FormattableObject {
+public abstract class FormattableObject implements Printable {
     /**
      * The formatter for the {@link #toWKT()} and {@link #toString()} methods. 
Formatters are not
      * thread-safe, consequently we must make sure that only one thread uses a 
given instance.
@@ -149,16 +151,13 @@ public abstract class FormattableObject {
      * <p>This is a convenience method for debugging purpose and for console 
applications.</p>
      */
     @Debug
+    @Override
     @SuppressWarnings("UseOfSystemOutOrSystemErr")
     public void print() {
         final Console console = System.console();
-        final PrintWriter out = (console != null) ? console.writer() : null;
-        final String wkt = formatWKT(Convention.WKT2_SIMPLIFIED, (out != null) 
&& X364.isAnsiSupported(), false);
-        if (out != null) {
-            out.println(wkt);
-        } else {
-            System.out.println(wkt);
-        }
+        final PrintWriter out = Environment.writer(console, System.out);
+        out.println(formatWKT(Convention.WKT2_SIMPLIFIED, (console != null) && 
X364.isAnsiSupported(), false));
+        out.flush();
     }
 
     /**
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/Formatter.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/Formatter.java
index 7d897f820d..2074da7e8e 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/Formatter.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/Formatter.java
@@ -19,7 +19,6 @@ package org.apache.sis.io.wkt;
 import java.util.Map;
 import java.util.Set;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -62,13 +61,11 @@ import org.apache.sis.math.Vector;
 import org.apache.sis.util.Classes;
 import org.apache.sis.util.Numbers;
 import org.apache.sis.util.Localized;
-import org.apache.sis.util.Exceptions;
 import org.apache.sis.util.Characters;
 import org.apache.sis.util.CharSequences;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.iso.Types;
 import org.apache.sis.util.resources.Errors;
-import org.apache.sis.util.resources.Vocabulary;
 import org.apache.sis.util.collection.IntegerList;
 import org.apache.sis.util.internal.X364;
 import org.apache.sis.util.internal.Numerics;
@@ -1855,23 +1852,11 @@ public class Formatter implements Localized {
             final String ln = System.lineSeparator();
             buffer.append(ln).append(ln);
             if (colors != null) {
-                
buffer.append(X364.BACKGROUND_RED.sequence()).append(X364.BOLD.sequence()).append('
 ');
+                buffer.append(X364.FOREGROUND_YELLOW.sequence());
             }
-            
Vocabulary.getResources(errorLocale).appendLabel(Vocabulary.Keys.Warnings, 
buffer);
+            buffer.append(warnings);
             if (colors != null) {
-                buffer.append(' 
').append(X364.RESET.sequence()).append(X364.FOREGROUND_RED.sequence());
-            }
-            buffer.append(ln);
-            final int n = warnings.getNumMessages();
-            final Set<String> done = new HashSet<>();
-            for (int i=0; i<n; i++) {
-                String message = 
Exceptions.getLocalizedMessage(warnings.getException(i), errorLocale);
-                if (message == null) {
-                    message = warnings.getMessage(i);
-                }
-                if (done.add(message)) {
-                    buffer.append("  • ").append(message).append(ln);
-                }
+                buffer.append(X364.RESET.sequence());
             }
         }
     }
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/parameter/ParameterFormat.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/parameter/ParameterFormat.java
index 9d9bd82a46..3e13498216 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/parameter/ParameterFormat.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/parameter/ParameterFormat.java
@@ -25,9 +25,9 @@ import java.util.Collection;
 import java.util.LinkedHashMap;
 import java.util.Locale;
 import java.util.TimeZone;
-import java.io.Console;
 import java.io.IOException;
 import java.io.UncheckedIOException;
+import java.io.PrintWriter;
 import java.text.Format;
 import java.text.NumberFormat;
 import java.text.FieldPosition;
@@ -46,15 +46,16 @@ import org.apache.sis.measure.Range;
 import org.apache.sis.io.TableAppender;
 import org.apache.sis.io.TabularFormat;
 import org.apache.sis.io.wkt.Colors;
+import org.apache.sis.system.Environment;
 import org.apache.sis.util.CharSequences;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.iso.Types;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.resources.Vocabulary;
-import org.apache.sis.referencing.IdentifiedObjects;
-import org.apache.sis.metadata.internal.NameToIdentifier;
 import org.apache.sis.util.internal.CollectionsExt;
 import org.apache.sis.util.internal.X364;
+import org.apache.sis.referencing.IdentifiedObjects;
+import org.apache.sis.metadata.internal.NameToIdentifier;
 import static org.apache.sis.util.collection.Containers.hashMapCapacity;
 
 // Specific to the geoapi-3.1 and geoapi-4.0 branches:
@@ -998,8 +999,7 @@ public class ParameterFormat extends TabularFormat<Object> {
      */
     @SuppressWarnings("UseOfSystemOutOrSystemErr")
     static void print(final Object object) {
-        final Console console = System.console();
-        final Appendable out = (console != null) ? console.writer() : 
System.out;
+        final PrintWriter out = Environment.writer();
         final ParameterFormat f = getSharedInstance(Colors.NAMING);
         try {
             f.format(object, out);
@@ -1007,6 +1007,7 @@ public class ParameterFormat extends 
TabularFormat<Object> {
             throw new UncheckedIOException(e);      // Should never happen 
since we are writing to stdout.
         }
         INSTANCE.set(f);
+        out.flush();
     }
 
     /**
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/parameter/Parameters.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/parameter/Parameters.java
index f57d0927c9..7f62e5f7d0 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/parameter/Parameters.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/parameter/Parameters.java
@@ -39,6 +39,7 @@ import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.ObjectConverters;
 import org.apache.sis.util.Classes;
 import org.apache.sis.util.Debug;
+import org.apache.sis.util.Printable;
 import org.apache.sis.util.resources.Errors;
 
 
@@ -104,11 +105,11 @@ import org.apache.sis.util.resources.Errors;
  * overriding one method has no impact on other methods.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.4
+ * @version 1.5
  * @since   0.4
  */
 @XmlTransient
-public abstract class Parameters implements ParameterValueGroup, Cloneable {
+public abstract class Parameters implements ParameterValueGroup, Cloneable, 
Printable {
     /**
      * For subclass constructors only.
      */
@@ -976,6 +977,7 @@ public abstract class Parameters implements 
ParameterValueGroup, Cloneable {
      * @since 0.7
      */
     @Debug
+    @Override
     public void print() {
         ParameterFormat.print(this);
     }
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/CacheRecord.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/CacheRecord.java
index 38a85cc74e..2891576e31 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/CacheRecord.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/CacheRecord.java
@@ -22,9 +22,9 @@ import java.util.Arrays;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.Collection;
-import java.io.Console;
 import java.io.PrintWriter;
 import org.opengis.referencing.IdentifiedObject;
+import org.apache.sis.system.Environment;
 import org.apache.sis.util.CharSequences;
 import org.apache.sis.util.Classes;
 import org.apache.sis.util.Debug;
@@ -119,8 +119,7 @@ final class CacheRecord implements Comparable<CacheRecord> {
         final CacheRecord[] records = list.toArray(CacheRecord[]::new);
         Arrays.sort(records);
         if (out == null) {
-            final Console c = System.console();
-            out = (c != null) ? c.writer() : new PrintWriter(System.out);
+            out = Environment.writer();
         }
         for (final CacheRecord record : records) {
             out.print(record.key);
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/ConcurrentAuthorityFactory.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/ConcurrentAuthorityFactory.java
index e5eb461322..cf58dad05b 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/ConcurrentAuthorityFactory.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/factory/ConcurrentAuthorityFactory.java
@@ -47,21 +47,22 @@ import org.opengis.metadata.citation.Citation;
 import org.opengis.parameter.ParameterDescriptor;
 import org.apache.sis.util.Classes;
 import org.apache.sis.util.Debug;
+import org.apache.sis.util.Printable;
 import org.apache.sis.util.Disposable;
 import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.util.resources.Errors;
+import org.apache.sis.util.resources.Messages;
 import org.apache.sis.util.logging.Logging;
 import org.apache.sis.util.logging.PerformanceLevel;
 import org.apache.sis.util.collection.Cache;
+import org.apache.sis.util.internal.CollectionsExt;
+import org.apache.sis.util.internal.StandardDateFormat;
 import org.apache.sis.metadata.simple.SimpleCitation;
 import org.apache.sis.system.ReferenceQueueConsumer;
 import org.apache.sis.system.DelayedExecutor;
 import org.apache.sis.system.DelayedRunnable;
 import org.apache.sis.system.Configuration;
 import org.apache.sis.system.Shutdown;
-import org.apache.sis.util.internal.CollectionsExt;
-import org.apache.sis.util.internal.StandardDateFormat;
-import org.apache.sis.util.resources.Errors;
-import org.apache.sis.util.resources.Messages;
 
 
 /**
@@ -102,7 +103,7 @@ import org.apache.sis.util.resources.Messages;
  * @since 0.7
  */
 public abstract class ConcurrentAuthorityFactory<DAO extends 
GeodeticAuthorityFactory>
-        extends GeodeticAuthorityFactory implements AutoCloseable
+        extends GeodeticAuthorityFactory implements AutoCloseable, Printable
 {
     /**
      * Duration of data access operations that should be logged, in 
nanoseconds.
@@ -2075,6 +2076,19 @@ public abstract class ConcurrentAuthorityFactory<DAO 
extends GeodeticAuthorityFa
         CacheRecord.printCacheContent(cache, out);
     }
 
+    /**
+     * Prints the cache content to the standard output stream.
+     * Keys are sorted by numerical order if possible, or alphabetical order 
otherwise.
+     * This method is used for debugging purpose only.
+     *
+     * @see #isCacheable(String, Object)
+     */
+    @Debug
+    @Override
+    public void print() {
+        printCacheContent(null);
+    }
+
     /**
      * A hook to be executed either when the {@link 
ConcurrentAuthorityFactory} is collected by the garbage collector,
      * when the Java Virtual Machine is shutdown, or when the module is 
uninstalled by the OSGi or Servlet container.
diff --git 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/util/j2d/ShapeUtilitiesViewer.java
 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/util/j2d/ShapeUtilitiesViewer.java
index 3259bdd8b1..e516ab4dbb 100644
--- 
a/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/util/j2d/ShapeUtilitiesViewer.java
+++ 
b/endorsed/src/org.apache.sis.referencing/test/org/apache/sis/referencing/util/j2d/ShapeUtilitiesViewer.java
@@ -18,7 +18,6 @@ package org.apache.sis.referencing.util.j2d;
 
 import java.util.Locale;
 import java.util.Random;
-import java.io.Console;
 import java.io.PrintWriter;
 import java.awt.BorderLayout;
 import java.awt.Color;
@@ -32,6 +31,7 @@ import java.awt.geom.Point2D;
 import javax.swing.JButton;
 import javax.swing.JFrame;
 import javax.swing.JPanel;
+import org.apache.sis.system.Environment;
 import org.apache.sis.util.CharSequences;
 
 
@@ -96,8 +96,7 @@ public final class ShapeUtilitiesViewer extends JPanel {
         input  = new Path2D.Float();
         output = new Path2D.Float();
         random = new Random();
-        final Console console = System.console();
-        out = (console != null) ? console.writer() : new 
PrintWriter(System.out);
+        out    = Environment.writer();
     }
 
     /**
diff --git 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/system/Environment.java 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/system/Environment.java
new file mode 100644
index 0000000000..c545d46fc2
--- /dev/null
+++ 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/system/Environment.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.system;
+
+import java.io.Console;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.OutputStreamWriter;
+import org.apache.sis.util.Static;
+import org.apache.sis.util.Workaround;
+import org.apache.sis.pending.jdk.JDK17;
+
+
+/**
+ * Method related to the environment where Apache SIS is executed.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ */
+public final class Environment extends Static {
+    /**
+     * Whether the use of the console writer should be avoided.
+     *
+     * @see #avoidConsoleWriter()
+     */
+    private static boolean avoidConsoleWriter;
+
+    /**
+     * Do not allow instantiation of this class.
+     */
+    private Environment() {
+    }
+
+    /**
+     * Notifies the command runners that the use of console writer should be 
avoided.
+     * In our tests with Java 21, sending non ASCII characters to the console 
writer in a Linux system
+     * from a JShell session resulted in wrong characters being printed, 
sometime followed by JShell errors.
+     * This flag is set only if the commands are run from JShell.
+     */
+    @Workaround(library="jshell", version="21")
+    public static void avoidConsoleWriter() {
+        avoidConsoleWriter = true;
+    }
+
+    /**
+     * Returns the console print writer, or the standard output stream if 
there is no console.
+     * Caller should flush the writer after use, because there is no guarantee 
that is will not
+     * be disposed by the garbage collector.
+     *
+     * @return the writer to use.
+     */
+    public static PrintWriter writer() {
+        return writer(System.console(), System.out);
+    }
+
+    /**
+     * Returns the console print writer, or the given alternative if the 
console cannot be used.
+     * Caller should flush the writer after use, because there is no guarantee 
that is will not
+     * be disposed by the garbage collector.
+     *
+     * @param  console   the value of {@link System#console()}, potentially 
null.
+     * @param  fallback  the fallback to use if the console cannot be used.
+     * @return the writer to use.
+     */
+    public static PrintWriter writer(final Console console, final PrintStream 
fallback) {
+        if (console == null) {
+            return new PrintWriter(fallback, true);
+        } else if (avoidConsoleWriter) {
+            return new PrintWriter(new OutputStreamWriter(fallback, 
JDK17.charset(console)), true);
+        } else {
+            return console.writer();
+        }
+    }
+}
diff --git 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/Printable.java 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/Printable.java
new file mode 100644
index 0000000000..afabe5b65a
--- /dev/null
+++ b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/Printable.java
@@ -0,0 +1,42 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.util;
+
+
+/**
+ * Object providing a {@code print()} method for sending a string 
representation to the standard output stream.
+ * A call to {@code object.print()} is often (but not necessarily) equivalent 
to {@code System.out.println(object)},
+ * except that <cite>ANSI escape codes</cite> (a.k.a. ECMA-48, ISO/IEC 6429 
and X3.64 standards) may be used for
+ * syntax coloring if the terminal support it. The character encoding may also 
be more suitable on some platforms.
+ * Finally, some implementations may be more verbose than {@code toString()}.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.5
+ * @since   1.5
+ */
+public interface Printable {
+    /**
+     * Prints a string representation of this object to the {@linkplain 
System#out standard output stream}.
+     * If a {@linkplain java.io.Console console} is attached to the running 
JVM (i.e. if the application is
+     * run from the command-line and the output is not redirected to a file) 
and if Apache SIS thinks that
+     * the console supports the ANSI escape codes (a.k.a. X3.64), then a 
syntax coloring may be applied.
+     *
+     * <p>This is a convenience method for debugging purpose and for console 
applications.</p>
+     */
+    @Debug
+    void print();
+}


Reply via email to