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 49a5bd39ee0c372b563e8686984cc889720272a9
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Tue May 5 14:47:27 2026 +0200

    Workaround an `IllegalAccessException` caused by module boundaries.
    https://issues.apache.org/jira/browse/SIS-632
---
 .../resources/IndexedResourceCompiler.java         | 44 ++++++++++++++--------
 .../org/apache/sis/cloud/aws/s3/Resources.java     | 14 +++++++
 .../org/apache/sis/feature/internal/Resources.java | 14 +++++++
 .../apache/sis/metadata/internal/Resources.java    | 14 +++++++
 .../org/apache/sis/map/internal/Resources.java     | 14 +++++++
 .../referencing/gazetteer/internal/Resources.java  | 14 +++++++
 .../apache/sis/referencing/internal/Resources.java | 14 +++++++
 .../apache/sis/storage/geotiff/base/Resources.java | 14 +++++++
 .../sis/storage/netcdf/internal/Resources.java     | 14 +++++++
 .../apache/sis/storage/sql/feature/Resources.java  | 14 +++++++
 .../org/apache/sis/storage/internal/Resources.java | 14 +++++++
 .../main/org/apache/sis/util/resources/Errors.java | 14 +++++++
 .../apache/sis/util/resources/KeyConstants.java    | 37 ++++++++++--------
 .../org/apache/sis/util/resources/Messages.java    | 14 +++++++
 .../org/apache/sis/util/resources/Vocabulary.java  | 14 +++++++
 .../org/apache/sis/gui/internal/Resources.java     | 14 +++++++
 .../org/apache/sis/storage/panama/Resources.java   | 14 +++++++
 17 files changed, 259 insertions(+), 32 deletions(-)

diff --git 
a/buildSrc/src/main/java/org/apache/sis/buildtools/resources/IndexedResourceCompiler.java
 
b/buildSrc/src/main/java/org/apache/sis/buildtools/resources/IndexedResourceCompiler.java
index ac991b2504..ebd9b436f0 100644
--- 
a/buildSrc/src/main/java/org/apache/sis/buildtools/resources/IndexedResourceCompiler.java
+++ 
b/buildSrc/src/main/java/org/apache/sis/buildtools/resources/IndexedResourceCompiler.java
@@ -30,7 +30,6 @@ import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.FileNotFoundException;
-import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.BufferedReader;
 import java.io.ByteArrayOutputStream;
