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]);
