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

commit ef16287fad0648ce31e6ca9c646b6571645967d6
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Sat Apr 25 00:11:00 2026 +0200

    Avoid repeating the same warnings twice after WKT formatting of a CRS.
---
 .../main/org/apache/sis/io/wkt/Formatter.java      |   5 +-
 .../main/org/apache/sis/io/wkt/WKTFormat.java      |   6 +-
 .../main/org/apache/sis/io/wkt/Warnings.java       | 145 +++++++++++++--------
 .../main/org/apache/sis/io/wkt/package-info.java   |   2 +-
 4 files changed, 101 insertions(+), 57 deletions(-)

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 cbab020e1f..45aa889fc3 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
@@ -1941,7 +1941,8 @@ public class Formatter implements Localized {
     }
 
     /**
-     * Appends the warnings after the WKT string. If there are no warnings, 
then this method does nothing.
+     * Appends the warnings after the <abbr>WKT</abbr> string.
+     * If there are no warnings, then this method does nothing.
      * If this method is invoked, then it shall be the last method before 
{@link #toWKT()}.
      */
     final void appendWarnings() throws IOException {
@@ -1980,7 +1981,7 @@ public class Formatter implements Localized {
     public String toString() {
         final StringBuilder b = new 
StringBuilder(Classes.getShortClassName(this));
         String separator = " of ";
-        for (int i=enclosingElements.size(); --i >= 0;) {
+        for (int i = enclosingElements.size(); --i >= 0;) {
             
b.append(separator).append(Classes.getShortClassName(enclosingElements.get(i)));
             separator = " inside ";
         }
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/WKTFormat.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/WKTFormat.java
index 3a858540fd..1e109bb5db 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/WKTFormat.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/WKTFormat.java
@@ -122,7 +122,7 @@ import org.opengis.metadata.Identifier;
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @author  Rémi Eve (IRD)
- * @version 1.5
+ * @version 1.7
  * @since   0.4
  */
 public class WKTFormat extends CompoundFormat<Object> {
@@ -1195,7 +1195,7 @@ public class WKTFormat extends CompoundFormat<Object> {
              * We can avoid the call to `Warnings.publish()` because we know 
that we are not keeping a
              * reference for long, so we do not need to copy the 
`AbstractParser.ignoredElements` map.
              */
-            final LogRecord record = new LogRecord(Level.WARNING, 
warnings.toString());
+            final var record = new LogRecord(Level.WARNING, 
warnings.toString());
             Logging.completeAndLog(LOGGER, classe, method, record);
         }
     }
@@ -1215,7 +1215,7 @@ public class WKTFormat extends CompoundFormat<Object> {
      */
     @Override
     public WKTFormat clone() {
-        final WKTFormat clone = (WKTFormat) super.clone();
+        final var clone = (WKTFormat) super.clone();
         clone.clear();
         clone.factories = null;                             // Not 
thread-safe; clone needs its own.
         clone.formatter = null;                             // Do not share 
the formatter.
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/Warnings.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/Warnings.java
index 5a53de67df..acf831c5d2 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/Warnings.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/Warnings.java
@@ -19,10 +19,9 @@ package org.apache.sis.io.wkt;
 import java.util.Locale;
 import java.util.Set;
 import java.util.Map;
-import java.util.ArrayList;
 import java.util.Collections;
 import java.util.LinkedHashMap;
-import java.util.Iterator;
+import java.util.LinkedHashSet;
 import java.util.Objects;
 import java.io.Serializable;
 import org.opengis.metadata.Identifier;
@@ -37,7 +36,7 @@ import org.apache.sis.util.resources.Vocabulary;
 
 
 /**
- * Warnings that occurred during a <i>Well Known Text</i> (WKT) parsing or 
formatting.
+ * Warnings that occurred during a <i>Well Known Text</i> (<abbr>WKT</abbr>) 
parsing or formatting.
  * Information provided by this object include:
  *
  * <ul>
@@ -47,7 +46,7 @@ import org.apache.sis.util.resources.Vocabulary;
  * </ul>
  *
  * <h2>Example</h2>
- * After parsing the following WKT:
+ * After parsing the following <abbr>WKT</abbr>:
  *
  * {@snippet lang="wkt" :
  *   GeographicCRS[“WGS 84”,
@@ -67,7 +66,7 @@ import org.apache.sis.util.resources.Vocabulary;
  * </ul>
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.5
+ * @version 1.7
  *
  * @see WKTFormat#getWarnings()
  *
@@ -77,7 +76,7 @@ public final class Warnings implements Localized, 
Serializable {
     /**
      * For cross-version compatibility.
      */
-    private static final long serialVersionUID = -1825161781642905329L;
+    private static final long serialVersionUID = -7472758787116735634L;
 
     /**
      * The locale in which warning messages are reported.
@@ -100,20 +99,59 @@ public final class Warnings implements Localized, 
Serializable {
      */
     private String root;
 
+    /**
+     * An item in the {@link #message} list.
+     */
+    private static final class Message extends 
org.apache.sis.pending.jdk.Record implements Serializable {
+        private static final long serialVersionUID = 5917311137600422421L;
+
+        /** An optional message. */
+        @SuppressWarnings("serial")
+        final InternationalString text;
+
+        /** An optional warning cause. */
+        final Exception cause;
+
+        Message(final InternationalString text, final Exception cause) {
+            this.text = text;
+            this.cause = cause;
+        }
+
+        @Override
+        public int hashCode() {
+            return Objects.hash(text, cause);
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj instanceof Message) {
+                final var other = (Message) obj;
+                return Objects.equals(text, other.text) && 
Objects.equals(cause, other.cause);
+            }
+            return false;
+        }
+    }
+
     /**
      * Warning messages or exceptions emitted during parsing or formatting.
-     * Objects in this list must be a sequence of the following tuple:
+     * Objects in this list must be a sequence of the following fields:
      *
      * <ul>
      *   <li>An optional message as an {@link InternationalString}.</li>
      *   <li>An optional warning cause as an {@link Exception}.</li>
      * </ul>
      *
-     * Any element of the above tuple can be null, but at least one element 
must be non-null.
+     * Any element of the above record can be null, but at least one element 
must be non-null.
      *
      * @see #add(InternationalString, Exception, String[])
      */
-    private final ArrayList<Object> messages;
+    private final LinkedHashSet<Message> messages;
+
+    /**
+     * The content of {@link #messages} as an array,
+     * for compatibility with <abbr>API</abbr> working with indexes.
+     */
+    private transient Message[] messageArray;
 
     /**
      * The keywords of elements in which exception occurred.
@@ -151,7 +189,7 @@ public final class Warnings implements Localized, 
Serializable {
         this.isParsing       = isParsing;
         this.ignoredElements = ignoredElements;
         exceptionSources = new LinkedHashMap<>(4);
-        messages = new ArrayList<>();
+        messages = new LinkedHashSet<>(4);
     }
 
     /**
@@ -183,11 +221,10 @@ public final class Warnings implements Localized, 
Serializable {
      */
     final void add(final InternationalString message, final Exception cause, 
final String[] source) {
         assert (message != null) || (cause != null);
-        messages.add(message);
-        messages.add(cause);
-        if (cause != null) {
+        if (messages.add(new Message(message, cause)) && cause != null) {
             exceptionSources.put(cause, source);
         }
+        messageArray = null;
     }
 
     /**
@@ -231,7 +268,17 @@ public final class Warnings implements Localized, 
Serializable {
      * @return the number of warning messages.
      */
     public final int getNumMessages() {
-        return (messages != null) ? messages.size() / 2 : 0;
+        return messages.size();
+    }
+
+    /**
+     * Returns the message at thegiven index.
+     */
+    private Message message(final int index) {
+        if (messageArray == null) {
+            messageArray = messages.toArray(Message[]::new);
+        }
+        return messageArray[index];
     }
 
     /**
@@ -240,14 +287,13 @@ public final class Warnings implements Localized, 
Serializable {
      * @param  index 0 for the first warning, 1 for the second warning, 
<i>etc.</i> until {@link #getNumMessages()} - 1.
      * @return the <var>i</var>-th warning message.
      */
-    public String getMessage(int index) {
-        Objects.checkIndex(index, getNumMessages());
-        index *= 2;
-        final var i18n = (InternationalString) messages.get(index);
+    public String getMessage(final int index) {
+        final Message item = message(index);
+        final var i18n = item.text;
         if (i18n != null) {
             return i18n.toString(errorLocale);
         } else {
-            final Exception cause = (Exception) messages.get(index + 1);
+            final Exception cause = item.cause;
             final String[] sources = exceptionSources.get(cause);           // 
See comment in 'toString(Locale)'.
             if (sources != null) {
                 return 
Errors.forLocale(errorLocale).getString(Errors.Keys.UnparsableStringInElement_2,
 sources);
@@ -264,8 +310,7 @@ public final class Warnings implements Localized, 
Serializable {
      * @return the exception which was the cause of the warning message, or 
{@code null} if none.
      */
     public Exception getException(final int index) {
-        Objects.checkIndex(index, getNumMessages());
-        return (Exception) messages.get(index*2 + 1);
+        return message(index).cause;
     }
 
     /**
@@ -275,7 +320,7 @@ public final class Warnings implements Localized, 
Serializable {
      * @return the non-fatal exceptions that occurred.
      */
     public Set<Exception> getExceptions() {
-        return (exceptionSources != null) ? exceptionSources.keySet() : 
Collections.emptySet();
+        return Collections.unmodifiableSet(exceptionSources.keySet());
     }
 
     /**
@@ -289,20 +334,20 @@ public final class Warnings implements Localized, 
Serializable {
      * @return the keywords of the WKT element where the given exception 
occurred, or {@code null} if unknown.
      */
     public String[] getExceptionSource(final Exception ex) {
-        return (exceptionSources != null) ? exceptionSources.get(ex) : null;
+        return exceptionSources.get(ex);
     }
 
     /**
-     * Returns the keywords of all unknown elements found during the WKT 
parsing.
+     * Returns the keywords of all unknown elements found during the 
<abbr>WKT</abbr> parsing.
      *
-     * @return the keywords of unknown WKT elements, or an empty set if none.
+     * @return the keywords of unknown <abbr>WKT</abbr> elements, or an empty 
set if none.
      */
     public Set<String> getUnknownElements() {
         return ignoredElements.keySet();
     }
 
     /**
-     * Returns the keyword of WKT elements that contains the given unknown 
element.
+     * Returns the keyword of <abbr>WKT</abbr> elements that contains the 
given unknown element.
      * If the given element is not one of the value returned by {@link 
#getUnknownElements()},
      * then this method returns {@code null}.
      *
@@ -339,34 +384,32 @@ public final class Warnings implements Localized, 
Serializable {
         final Messages resources   = Messages.forLocale(locale);
         buffer.append(resources.getString(isParsing ? 
Messages.Keys.IncompleteParsing_1
                                                     : 
Messages.Keys.NonConformFormatting_1, root));
-        if (messages != null) {
-            for (final Iterator<?> it = messages.iterator(); it.hasNext();) {
-                final var i18n = (InternationalString) it.next();
-                Exception cause = (Exception) it.next();
-                final String message;
-                if (i18n != null) {
-                    message = i18n.toString(locale);
+        for (final Message item : messages) {
+            final InternationalString i18n = item.text;
+            Exception cause = item.cause;
+            final String message;
+            if (i18n != null) {
+                message = i18n.toString(locale);
+            } else {
+                /*
+                 * If there is no message, then we must have at least an 
exception.
+                 * Consequently, a NullPointerException in following line 
would be a bug.
+                 */
+                final String[] sources = exceptionSources.get(cause);
+                if (sources != null) {
+                    message = 
Errors.forLocale(locale).getString(Errors.Keys.UnparsableStringInElement_2, 
sources);
                 } else {
-                    /*
-                     * If there is no message, then we must have at least an 
exception.
-                     * Consequently, a NullPointerException in following line 
would be a bug.
-                     */
-                    final String[] sources = exceptionSources.get(cause);
-                    if (sources != null) {
-                        message = 
Errors.forLocale(locale).getString(Errors.Keys.UnparsableStringInElement_2, 
sources);
-                    } else {
-                        message = cause.toString();
-                        cause = null;
-                    }
+                    message = cause.toString();
+                    cause = null;
                 }
-                buffer.append(lineSeparator).append(" • ").append(message);
-                if (cause != null) {
-                    String details = Exceptions.getLocalizedMessage(cause, 
locale);
-                    if (details == null) {
-                        details = Classes.getShortClassName(cause);
-                    }
-                    buffer.append(lineSeparator).append("   ").append(details);
+            }
+            buffer.append(lineSeparator).append(" • ").append(message);
+            if (cause != null) {
+                String details = Exceptions.getLocalizedMessage(cause, locale);
+                if (details == null) {
+                    details = Classes.getShortClassName(cause);
                 }
+                buffer.append(lineSeparator).append("   ").append(details);
             }
         }
         /*
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/package-info.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/package-info.java
index f713292399..e53edbfd09 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/package-info.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/io/wkt/package-info.java
@@ -82,7 +82,7 @@
  * @author  Martin Desruisseaux (IRD, Geomatys)
  * @author  Rémi Eve (IRD)
  * @author  Rueben Schulz (UBC)
- * @version 1.6
+ * @version 1.7
  * @since   0.4
  */
 package org.apache.sis.io.wkt;

Reply via email to