@@ -122,13 +121,13 @@ public class IndexedResourceCompiler {
      * Integer IDs allocated to resource keys.
      * This map will be shared for all languages of a given resource bundle.
      */
-    private final Map<Integer,String> allocatedIDs = new HashMap<>();
+    private final Map<Integer, String> allocatedIDs = new HashMap<>();
 
     /**
      * Resource keys and their localized values.
      * This map will be cleared for each language in a resource bundle.
      */
-    private final Map<Object,Object> resources = new HashMap<>();
+    private final Map<Object, Object> resources = new HashMap<>();
 
     /**
      * Buffer to use for writing UTF data in an array of bytes.
@@ -191,12 +190,12 @@ public class IndexedResourceCompiler {
      * @param  resourcesToProcess  the files to filter. This map will be 
modified in-place.
      * @param  checkSourceExists   whether to check that the Java source file 
exists.
      */
-    static void filterLanguages(final SortedMap<File,Boolean> 
resourcesToProcess, final boolean checkSourceExists) {
-        final Iterator<Map.Entry<File,Boolean>> it = 
resourcesToProcess.entrySet().iterator();
-        Map.Entry<File,Boolean> baseEntry = null;
+    static void filterLanguages(final SortedMap<File, Boolean> 
resourcesToProcess, final boolean checkSourceExists) {
+        final Iterator<Map.Entry<File, Boolean>> it = 
resourcesToProcess.entrySet().iterator();
+        Map.Entry<File, Boolean> baseEntry = null;
         String baseName = null;
         while (it.hasNext()) {
-            final Map.Entry<File,Boolean> entry = it.next();
+            final Map.Entry<File, Boolean> entry = it.next();
             final File file = entry.getKey();
             if (baseName != null && file.getName().startsWith(baseName)) {
                 if (baseEntry != null) {
@@ -248,7 +247,7 @@ public class IndexedResourceCompiler {
         if (files == null) {
             throw new FileNotFoundException(directory + " not found or is not 
a directory.");
         }
-        final var resourcesToProcess = new TreeMap<File,Boolean>();
+        final var resourcesToProcess = new TreeMap<File, Boolean>();
         for (final File file : files) {
             final String name = file.getName();
             if (!name.isEmpty() && name.charAt(0) != '.') {
@@ -264,7 +263,7 @@ public class IndexedResourceCompiler {
             }
         }
         filterLanguages(resourcesToProcess, true);
-        for (final Map.Entry<File,Boolean> entry : 
resourcesToProcess.entrySet()) {
+        for (final Map.Entry<File, Boolean> entry : 
resourcesToProcess.entrySet()) {
             final File source = entry.getKey();
             if (entry.getValue()) {
                 onJavaSource(new File(source.getParentFile(), 
getBaseName(source) + JAVA_EXT));
@@ -299,7 +298,7 @@ public class IndexedResourceCompiler {
      * @throws IOException if an error occurred while reading the source file.
      */
     private void loadKeyValues() throws IOException {
-        try (BufferedReader in = new BufferedReader(new InputStreamReader(new 
FileInputStream(bundleClass), JAVA_ENCODING))) {
+        try (var in = new BufferedReader(new InputStreamReader(new 
FileInputStream(bundleClass), JAVA_ENCODING))) {
             String line;
             while ((line = in.readLine()) != null) {
                 if ((line = line.trim()).startsWith(KEY_MODIFIERS)) {
@@ -399,7 +398,7 @@ public class IndexedResourceCompiler {
      */
     private static Properties loadRawProperties(final File file) throws 
IOException {
         final Properties properties;
-        try (InputStream input = new FileInputStream(file)) {
+        try (var input = new FileInputStream(file)) {
             properties = new Properties();
             properties.load(input);
         }
@@ -424,7 +423,7 @@ public class IndexedResourceCompiler {
     private void loadProperties(final File file) throws IOException {
         resources.clear();
         final Properties properties = loadRawProperties(file);
-        for (final Map.Entry<Object,Object> entry : properties.entrySet()) {
+        for (final Map.Entry<Object, Object> entry : properties.entrySet()) {
             final String key   = (String) entry.getKey();
             final String value = (String) entry.getValue();
             /*
@@ -496,7 +495,7 @@ public class IndexedResourceCompiler {
     private static String toMessageFormatString(final String text) {
         int level =  0;
         int last  = -1;
-        final StringBuilder buffer = new StringBuilder(text);
+        final var buffer = new StringBuilder(text);
 search: for (int i=0; i<buffer.length(); i++) {                 // Length of 
`buffer` will vary.
             switch (buffer.charAt(i)) {
                 /*
@@ -559,7 +558,7 @@ search: for (int i=0; i<buffer.length(); i++) {             
    // Length of `bu
     private byte[] writeUTF() throws IOException {
         bufferUTF.reset();
         final int count = allocatedIDs.isEmpty() ? 0 : 
Collections.max(allocatedIDs.keySet());
-        try (DataOutputStream out = new DataOutputStream(bufferUTF)) {
+        try (var out = new DataOutputStream(bufferUTF)) {
             out.writeInt(count);
             for (int i=1; i<=count; i++) {
                 final String value = (String) 
resources.get(allocatedIDs.get(i));
@@ -616,6 +615,19 @@ search: for (int i=0; i<buffer.length(); i++) {            
     // Length of `bu
                     buffer.append(line).append(lineSeparator);
                 } while (!pattern.matcher(line).matches());
             }
+            buffer.append(lineSeparator)
+                  .append("        /**").append(lineSeparator)
+                  .append("         * Returns the value of a field declared in 
this {@code Keys} class.").append(lineSeparator)
+                  .append("         * This method is needed for encapsulation 
reason, because classes in").append(lineSeparator)
+                  .append("         * other modules cannot access this class 
even by reflection.").append(lineSeparator)
+                  .append("         */").append(lineSeparator)
+                  .append("        @Override").append(lineSeparator)
+                  .append("        protected Object getStaticValue(final Field 
field) throws IllegalAccessException {").append(lineSeparator)
+                  .append("            if (field.getDeclaringClass() == 
Keys.class) {").append(lineSeparator)
+                  .append("                return 
field.get(null);").append(lineSeparator)
+                  .append("            }").append(lineSeparator)
+                  .append("            throw new 
IllegalAccessException();").append(lineSeparator)
+                  .append("        }").append(lineSeparator);
             /*
              * Starting from this point, the content that we are going to 
write in the buffer
              * may be different than the file content.  Remember the buffer 
position in order
@@ -635,7 +647,7 @@ search: for (int i=0; i<buffer.length(); i++) {             
    // Length of `bu
                 if (message != null) {
                     message = message.replace('\t', ' ');
                     
buffer.append(KEY_MARGIN).append("/**").append(lineSeparator);
-                    while (((message=message.trim()).length()) != 0) {
+                    while (((message = message.trim()).length()) != 0) {
                         buffer.append(KEY_MARGIN).append(" * ");
                         int stop = message.indexOf('\n');
                         if (stop < 0) {
@@ -754,7 +766,7 @@ search: for (int i=0; i<buffer.length(); i++) {             
    // Length of `bu
     private void warning(final File file,      final String key,
                          final String message, final Exception exception)
     {
-        final StringBuilder buffer = new StringBuilder("ERROR ");
+        final var buffer = new StringBuilder("ERROR ");
         if (file != null) {
             String filename = file.getPath();
             if (filename.endsWith(PROPERTIES_EXT)) {
diff --git 
a/endorsed/src/org.apache.sis.cloud.aws/main/org/apache/sis/cloud/aws/s3/Resources.java
 
b/endorsed/src/org.apache.sis.cloud.aws/main/org/apache/sis/cloud/aws/s3/Resources.java
index d7afb279a9..cf5e2608c1 100644
--- 
a/endorsed/src/org.apache.sis.cloud.aws/main/org/apache/sis/cloud/aws/s3/Resources.java
+++ 
b/endorsed/src/org.apache.sis.cloud.aws/main/org/apache/sis/cloud/aws/s3/Resources.java
@@ -17,6 +17,7 @@
 package org.apache.sis.cloud.aws.s3;
 
 import java.io.InputStream;
+import java.lang.reflect.Field;
 import java.util.Locale;
 import java.util.MissingResourceException;
 import org.apache.sis.util.resources.KeyConstants;
@@ -51,6 +52,19 @@ class Resources extends IndexedResourceBundle {
         private Keys() {
         }
 
+        /**
+         * Returns the value of a field declared in this {@code Keys} class.
+         * This method is needed for encapsulation reason, because classes in
+         * other modules cannot access this class even by reflection.
+         */
+        @Override
+        protected Object getStaticValue(final Field field) throws 
IllegalAccessException {
+            if (field.getDeclaringClass() == Keys.class) {
+                return field.get(null);
+            }
+            throw new IllegalAccessException();
+        }
+
         /**
          * Cannot change a relative path to an absolute path.
          */
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/internal/Resources.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/internal/Resources.java
index 8f099fc9d1..ab67b06ea4 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/internal/Resources.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/internal/Resources.java
@@ -17,6 +17,7 @@
 package org.apache.sis.feature.internal;
 
 import java.io.InputStream;
+import java.lang.reflect.Field;
 import java.util.Map;
 import java.util.Locale;
 import java.util.MissingResourceException;
@@ -54,6 +55,19 @@ public class Resources extends IndexedResourceBundle {
         private Keys() {
         }
 
+        /**
+         * Returns the value of a field declared in this {@code Keys} class.
+         * This method is needed for encapsulation reason, because classes in
+         * other modules cannot access this class even by reflection.
+         */
+        @Override
+        protected Object getStaticValue(final Field field) throws 
IllegalAccessException {
+            if (field.getDeclaringClass() == Keys.class) {
+                return field.get(null);
+            }
+            throw new IllegalAccessException();
+        }
+
         /**
          * Feature type ‘{0}’ is abstract.
          */
diff --git 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/internal/Resources.java
 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/internal/Resources.java
index 34598f5563..d2322ec966 100644
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/internal/Resources.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/internal/Resources.java
@@ -17,6 +17,7 @@
 package org.apache.sis.metadata.internal;
 
 import java.io.InputStream;
+import java.lang.reflect.Field;
 import java.util.Locale;
 import java.util.MissingResourceException;
 import org.opengis.util.InternationalString;
@@ -53,6 +54,19 @@ public class Resources extends IndexedResourceBundle {
         private Keys() {
         }
 
+        /**
+         * Returns the value of a field declared in this {@code Keys} class.
+         * This method is needed for encapsulation reason, because classes in
+         * other modules cannot access this class even by reflection.
+         */
+        @Override
+        protected Object getStaticValue(final Field field) throws 
IllegalAccessException {
+            if (field.getDeclaringClass() == Keys.class) {
+                return field.get(null);
+            }
+            throw new IllegalAccessException();
+        }
+
         /**
          * Connection to “{0}” database is already initialized.
          */
diff --git 
a/endorsed/src/org.apache.sis.portrayal/main/org/apache/sis/map/internal/Resources.java
 
b/endorsed/src/org.apache.sis.portrayal/main/org/apache/sis/map/internal/Resources.java
index e80ebd1814..a667a65cfd 100644
--- 
a/endorsed/src/org.apache.sis.portrayal/main/org/apache/sis/map/internal/Resources.java
+++ 
b/endorsed/src/org.apache.sis.portrayal/main/org/apache/sis/map/internal/Resources.java
@@ -17,6 +17,7 @@
 package org.apache.sis.map.internal;
 
 import java.io.InputStream;
+import java.lang.reflect.Field;
 import java.util.Locale;
 import java.util.MissingResourceException;
 import org.apache.sis.util.resources.KeyConstants;
@@ -49,6 +50,19 @@ public class Resources extends IndexedResourceBundle {
         private Keys() {
         }
 
+        /**
+         * Returns the value of a field declared in this {@code Keys} class.
+         * This method is needed for encapsulation reason, because classes in
+         * other modules cannot access this class even by reflection.
+         */
+        @Override
+        protected Object getStaticValue(final Field field) throws 
IllegalAccessException {
+            if (field.getDeclaringClass() == Keys.class) {
+                return field.get(null);
+            }
+            throw new IllegalAccessException();
+        }
+
         /**
          * The “{0}” resource specifies an invalid conversion from pixel 
coordinates to the “{1}”
          * coordinate reference system.
diff --git 
a/endorsed/src/org.apache.sis.referencing.gazetteer/main/org/apache/sis/referencing/gazetteer/internal/Resources.java
 
b/endorsed/src/org.apache.sis.referencing.gazetteer/main/org/apache/sis/referencing/gazetteer/internal/Resources.java
index e88d344cb3..d83e59997b 100644
--- 
a/endorsed/src/org.apache.sis.referencing.gazetteer/main/org/apache/sis/referencing/gazetteer/internal/Resources.java
+++ 
b/endorsed/src/org.apache.sis.referencing.gazetteer/main/org/apache/sis/referencing/gazetteer/internal/Resources.java
@@ -17,6 +17,7 @@
 package org.apache.sis.referencing.gazetteer.internal;
 
 import java.io.InputStream;
+import java.lang.reflect.Field;
 import java.util.Locale;
 import java.util.Map;
 import java.util.MissingResourceException;
@@ -54,6 +55,19 @@ public class Resources extends IndexedResourceBundle {
         private Keys() {
         }
 
+        /**
+         * Returns the value of a field declared in this {@code Keys} class.
+         * This method is needed for encapsulation reason, because classes in
+         * other modules cannot access this class even by reflection.
+         */
+        @Override
+        protected Object getStaticValue(final Field field) throws 
IllegalAccessException {
+            if (field.getDeclaringClass() == Keys.class) {
+                return field.get(null);
+            }
+            throw new IllegalAccessException();
+        }
+
         /**
          * Location type parent already has a child named “{0}”.
          */
diff --git 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/Resources.java
 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/Resources.java
index 641280441a..75a81983ce 100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/Resources.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/internal/Resources.java
@@ -17,6 +17,7 @@
 package org.apache.sis.referencing.internal;
 
 import java.io.InputStream;
+import java.lang.reflect.Field;
 import java.util.Locale;
 import java.util.Map;
 import java.util.MissingResourceException;
@@ -54,6 +55,19 @@ public class Resources extends IndexedResourceBundle {
         private Keys() {
         }
 
+        /**
+         * Returns the value of a field declared in this {@code Keys} class.
+         * This method is needed for encapsulation reason, because classes in
+         * other modules cannot access this class even by reflection.
+         */
+        @Override
+        protected Object getStaticValue(final Field field) throws 
IllegalAccessException {
+            if (field.getDeclaringClass() == Keys.class) {
+                return field.get(null);
+            }
+            throw new IllegalAccessException();
+        }
+
         /**
          * Accuracy declared in a geodetic dataset.
          */
diff --git 
a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/base/Resources.java
 
b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/base/Resources.java
index e9bde86ae6..fdd6002468 100644
--- 
a/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/base/Resources.java
+++ 
b/endorsed/src/org.apache.sis.storage.geotiff/main/org/apache/sis/storage/geotiff/base/Resources.java
@@ -17,6 +17,7 @@
 package org.apache.sis.storage.geotiff.base;
 
 import java.io.InputStream;
+import java.lang.reflect.Field;
 import java.util.Locale;
 import java.util.MissingResourceException;
 import org.opengis.util.InternationalString;
@@ -53,6 +54,19 @@ public class Resources extends IndexedResourceBundle {
         private Keys() {
         }
 
+        /**
+         * Returns the value of a field declared in this {@code Keys} class.
+         * This method is needed for encapsulation reason, because classes in
+         * other modules cannot access this class even by reflection.
+         */
+        @Override
+        protected Object getStaticValue(final Field field) throws 
IllegalAccessException {
+            if (field.getDeclaringClass() == Keys.class) {
+                return field.get(null);
+            }
+            throw new IllegalAccessException();
+        }
+
         /**
          * Cannot compute the grid geometry of “{0}” GeoTIFF file.
          */
diff --git 
a/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/internal/Resources.java
 
b/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/internal/Resources.java
index 5c37f894fb..26b5fa05ae 100644
--- 
a/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/internal/Resources.java
+++ 
b/endorsed/src/org.apache.sis.storage.netcdf/main/org/apache/sis/storage/netcdf/internal/Resources.java
@@ -17,6 +17,7 @@
 package org.apache.sis.storage.netcdf.internal;
 
 import java.io.InputStream;
+import java.lang.reflect.Field;
 import java.util.Locale;
 import java.util.MissingResourceException;
 import org.opengis.util.InternationalString;
@@ -53,6 +54,19 @@ public class Resources extends IndexedResourceBundle {
         private Keys() {
         }
 
+        /**
+         * Returns the value of a field declared in this {@code Keys} class.
+         * This method is needed for encapsulation reason, because classes in
+         * other modules cannot access this class even by reflection.
+         */
+        @Override
+        protected Object getStaticValue(final Field field) throws 
IllegalAccessException {
+            if (field.getDeclaringClass() == Keys.class) {
+                return field.get(null);
+            }
+            throw new IllegalAccessException();
+        }
+
         /**
          * NetCDF file “{0}” provides an ambiguous axis direction for variable 
“{1}”. It could be
          * {2} or {3}.
diff --git 
a/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/Resources.java
 
b/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/Resources.java
index 1edcfa8eac..225bddc0ed 100644
--- 
a/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/Resources.java
+++ 
b/endorsed/src/org.apache.sis.storage.sql/main/org/apache/sis/storage/sql/feature/Resources.java
@@ -17,6 +17,7 @@
 package org.apache.sis.storage.sql.feature;
 
 import java.io.InputStream;
+import java.lang.reflect.Field;
 import java.util.Locale;
 import java.util.MissingResourceException;
 import org.opengis.util.InternationalString;
@@ -53,6 +54,19 @@ public class Resources extends IndexedResourceBundle {
         private Keys() {
         }
 
+        /**
+         * Returns the value of a field declared in this {@code Keys} class.
+         * This method is needed for encapsulation reason, because classes in
+         * other modules cannot access this class even by reflection.
+         */
+        @Override
+        protected Object getStaticValue(final Field field) throws 
IllegalAccessException {
+            if (field.getDeclaringClass() == Keys.class) {
+                return field.get(null);
+            }
+            throw new IllegalAccessException();
+        }
+
         /**
          * Assume database byte/tinyint unsigned, due to a lack of metadata.
          */
diff --git 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/internal/Resources.java
 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/internal/Resources.java
index 4f451dffab..a3b4fc5f6b 100644
--- 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/internal/Resources.java
+++ 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/internal/Resources.java
@@ -17,6 +17,7 @@
 package org.apache.sis.storage.internal;
 
 import java.io.InputStream;
+import java.lang.reflect.Field;
 import java.util.Locale;
 import java.util.MissingResourceException;
 import org.opengis.util.InternationalString;
@@ -53,6 +54,19 @@ public class Resources extends IndexedResourceBundle {
         private Keys() {
         }
 
+        /**
+         * Returns the value of a field declared in this {@code Keys} class.
+         * This method is needed for encapsulation reason, because classes in
+         * other modules cannot access this class even by reflection.
+         */
+        @Override
+        protected Object getStaticValue(final Field field) throws 
IllegalAccessException {
+            if (field.getDeclaringClass() == Keys.class) {
+                return field.get(null);
+            }
+            throw new IllegalAccessException();
+        }
+
         /**
          * Name “{3}” is ambiguous because it can be understood as either 
“{1}” or “{2}” in the context
          * of “{0}” data.
diff --git 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Errors.java
 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Errors.java
index 860b837840..ae65d1bfd1 100644
--- 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Errors.java
+++ 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Errors.java
@@ -17,6 +17,7 @@
 package org.apache.sis.util.resources;
 
 import java.io.InputStream;
+import java.lang.reflect.Field;
 import java.util.Map;
 import java.util.Locale;
 import java.util.MissingResourceException;
@@ -57,6 +58,19 @@ public class Errors extends IndexedResourceBundle {
         private Keys() {
         }
 
+        /**
+         * Returns the value of a field declared in this {@code Keys} class.
+         * This method is needed for encapsulation reason, because classes in
+         * other modules cannot access this class even by reflection.
+         */
+        @Override
+        protected Object getStaticValue(final Field field) throws 
IllegalAccessException {
+            if (field.getDeclaringClass() == Keys.class) {
+                return field.get(null);
+            }
+            throw new IllegalAccessException();
+        }
+
         /**
          * ‘{0}’ is already initialized.
          */
diff --git 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/KeyConstants.java
 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/KeyConstants.java
index 0c66565b3a..2be050872f 100644
--- 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/KeyConstants.java
+++ 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/KeyConstants.java
@@ -20,7 +20,6 @@ import java.util.Arrays;
 import java.lang.reflect.Field;
 import java.lang.reflect.Modifier;
 import org.apache.sis.util.ArraysExt;
-import org.apache.sis.util.CharSequences;
 
 
 /**
@@ -32,12 +31,7 @@ import org.apache.sis.util.CharSequences;
  *
  * @see IndexedResourceBundle#getKeyConstants()
  */
-public class KeyConstants {
-    /**
-     * The class that defines key constants.
-     */
-    private final Class<?> keysClass;
-
+public abstract class KeyConstants {
     /**
      * The key names in the exact same order as {@link 
IndexedResourceBundle#values}.
      * This is usually not needed, but may be created from the {@code Keys} 
inner class in some occasions.
@@ -51,15 +45,26 @@ public class KeyConstants {
      * For sub-classes constructors only.
      */
     protected KeyConstants() {
-        keysClass = getClass();
     }
 
     /**
-     * Creates a new instance for key constants defined in an independent 
class.
+     * Returns the value of the given static field.
+     * Subclasses should implement this method as below:
+     *
+     * {@snippet lang="java" :
+     *     return field.get(null);
+     *     }
+     *
+     * This implementation must be provided by subclasses for Java security 
reason.
+     * This is because this {@code KeyConstants} class is not allowed to 
perform the
+     * above call if the module that provides the {@code KeyConstants} 
subclass does
+     * not export the package that contain the subclass.
+     *
+     * @param  field  the field for which to get the value.
+     * @return value of the given field.
+     * @throws IllegalAccessException if access to the field is denied.
      */
-    KeyConstants(final Class<?> keysClass) {
-        this.keysClass = keysClass;
-    }
+    protected abstract Object getStaticValue(Field field) throws 
IllegalAccessException;
 
     /**
      * Returns the internal array of key names. <strong>Do not modify the 
returned array.</strong>
@@ -73,11 +78,11 @@ public class KeyConstants {
             String[] names;
             int length = 0;
             try {
-                final Field[] fields = keysClass.getFields();
+                final Field[] fields = getClass().getFields();
                 names = new String[fields.length];
                 for (final Field field : fields) {
                     if (Modifier.isStatic(field.getModifiers()) && 
field.getType() == Short.TYPE) {
-                        final int index = Short.toUnsignedInt((Short) 
field.get(null)) - IndexedResourceBundle.FIRST;
+                        final int index = Short.toUnsignedInt((Short) 
getStaticValue(field)) - IndexedResourceBundle.FIRST;
                         if (index >= length) {
                             length = index + 1;
                             if (length > names.length) {
@@ -89,7 +94,7 @@ public class KeyConstants {
                     }
                 }
             } catch (IllegalAccessException e) {
-                names = CharSequences.EMPTY_ARRAY;
+                throw new IllegalCallerException(e);
             }
             keys = ArraysExt.resize(names, length);
         }
@@ -118,6 +123,6 @@ public class KeyConstants {
      * Returns the numerical value for the key of the given name.
      */
     final short getKeyValue(final String name) throws NoSuchFieldException, 
IllegalAccessException {
-        return (Short) keysClass.getField(name).get(null);
+        return (Short) getStaticValue(getClass().getField(name));
     }
 }
diff --git 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Messages.java
 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Messages.java
index 2a661986a5..fec0b6a6d7 100644
--- 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Messages.java
+++ 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Messages.java
@@ -17,6 +17,7 @@
 package org.apache.sis.util.resources;
 
 import java.io.InputStream;
+import java.lang.reflect.Field;
 import java.util.Locale;
 import java.util.MissingResourceException;
 import org.opengis.util.InternationalString;
@@ -48,6 +49,19 @@ public class Messages extends IndexedResourceBundle {
         private Keys() {
         }
 
+        /**
+         * Returns the value of a field declared in this {@code Keys} class.
+         * This method is needed for encapsulation reason, because classes in
+         * other modules cannot access this class even by reflection.
+         */
+        @Override
+        protected Object getStaticValue(final Field field) throws 
IllegalAccessException {
+            if (field.getDeclaringClass() == Keys.class) {
+                return field.get(null);
+            }
+            throw new IllegalAccessException();
+        }
+
         /**
          * {0} “{1}” is already registered. The second instance will be 
ignored.
          */
diff --git 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Vocabulary.java
 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Vocabulary.java
index 5400228dde..db82cf3935 100644
--- 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Vocabulary.java
+++ 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/resources/Vocabulary.java
@@ -17,6 +17,7 @@
 package org.apache.sis.util.resources;
 
 import java.io.InputStream;
+import java.lang.reflect.Field;
 import java.util.Map;
 import java.util.Locale;
 import java.util.MissingResourceException;
@@ -49,6 +50,19 @@ public class Vocabulary extends IndexedResourceBundle {
         private Keys() {
         }
 
+        /**
+         * Returns the value of a field declared in this {@code Keys} class.
+         * This method is needed for encapsulation reason, because classes in
+         * other modules cannot access this class even by reflection.
+         */
+        @Override
+        protected Object getStaticValue(final Field field) throws 
IllegalAccessException {
+            if (field.getDeclaringClass() == Keys.class) {
+                return field.get(null);
+            }
+            throw new IllegalAccessException();
+        }
+
         /**
          * Abstract
          */
diff --git 
a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/internal/Resources.java
 
b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/internal/Resources.java
index c789f97385..49f9abb6d2 100644
--- 
a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/internal/Resources.java
+++ 
b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/internal/Resources.java
@@ -17,6 +17,7 @@
 package org.apache.sis.gui.internal;
 
 import java.io.InputStream;
+import java.lang.reflect.Field;
 import java.util.Locale;
 import java.util.MissingResourceException;
 import javafx.event.ActionEvent;
@@ -54,6 +55,19 @@ public class Resources extends IndexedResourceBundle {
         private Keys() {
         }
 
+        /**
+         * Returns the value of a field declared in this {@code Keys} class.
+         * This method is needed for encapsulation reason, because classes in
+         * other modules cannot access this class even by reflection.
+         */
+        @Override
+        protected Object getStaticValue(final Field field) throws 
IllegalAccessException {
+            if (field.getDeclaringClass() == Keys.class) {
+                return field.get(null);
+            }
+            throw new IllegalAccessException();
+        }
+
         /**
          * About…
          */
diff --git 
a/optional/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/panama/Resources.java
 
b/optional/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/panama/Resources.java
index d4b4c8f13a..4c728ce99d 100644
--- 
a/optional/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/panama/Resources.java
+++ 
b/optional/src/org.apache.sis.storage.gdal/main/org/apache/sis/storage/panama/Resources.java
@@ -17,6 +17,7 @@
 package org.apache.sis.storage.panama;
 
 import java.io.InputStream;
+import java.lang.reflect.Field;
 import java.util.Locale;
 import java.util.MissingResourceException;
 import org.opengis.util.InternationalString;
@@ -53,6 +54,19 @@ public class Resources extends IndexedResourceBundle {
         private Keys() {
         }
 
+        /**
+         * Returns the value of a field declared in this {@code Keys} class.
+         * This method is needed for encapsulation reason, because classes in
+         * other modules cannot access this class even by reflection.
+         */
+        @Override
+        protected Object getStaticValue(final Field field) throws 
IllegalAccessException {
+            if (field.getDeclaringClass() == Keys.class) {
+                return field.get(null);
+            }
+            throw new IllegalAccessException();
+        }
+
         /**
          * Allowed {0} drivers for opening the file.
          */


Reply via email to