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

commit 0cd54bd9de9f1d53f510324a753c1d16dfc498b4
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Thu Jul 6 18:09:20 2023 +0200

    `ResourceInternationalString` needs to let subclasses invoke 
`ResourceBundle.getBundle(…)` themselves.
    This is necessary in a JPMS context, because this class is not allowed to 
load resources of another module.
---
 .../main/java/org/apache/sis/util/iso/Types.java   | 52 ++++----------
 .../java/org/apache/sis/util/iso/TypesTest.java    |  9 ---
 .../internal/referencing/provider/Description.java | 54 ++++++++++++++
 .../referencing/provider/SatelliteTracking.java    |  4 +-
 .../sis/util/ResourceInternationalString.java      | 82 +++++++---------------
 .../sis/internal/storage/gpx/Description.java      | 62 ++++++++++++++++
 .../org/apache/sis/internal/storage/gpx/Types.java |  5 +-
 7 files changed, 159 insertions(+), 109 deletions(-)

diff --git a/core/sis-metadata/src/main/java/org/apache/sis/util/iso/Types.java 
b/core/sis-metadata/src/main/java/org/apache/sis/util/iso/Types.java
index 11af4a3269..9e2a783799 100644
--- a/core/sis-metadata/src/main/java/org/apache/sis/util/iso/Types.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/util/iso/Types.java
@@ -294,11 +294,8 @@ public final class Types extends Static {
      */
     @OptionalCandidate
     public static InternationalString getDescription(final 
ControlledVocabulary code) {
-        if (code != null) {
-            final String resources = toResourceName(code.getClass().getName());
-            if (resources != null) {
-                return new Description(resources, 
Description.resourceKey(code));
-            }
+        if (code != null && hasResources(code.getClass())) {
+            return new Description(Description.resourceKey(code));
         }
         return null;
     }
@@ -315,11 +312,8 @@ public final class Types extends Static {
     @OptionalCandidate
     public static InternationalString getDescription(final Class<?> type) {
         final String name = getStandardName(type);
-        if (name != null) {
-            final String resources = toResourceName(type.getName());
-            if (resources != null) {
-                return new Description(resources, name);
-            }
+        if (name != null && hasResources(type)) {
+            return new Description(name);
         }
         return null;
     }
@@ -337,11 +331,8 @@ public final class Types extends Static {
     public static InternationalString getDescription(final Class<?> type, 
final String property) {
         if (property != null) {
             final String name = getStandardName(type);
-            if (name != null) {
-                final String resources = toResourceName(type.getName());
-                if (resources != null) {
-                    return new Description(resources, name + SEPARATOR + 
property);
-                }
+            if (name != null && hasResources(type)) {
+                return new Description(name + SEPARATOR + property);
             }
         }
         return null;
@@ -362,19 +353,15 @@ public final class Types extends Static {
 
         /**
          * Creates a new international string from the specified resource 
bundle and key.
-         * The {@code resources} argument is only informative since this class 
overrides
-         * the {@link #getBundle(Locale)} method.
          *
-         * @param resources  the name of the resource bundle, as a fully 
qualified class name.
-         * @param key        the key for the resource to fetch.
+         * @param key  the key for the resource to fetch.
          */
-        Description(final String resources, final String key) {
-            super(resources, key);
+        Description(final String key) {
+            super(key);
         }
 
         /**
          * Loads the GeoAPI resources. A cache is managed by {@link 
ResourceBundle}.
-         * Note that the {@link #resources} field value is ignored.
          */
         @Override
         protected ResourceBundle getBundle(final Locale locale) {
@@ -443,13 +430,12 @@ public final class Types extends Static {
          * @param  code  the code list for which to create a title.
          */
         CodeTitle(final ControlledVocabulary code) {
-            super("org.opengis.metadata.CodeLists", resourceKey(code));
+            super(resourceKey(code));
             this.code = code;
         }
 
         /**
          * Loads the GeoAPI resources. A cache is managed by {@link 
ResourceBundle}.
-         * Note that the {@link #resources} field value is ignored.
          */
         @Override
         protected ResourceBundle getBundle(final Locale locale) {
@@ -466,21 +452,13 @@ public final class Types extends Static {
     }
 
     /**
-     * Returns the resource name for the given GeoAPI type, or {@code null} if 
none.
-     * The non-null resource name is only informative in this implementation.
-     * We need to allow {@code null} return value for telling that no resource
-     * is expected to exist for the given class.
+     * Returns whether the specified class is expected to have GeoAPI 
resources.
      *
-     * @param  classname  the fully qualified name of the GeoAPI type.
-     * @return the resource bundle to load, or {@code null} if none.
+     * @param  type  the type to test.
+     * @return whether the given class is expected to have resources.
      */
-    static String toResourceName(final String classname) {
-        String resources = "org.opengis.metadata.Descriptions";
-        if (classname.regionMatches(0, resources, 0, 21)) {             // 21 
is the location after the last dot.
-            return resources;
-        }
-        // Add more checks here (maybe in a loop) if there is more resource 
candidates.
-        return null;
+    private static boolean hasResources(final Class<?> type) {
+        return type.getName().startsWith("org.opengis.metadata.");
     }
 
     /**
diff --git 
a/core/sis-metadata/src/test/java/org/apache/sis/util/iso/TypesTest.java 
b/core/sis-metadata/src/test/java/org/apache/sis/util/iso/TypesTest.java
index 1773da29a2..ad72a9692b 100644
--- a/core/sis-metadata/src/test/java/org/apache/sis/util/iso/TypesTest.java
+++ b/core/sis-metadata/src/test/java/org/apache/sis/util/iso/TypesTest.java
@@ -159,15 +159,6 @@ public final class TypesTest extends TestCase {
         assertSame(PixelInCell.CELL_CENTER, 
Types.forCodeName(PixelInCell.class, "cellCentre",  false));
     }
 
-    /**
-     * Tests the {@link Types#toResourceName(String)} method.
-     */
-    @Test
-    public void testGetResources() {
-        assertEquals("org.opengis.metadata.Descriptions", 
Types.toResourceName("org.opengis.metadata.Identifier"));
-        assertNull(Types.toResourceName("org.opengis.metadata2.Identifier"));
-    }
-
     /**
      * Tests the {@link Types#getDescription(Class)} method.
      */
diff --git 
a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Description.java
 
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Description.java
new file mode 100644
index 0000000000..96c4d936dd
--- /dev/null
+++ 
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/Description.java
@@ -0,0 +1,54 @@
+/*
+ * 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.internal.referencing.provider;
+
+import java.util.Locale;
+import java.util.ResourceBundle;
+import org.apache.sis.util.ResourceInternationalString;
+
+
+/**
+ * A localized description.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.4
+ * @since   1.4
+ */
+final class Description extends ResourceInternationalString {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = 400673221337938815L;
+
+    /**
+     * Creates a new international string from the specified key.
+     *
+     * @param key  the key for the resource to fetch.
+     */
+    Description(final String key) {
+        super(key);
+    }
+
+    /**
+     * Returns the resource bundle for the given locale.
+     */
+    @Override
+    protected ResourceBundle getBundle(final Locale locale) {
+        return 
ResourceBundle.getBundle("org.apache.sis.internal.referencing.provider.Descriptions",
+                                        locale, 
Description.class.getClassLoader());
+    }
+}
diff --git 
a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/SatelliteTracking.java
 
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/SatelliteTracking.java
index aa3f15dd47..14a72ba497 100644
--- 
a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/SatelliteTracking.java
+++ 
b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/provider/SatelliteTracking.java
@@ -25,7 +25,6 @@ import org.apache.sis.metadata.iso.citation.Citations;
 import org.apache.sis.parameter.ParameterBuilder;
 import org.apache.sis.parameter.Parameters;
 import org.apache.sis.referencing.operation.projection.NormalizedProjection;
-import org.apache.sis.util.ResourceInternationalString;
 import org.apache.sis.internal.util.Constants;
 import org.apache.sis.measure.Units;
 
@@ -193,8 +192,7 @@ public class SatelliteTracking extends MapProjection {
      * @return the builder, for method calls chaining.
      */
     private static ParameterBuilder setNameAndDescription(final 
ParameterBuilder builder, final String name) {
-        return builder.addName(name).setDescription(new 
ResourceInternationalString(
-                "org.apache.sis.internal.referencing.provider.Descriptions", 
name));
+        return builder.addName(name).setDescription(new Description(name));
     }
 
     /**
diff --git 
a/core/sis-utility/src/main/java/org/apache/sis/util/ResourceInternationalString.java
 
b/core/sis-utility/src/main/java/org/apache/sis/util/ResourceInternationalString.java
index 5db3ca326f..6e813f16a3 100644
--- 
a/core/sis-utility/src/main/java/org/apache/sis/util/ResourceInternationalString.java
+++ 
b/core/sis-utility/src/main/java/org/apache/sis/util/ResourceInternationalString.java
@@ -25,9 +25,10 @@ import java.util.MissingResourceException;
 
 /**
  * An international string backed by a {@link ResourceBundle}.
- * Resource bundles can be Java classes or {@linkplain Properties properties} 
files, one for each
- * language. The fully qualified class name of the base resource bundle 
(without locale suffix)
- * is specified at {@linkplain #ResourceInternationalString(String, String) 
construction time}.
+ * Resource bundles can be Java classes or {@linkplain Properties properties} 
files, one for each language.
+ * The resource bundle is specified by {@link #getBundle(Locale)}, which must 
be defined by subclasses.
+ * The subclass will typically invoke {@code 
ResourceBundle.getBundle(resources, locale)} where
+ * <var>resources</var> is the fully qualified class name of the base resource 
bundle (without locale suffix).
  * The appropriate resource bundle is loaded at runtime for the client's 
language by looking for
  * a class or a properties file with the right suffix, for example {@code 
"_en"} for English or
  * {@code "_fr"} for French.
@@ -35,7 +36,7 @@ import java.util.MissingResourceException;
  * Javadoc for more information.
  *
  * <h2>Example</h2>
- * if a file named "{@code MyResources.properties}" exists in {@code 
org.mypackage}
+ * If a file named "{@code MyResources.properties}" exists in {@code 
org.mypackage}
  * and contains the following line:
  *
  * <pre class="text">MyKey = some value</pre>
@@ -43,7 +44,11 @@ import java.util.MissingResourceException;
  * Then an international string for {@code "some value"} can be created using 
the following code:
  *
  * {@snippet lang="java" :
- *     InternationalString value = new 
ResourceInternationalString("org.mypackage.MyResources", "MyKey");
+ *     InternationalString value = new ResourceInternationalString("MyKey") {
+ *         @Override protected ResourceBundle getBundle(Locale locale) {
+ *             return ResourceBundle.getBundle("org.mypackage.MyResources", 
locale);
+ *         }
+ *     };
  *     }
  *
  * The {@code "some value"} string will be localized if the required 
properties files exist, for
@@ -51,22 +56,6 @@ import java.util.MissingResourceException;
  * for Italian, <i>etc</i>.
  * If needed, users can gain more control by overriding the {@link 
#getBundle(Locale)} method.
  *
- * <h2>Class loaders</h2>
- * Developers can specify explicitly the {@link ClassLoader} to use be 
overriding the
- * {@link #getBundle(Locale)} method. This is recommended if the running 
environment
- * loads modules in isolated class loaders, as OSGi does for instance.
- *
- * <div class="note"><b>API note:</b>
- * We do not provide {@code ClassLoader} argument in the constructor of this 
class because class loaders
- * can often be hard-coded (thus avoiding the cost of an extra field) and are 
usually not serializable.</div>
- *
- * <h2>Apache SIS resources</h2>
- * Apache SIS has its own resources mechanism, built on top of the standard 
{@code ResourceBundle}
- * with the addition of type safety and optional arguments to be formatted in 
the localized string.
- * Those resource bundles provide {@code formatInternational(int, …)} static 
methods for creating
- * international strings with the same functionality than this {@code 
ResourceInternationalString}.
- * See {@code org.apache.sis.util.resources} for more information.
- *
  * <h2>Immutability and thread safety</h2>
  * This class is immutable and thus inherently thread-safe if the bundles 
created by {@link #getBundle(Locale)}
  * is also immutable. Subclasses may or may not be immutable, at 
implementation choice. But implementers are
@@ -74,23 +63,17 @@ import java.util.MissingResourceException;
  *
  * @author  Martin Desruisseaux (IRD, Geomatys)
  * @author  Johann Sorel (Geomatys)
- * @version 1.1
+ * @version 1.4
  *
  * @see ResourceBundle#getBundle(String, Locale)
  *
  * @since 1.1
  */
-public class ResourceInternationalString extends AbstractInternationalString 
implements Serializable {
+public abstract class ResourceInternationalString extends 
AbstractInternationalString implements Serializable {
     /**
      * Serial number for inter-operability with different versions.
      */
-    private static final long serialVersionUID = -8636012022904092254L;
-
-    /**
-     * The name of the resource bundle, as a fully qualified class name.
-     * This value is given at construction time and cannot be {@code null}.
-     */
-    protected final String resources;
+    private static final long serialVersionUID = -4354957955509756039L;
 
     /**
      * The key for the resource to fetch.
@@ -99,30 +82,20 @@ public class ResourceInternationalString extends 
AbstractInternationalString imp
     protected final String key;
 
     /**
-     * Creates a new international string from the specified resource bundle 
and key.
-     * The class loader will be the one of the {@link #toString(Locale)} 
caller,
-     * unless the {@link #getBundle(Locale)} method is overridden.
+     * Creates a new international string from the specified key.
      *
-     * @param resources  the name of the resource bundle, as a fully qualified 
class name.
-     * @param key        the key for the resource to fetch.
+     * @param key  the key for the resource to fetch.
      */
-    public ResourceInternationalString(final String resources, final String 
key) {
-        ArgumentChecks.ensureNonNull("resources", resources);
-        ArgumentChecks.ensureNonNull("key",       key);
-        this.resources = resources;
-        this.key       = key;
+    protected ResourceInternationalString(final String key) {
+        ArgumentChecks.ensureNonNull("key", key);
+        this.key = key;
     }
 
     /**
-     * Returns the resource bundle for the given locale. The default 
implementation fetches the
-     * bundle from the name given at {@linkplain #ResourceInternationalString 
construction time}.
-     * Subclasses can override this method if they need to fetch the bundle in 
another way.
-     *
-     * <h4>Class loaders</h4>
-     * By default, this method loads the resources using the caller's class 
loader.
-     * Subclasses can override this method in order to specify a different 
class loader.
-     * For example, the code below works well if {@code MyResource} is a class 
defined
-     * in the same module than the one that contain the resources to load:
+     * Returns the resource bundle for the given locale.
+     * This implementation must be provided by subclasses because, in a 
modularized application,
+     * the call to the {@link ResourceBundle#getBundle(String, Locale)} method 
is caller-sensitive.
+     * This method is typically implemented as below:
      *
      * {@snippet lang="java" :
      *     @Override
@@ -133,14 +106,10 @@ public class ResourceInternationalString extends 
AbstractInternationalString imp
      *
      * @param  locale  the locale for which to get the resource bundle.
      * @return the resource bundle for the given locale.
-     * @throws MissingResourceException if no resource bundle can be found for 
the base name specified
-     *         at {@linkplain #ResourceInternationalString(String, String) 
construction time}.
      *
      * @see ResourceBundle#getBundle(String, Locale, ClassLoader)
      */
-    protected ResourceBundle getBundle(final Locale locale) throws 
MissingResourceException {
-        return ResourceBundle.getBundle(resources, locale);
-    }
+    protected abstract ResourceBundle getBundle(Locale locale);
 
     /**
      * Returns a string in the specified locale. If there is no string for the 
specified
@@ -174,8 +143,7 @@ public class ResourceInternationalString extends 
AbstractInternationalString imp
     @Override
     public boolean equals(final Object object) {
         if (object != null && object.getClass() == getClass()) {
-            final ResourceInternationalString that = 
(ResourceInternationalString) object;
-            return key.equals(that.key) && resources.equals(that.resources);
+            return key.equals(((ResourceInternationalString) object).key);
         }
         return false;
     }
@@ -187,6 +155,6 @@ public class ResourceInternationalString extends 
AbstractInternationalString imp
      */
     @Override
     public int hashCode() {
-        return key.hashCode() ^ resources.hashCode() ^ (int) serialVersionUID;
+        return key.hashCode() ^ (int) serialVersionUID;
     }
 }
diff --git 
a/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/gpx/Description.java
 
b/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/gpx/Description.java
new file mode 100644
index 0000000000..7f0440c352
--- /dev/null
+++ 
b/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/gpx/Description.java
@@ -0,0 +1,62 @@
+/*
+ * 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.internal.storage.gpx;
+
+import java.util.Locale;
+import java.util.ResourceBundle;
+import org.apache.sis.util.ResourceInternationalString;
+
+
+/**
+ * A localized description.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.4
+ * @since   1.4
+ */
+final class Description extends ResourceInternationalString {
+    /**
+     * For cross-version compatibility.
+     */
+    private static final long serialVersionUID = -7091720253401192418L;
+
+    /**
+     * The name of the resource bundle, as a fully qualified class name.
+     * This value is given at construction time and cannot be {@code null}.
+     * It most be a resource in the same module than this class.
+     */
+    private final String resources;
+
+    /**
+     * Creates a new international string from the specified key.
+     *
+     * @param resources  the name of the resource bundle, as a fully qualified 
class name.
+     * @param key        the key for the resource to fetch.
+     */
+    Description(final String resources, final String key) {
+        super(key);
+        this.resources = resources;
+    }
+
+    /**
+     * Returns the resource bundle for the given locale.
+     */
+    @Override
+    protected ResourceBundle getBundle(final Locale locale) {
+        return ResourceBundle.getBundle(resources, locale, 
Description.class.getClassLoader());
+    }
+}
diff --git 
a/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/gpx/Types.java
 
b/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/gpx/Types.java
index 7f87a6d2b4..eddfca7f5f 100644
--- 
a/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/gpx/Types.java
+++ 
b/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/gpx/Types.java
@@ -42,7 +42,6 @@ import org.apache.sis.feature.builder.AttributeRole;
 import org.apache.sis.internal.feature.AttributeConvention;
 import org.apache.sis.internal.feature.Geometries;
 import org.apache.sis.internal.storage.FeatureCatalogBuilder;
-import org.apache.sis.util.ResourceInternationalString;
 import org.apache.sis.util.iso.DefaultNameFactory;
 
 // Branch-dependent imports
@@ -306,8 +305,8 @@ final class Types {
             final GenericName name = p.getName();
             if (!AttributeConvention.contains(name)) {
                 final InternationalString[] resources = 
previous.computeIfAbsent(name.toString(), (key) -> new InternationalString[] {
-                    new 
ResourceInternationalString("org.apache.sis.internal.storage.gpx.Designations", 
key),
-                    new 
ResourceInternationalString("org.apache.sis.internal.storage.gpx.Definitions",  
key)
+                    new 
Description("org.apache.sis.internal.storage.gpx.Designations", key),
+                    new 
Description("org.apache.sis.internal.storage.gpx.Definitions",  key)
                 });
                 p.setDefinition (resources[1]);
                 p.setDesignation(resources[0]);

Reply via email to