This is an automated email from the ASF dual-hosted git repository.

asf-gitbox-commits 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 7eee5e5401 Test warning by registering a listener instead of watching 
the logger.
7eee5e5401 is described below

commit 7eee5e5401d1ed74ddcf66043b230632917c990f
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Thu Sep 3 18:02:37 2026 +0900

    Test warning by registering a listener instead of watching the logger.
---
 .../sis/storage/landsat/LandsatStoreProvider.java  |  2 +-
 .../storage/landsat/LandsatStoreProviderTest.java  | 40 ++++++++---
 .../org/apache/sis/storage/event/WarningEvent.java | 18 ++++-
 .../org/apache/sis/storage/event/package-info.java |  4 +-
 .../sis/util/internal/AutoMessageFormat.java       | 22 ++++--
 .../apache/sis/util/internal/shared/Strings.java   | 78 ++++++++++++++++++++++
 .../apache/sis/util/logging/MonolineFormatter.java | 44 +-----------
 .../sis/util/resources/IndexedResourceBundle.java  | 18 +----
 8 files changed, 146 insertions(+), 80 deletions(-)

diff --git 
a/endorsed/src/org.apache.sis.storage.earthobservation/main/org/apache/sis/storage/landsat/LandsatStoreProvider.java
 
b/endorsed/src/org.apache.sis.storage.earthobservation/main/org/apache/sis/storage/landsat/LandsatStoreProvider.java
index 00293599ed..8fa79319a9 100644
--- 
a/endorsed/src/org.apache.sis.storage.earthobservation/main/org/apache/sis/storage/landsat/LandsatStoreProvider.java
+++ 
b/endorsed/src/org.apache.sis.storage.earthobservation/main/org/apache/sis/storage/landsat/LandsatStoreProvider.java
@@ -59,7 +59,7 @@ public class LandsatStoreProvider extends DataStoreProvider {
      *
      * @see #getLogger()
      */
-    static final Logger LOGGER = 
Logger.getLogger("org.apache.sis.storage.landsat");
+    private static final Logger LOGGER = 
Logger.getLogger("org.apache.sis.storage.landsat");
 
     /**
      * The parameter descriptor to be returned by {@link #getOpenParameters()}.
diff --git 
a/endorsed/src/org.apache.sis.storage.earthobservation/test/org/apache/sis/storage/landsat/LandsatStoreProviderTest.java
 
b/endorsed/src/org.apache.sis.storage.earthobservation/test/org/apache/sis/storage/landsat/LandsatStoreProviderTest.java
index d976dfac47..9932219678 100644
--- 
a/endorsed/src/org.apache.sis.storage.earthobservation/test/org/apache/sis/storage/landsat/LandsatStoreProviderTest.java
+++ 
b/endorsed/src/org.apache.sis.storage.earthobservation/test/org/apache/sis/storage/landsat/LandsatStoreProviderTest.java
@@ -29,13 +29,15 @@ import org.apache.sis.storage.OptionKey;
 import org.apache.sis.storage.ProbeResult;
 import org.apache.sis.storage.StorageConnector;
 import org.apache.sis.storage.DataStoreException;
+import org.apache.sis.storage.event.StoreListener;
+import org.apache.sis.storage.event.WarningEvent;
 
 // Test dependencies
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.io.TempDir;
 import static org.junit.jupiter.api.Assertions.*;
 import static org.apache.sis.test.Assertions.assertSingleton;
-import org.apache.sis.test.TestCaseWithLogs;
+import org.apache.sis.test.TestCase;
 
 
 /**
@@ -44,12 +46,16 @@ import org.apache.sis.test.TestCaseWithLogs;
  * @author  Martin Desruisseaux (Geomatys)
  */
 @SuppressWarnings("exports")
-public final class LandsatStoreProviderTest extends TestCaseWithLogs {
+public final class LandsatStoreProviderTest extends TestCase implements 
StoreListener<WarningEvent> {
+    /**
+     * Whether a warning is expected.
+     */
+    private boolean isWarningExpected;
+
     /**
      * Creates a new test case.
      */
     public LandsatStoreProviderTest() {
-        super(LandsatStoreProvider.LOGGER);
     }
 
     /**
@@ -63,7 +69,6 @@ public final class LandsatStoreProviderTest extends 
TestCaseWithLogs {
         connector.setOption(OptionKey.ENCODING, StandardCharsets.UTF_8);
         final var provider = new LandsatStoreProvider();
         assertEquals(ProbeResult.SUPPORTED, provider.probeContent(connector));
-        loggings.assertNoUnexpectedLog();
     }
 
     /**
@@ -77,7 +82,7 @@ public final class LandsatStoreProviderTest extends 
TestCaseWithLogs {
      * @throws IOException if an error occurred while writing the temporary 
file.
      * @throws DataStoreException if an error occurred while reading the 
temporary file.
      */
-    private static Collection<Path> readSingleBand(final Path tmpDir, final 
String sceneName, final String tiffFile)
+    private Collection<Path> readSingleBand(final Path tmpDir, final String 
sceneName, final String tiffFile)
             throws IOException, DataStoreException
     {
         final Path sceneDir = 
Files.createDirectories(tmpDir.resolve(sceneName));
@@ -92,6 +97,7 @@ public final class LandsatStoreProviderTest extends 
TestCaseWithLogs {
 
         final var paths = new ArrayList<Path>();
         try (var store = new LandsatStore(null, new 
StorageConnector(sceneDir))) {
+            store.addListener(WarningEvent.class, this);
             for (Resource component : store.components()) {
                 Aggregate group = assertInstanceOf(Aggregate.class, component);
                 Resource band = assertSingleton(group.components());
@@ -109,15 +115,27 @@ public final class LandsatStoreProviderTest extends 
TestCaseWithLogs {
      * @throws DataStoreException if an error occurred while reading the 
temporary file.
      */
     @Test
-    public void testBandPathValidation(@TempDir final Path tmpDir)
-            throws IOException, DataStoreException
-    {
+    public void testBandPathValidation(@TempDir final Path tmpDir) throws 
IOException, DataStoreException {
         final Path expected = tmpDir.resolve("valid", "B1.TIFF");
         final Path actual = assertSingleton(readSingleBand(tmpDir, "valid", 
"B1.TIFF"));
         assertEquals(expected.toAbsolutePath(), actual.toAbsolutePath());
-        loggings.assertNoUnexpectedLog();
+        isWarningExpected = true;
         assertTrue(readSingleBand(tmpDir, "invalid", 
"../outside/secret.tiff").isEmpty());
-        loggings.assertNextLogContains("../outside/secret.tiff", "Coastal 
Aerosol");
-        loggings.assertNoUnexpectedLog();
+        assertFalse(isWarningExpected, "Warning should have been emitted.");
+    }
+
+    /**
+     * Invoked when a warning is emitted.
+     * Verifies if the warning was expected, and if so, if it contains the 
expected message.
+     *
+     * @param  event  the warning.
+     */
+    @Override
+    public void eventOccurred(final WarningEvent event) {
+        final String message = event.getMessage(null);
+        assertTrue(isWarningExpected, message);
+        assertTrue(message.contains("../outside/secret.tiff"), 
"../outside/secret.tiff");
+        assertTrue(message.contains("Coastal Aerosol"), "Coastal Aerosol");
+        isWarningExpected = false;
     }
 }
diff --git 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/event/WarningEvent.java
 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/event/WarningEvent.java
index 910ec2dd87..a2b68b629e 100644
--- 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/event/WarningEvent.java
+++ 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/event/WarningEvent.java
@@ -16,10 +16,12 @@
  */
 package org.apache.sis.storage.event;
 
+import java.util.Locale;
 import java.util.Objects;
 import java.util.logging.Level;
 import java.util.logging.LogRecord;
 import org.apache.sis.storage.Resource;
+import org.apache.sis.util.internal.shared.Strings;
 
 
 /**
@@ -29,8 +31,8 @@ import org.apache.sis.storage.Resource;
  * {@linkplain LogRecord#getInstant() log time}, <i>etc.</i>) in addition of 
warning message.
  *
  * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.7
  * @since   1.0
- * @version 1.0
  */
 public class WarningEvent extends StoreEvent {
     /**
@@ -66,6 +68,18 @@ public class WarningEvent extends StoreEvent {
         return description;
     }
 
+    /**
+     * Returns the warning message, preferably in the given locale.
+     * The locale is used on a best-effort basis and may be ignored if 
unsupported.
+     *
+     * @param  locale  the desired locale, or {@code null} for the default 
locale.
+     * @return the warning message, preferably in the given locale.
+     * @since 1.7
+     */
+    public String getMessage(final Locale locale) {
+        return Strings.formatMessage(description, locale, null, null, null);
+    }
+
     /**
      * Returns a string representation of this warning for debugging purpose.
      *
@@ -73,7 +87,7 @@ public class WarningEvent extends StoreEvent {
      */
     @Override
     public String toString() {
-        final StringBuilder b = new StringBuilder();
+        final var b = new StringBuilder();
         final Level level = description.getLevel();
         if (level != null) {
             b.append(level.getLocalizedName()).append(": ");
diff --git 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/event/package-info.java
 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/event/package-info.java
index f38c736143..35d79d8856 100644
--- 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/event/package-info.java
+++ 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/event/package-info.java
@@ -35,7 +35,7 @@
  *
  * @author  Johann Sorel (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
- * @since   1.7
- * @version 1.0
+ * @version 1.7
+ * @since   1.0
  */
 package org.apache.sis.storage.event;
diff --git 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/AutoMessageFormat.java
 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/AutoMessageFormat.java
index 3b86c72da0..7e51838f3f 100644
--- 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/AutoMessageFormat.java
+++ 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/AutoMessageFormat.java
@@ -37,6 +37,13 @@ public final class AutoMessageFormat extends MessageFormat {
      */
     private transient NumberFormat[] formatsToConfigure;
 
+    /**
+     * Value of the last call to {@link MessageFormat#applyPattern(String)}. 
Saved in order to avoid
+     * calling {@code super.applyPattern(String)} in the common case where the 
same message is logged
+     * many times with different arguments.
+     */
+    private transient String currentPattern;
+
     /**
      * Whether the {@link #formatsToConfigure} needs to be updated.
      */
@@ -49,6 +56,7 @@ public final class AutoMessageFormat extends MessageFormat {
      */
     public AutoMessageFormat(final String pattern) {
         super(pattern);
+        currentPattern = pattern;
     }
 
     /**
@@ -59,17 +67,23 @@ public final class AutoMessageFormat extends MessageFormat {
      */
     public AutoMessageFormat(final String pattern, final Locale locale) {
         super(pattern, locale);
+        currentPattern = pattern;
     }
 
     /**
      * Modifies the pattern used by this message format.
+     * This method does nothing if this formatter is already applying the 
given pattern.
      *
      * @param pattern the new pattern for this message format
      */
     @Override
     public void applyPattern(final String pattern) {
-        super.applyPattern(pattern);
-        update = true;
+        if (!pattern.equals(currentPattern)) {
+            currentPattern = null;
+            super.applyPattern(pattern);
+            currentPattern = pattern;
+            update = true;
+        }
     }
 
     /**
@@ -82,7 +96,7 @@ public final class AutoMessageFormat extends MessageFormat {
         if (update) {
             formatsToConfigure = null;
             final Format[] fc = getFormatsByArgumentIndex();
-            for (int i=fc.length; --i >= 0;) {
+            for (int i = fc.length; --i >= 0;) {
                 final Format c = fc[i];
                 if (c instanceof NumberFormat) {
                     if (formatsToConfigure == null) {
@@ -93,7 +107,7 @@ public final class AutoMessageFormat extends MessageFormat {
             }
         }
         if (formatsToConfigure != null) {
-            for (int i=Math.min(formatsToConfigure.length, arguments.length); 
--i >= 0;) {
+            for (int i = Math.min(formatsToConfigure.length, 
arguments.length); --i >= 0;) {
                 final NumberFormat f = formatsToConfigure[i];
                 if (f != null) {
                     final Object value = arguments[i];
diff --git 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/shared/Strings.java
 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/shared/Strings.java
index d37ee4cd22..ed17df182a 100644
--- 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/shared/Strings.java
+++ 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/shared/Strings.java
@@ -17,14 +17,24 @@
 package org.apache.sis.util.internal.shared;
 
 import java.lang.reflect.Array;
+import java.text.FieldPosition;
+import java.text.MessageFormat;
 import java.util.Locale;
 import java.util.Formatter;
 import java.util.FormattableFlags;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+import java.util.function.Consumer;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
 import org.apache.sis.util.Classes;
 import org.apache.sis.util.ArraysExt;
 import org.apache.sis.util.Characters;
 import org.apache.sis.util.CharSequences;
+import org.apache.sis.util.logging.Logging;
+import org.apache.sis.util.internal.AutoMessageFormat;
 import org.apache.sis.pending.jdk.JDK15;
+import org.apache.sis.system.Modules;
 
 
 /**
@@ -458,4 +468,72 @@ public final class Strings {
         }
         formatter.format(format, args);
     }
+
+    /**
+     * Returns the localized message from the given log record.
+     * This is the implementation of {@link 
org.apache.sis.util.logging.MonolineFormatter#formatMessage(LogRecord)}.
+     * See the javadoc of that method for more information.
+     *
+     * <h4>Source</h4>
+     * This method does the same work as {@link 
java.util.logging.Formatter#formatMessage(LogRecord)} except for the
+     * synchronization lock, the reuse of existing {@link MessageFormat} and 
{@link StringBuffer} instances, and not
+     * catching formatting exceptions (we want to know if our messages have a 
problem).
+     *
+     * @param  record  the log record from which to get a localized message.
+     * @param  locale  the desired locale, or {@code null} for the default.
+     * @param  buffer  a preexisting buffer to reuse, or {@code null} if none.
+     * @param  format  a preexisting formatter to reuse, or {@code null} if 
none.
+     * @param  cache   where to cache new {@link AutoMessageFormat}, of {@code 
null} if none.
+     * @return the localized message.
+     */
+    public static String formatMessage(final LogRecord record, final Locale 
locale,
+            StringBuffer buffer, AutoMessageFormat format, 
Consumer<AutoMessageFormat> cache)
+    {
+        String message = record.getMessage();
+        ResourceBundle resources = null;
+        if (locale != null) {
+            final String baseName = record.getResourceBundleName();
+            if (baseName != null) try {
+                resources = ResourceBundle.getBundle(baseName, locale);
+            } catch (MissingResourceException e) {
+                
Logging.ignorableException(Logger.getLogger(Modules.UTILITIES), null, null, e);
+            }
+        }
+        if (resources == null) {
+            resources = record.getResourceBundle();
+        }
+        if (resources != null) {
+            message = resources.getString(message);
+        }
+        final Object[] parameters = record.getParameters();
+        if (parameters != null && parameters.length != 0) {
+            int i = message.indexOf('{');
+            if (i >= 0 && ++i < message.length()) {
+                final char c = message.charAt(i);
+                if (c >= '0' && c <= '9') {
+                    if (buffer == null) {
+                        buffer = new StringBuffer();
+                    }
+                    synchronized (buffer) {
+                        if (format != null) {
+                            format.applyPattern(message);
+                        } else {
+                            format = new AutoMessageFormat(message);
+                            if (cache != null) {
+                                cache.accept(format);
+                            }
+                        }
+                        final int base = buffer.length();
+                        try {
+                            format.configure(parameters);
+                            message = format.format(parameters, buffer, new 
FieldPosition(0)).substring(base);
+                        } finally {
+                            buffer.setLength(base);
+                        }
+                    }
+                }
+            }
+        }
+        return message;
+    }
 }
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 f6f6d83b35..ed79855b93 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
@@ -30,7 +30,6 @@ import java.util.Arrays;
 import java.util.TreeMap;
 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;
@@ -246,13 +245,6 @@ public class MonolineFormatter extends Formatter {
      */
     private transient AutoMessageFormat messageFormat;
 
-    /**
-     * Value of the last call to {@link MessageFormat#applyPattern(String)}. 
Saved in order to avoid
-     * calling {@code applyPattern(String)} in the common case where the same 
message is logged many
-     * times with different arguments.
-     */
-    private transient String messagePattern;
-
     /**
      * One of the following constants: {@link #NO_SOURCE}, {@link 
#LOGGER_SHORT},
      * {@link #LOGGER_LONG}, {@link #CLASS_SHORT}, {@link #CLASS_LONG} or 
{@link #METHOD}.
@@ -855,41 +847,7 @@ loop:   for (int i=0; ; i++) {
      */
     @Override
     public String formatMessage(final LogRecord record) {
-        /*
-         * Same work as java.util.logging.Formatter.formatMessage(LogRecord) 
except for the synchronization lock,
-         * the reuse of existing MessageFormat and StringBuffer instances, and 
not catching formatting exceptions
-         * (we want to know if our messages have a problem).
-         */
-        String message = record.getMessage();
-        ResourceBundle resources = record.getResourceBundle();
-        if (resources != null) {
-            message = resources.getString(message);
-        }
-        final Object[] parameters = record.getParameters();
-        if (parameters != null && parameters.length != 0) {
-            int i = message.indexOf('{');
-            if (i >= 0 && ++i < message.length()) {
-                final char c = message.charAt(i);
-                if (c >= '0' && c <= '9') {
-                    synchronized (buffer) {
-                        if (messageFormat == null) {
-                            messageFormat = new AutoMessageFormat(message);
-                        } else if (!message.equals(messagePattern)) {
-                            messageFormat.applyPattern(message);
-                        }
-                        messagePattern = message;
-                        final int base = buffer.length();
-                        try {
-                            messageFormat.configure(parameters);
-                            message = messageFormat.format(parameters, buffer, 
new FieldPosition(0)).substring(base);
-                        } finally {
-                            buffer.setLength(base);
-                        }
-                    }
-                }
-            }
-        }
-        return message;
+        return Strings.formatMessage(record, null, buffer, messageFormat, 
(created) -> messageFormat = created);
     }
 
     /**
diff --git 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/IndexedResourceBundle.java
 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/IndexedResourceBundle.java
index 034f41e792..998e711792 100644
--- 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/IndexedResourceBundle.java
+++ 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/IndexedResourceBundle.java
@@ -131,13 +131,6 @@ public abstract class IndexedResourceBundle extends 
ResourceBundle implements Lo
      */
     private transient AutoMessageFormat format;
 
-    /**
-     * The key of the last resource requested. If the same resource is 
requested multiple times,
-     * knowing its key allows us to avoid invoking the costly {@link 
MessageFormat#applyPattern}
-     * method.
-     */
-    private transient short lastKey;
-
     /**
      * Constructs a new resource bundle loading data from a UTF file derived 
from the class name.
      */
@@ -557,18 +550,9 @@ public abstract class IndexedResourceBundle extends 
ResourceBundle implements Lo
         final Object[] arguments = toArray(arg0);
         synchronized (this) {
             if (format == null) {
-                /*
-                 * Constructs a new MessageFormat for formatting the arguments.
-                 */
                 format  = new AutoMessageFormat(pattern, getLocale());
-                lastKey = key;
-            } else if (key != lastKey) {
-                /*
-                 * Method MessageFormat.applyPattern(…) is costly! We will 
avoid
-                 * calling it again if the format already has the right 
pattern.
-                 */
+            } else {
                 format.applyPattern(pattern);
-                lastKey = key;
             }
             try {
                 format.configure(arguments);

Reply via email to