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 8f1dbbf6df840ad0e5cf8eb9a69288b5218878e6 Author: Martin Desruisseaux <martin.desruisse...@geomatys.com> AuthorDate: Thu Mar 24 17:36:40 2022 +0100 `AbstractResource` does not inherit anymore from `StoreListeners`. Instead, `StoreListeners` become a field. This is a step toward moving `AbstractResource` to public API. --- .../org/apache/sis/storage/landsat/BandGroup.java | 2 +- .../apache/sis/storage/landsat/package-info.java | 2 +- .../sis/storage/landsat/MetadataReaderTest.java | 24 +++++++- .../org/apache/sis/storage/geotiff/CRSBuilder.java | 6 +- .../org/apache/sis/storage/geotiff/DataCube.java | 11 ++++ .../apache/sis/storage/geotiff/GeoTiffStore.java | 17 ++---- .../sis/storage/geotiff/GridGeometryBuilder.java | 5 +- .../sis/storage/geotiff/ImageFileDirectory.java | 18 ++++-- .../sis/storage/geotiff/ImageMetadataBuilder.java | 5 +- .../org/apache/sis/storage/geotiff/Reader.java | 2 +- .../sis/internal/netcdf/DiscreteSampling.java | 2 +- .../apache/sis/internal/netcdf/RasterResource.java | 12 ++-- .../org/apache/sis/internal/netcdf/TestCase.java | 26 ++++++++- .../internal/netcdf/impl/ChannelDecoderTest.java | 3 +- .../storage/netcdf/NetcdfStoreProviderTest.java | 5 +- .../org/apache/sis/internal/sql/feature/Table.java | 2 +- .../sis/internal/storage/AbstractFeatureSet.java | 5 +- .../sis/internal/storage/AbstractGridResource.java | 15 +++-- .../sis/internal/storage/AbstractResource.java | 65 +++++++++++++++------- .../sis/internal/storage/AggregatedFeatureSet.java | 4 +- .../internal/storage/ConcatenatedFeatureSet.java | 2 +- .../org/apache/sis/storage/CoverageSubset.java | 6 +- .../java/org/apache/sis/storage/DataStore.java | 2 +- .../java/org/apache/sis/storage/FeatureSubset.java | 2 +- .../main/java/org/apache/sis/storage/Resource.java | 4 +- .../apache/sis/storage/event/StoreListeners.java | 14 +---- .../internal/storage/AbstractGridResourceTest.java | 4 +- 27 files changed, 168 insertions(+), 97 deletions(-) diff --git a/storage/sis-earth-observation/src/main/java/org/apache/sis/storage/landsat/BandGroup.java b/storage/sis-earth-observation/src/main/java/org/apache/sis/storage/landsat/BandGroup.java index 3750f28..1ec0a31 100644 --- a/storage/sis-earth-observation/src/main/java/org/apache/sis/storage/landsat/BandGroup.java +++ b/storage/sis-earth-observation/src/main/java/org/apache/sis/storage/landsat/BandGroup.java @@ -114,7 +114,7 @@ final class BandGroup extends AbstractResource implements Aggregate { protected Metadata createMetadata() throws DataStoreException { final MetadataBuilder metadata = new MetadataBuilder(); metadata.addTitle(group.title); // Must be before `addDefaultMetadata(…)`. - metadata.addDefaultMetadata(this, this); + metadata.addDefaultMetadata(this, listeners); return metadata.build(true); } diff --git a/storage/sis-earth-observation/src/main/java/org/apache/sis/storage/landsat/package-info.java b/storage/sis-earth-observation/src/main/java/org/apache/sis/storage/landsat/package-info.java index 81a1201..2f50ae6 100644 --- a/storage/sis-earth-observation/src/main/java/org/apache/sis/storage/landsat/package-info.java +++ b/storage/sis-earth-observation/src/main/java/org/apache/sis/storage/landsat/package-info.java @@ -27,7 +27,7 @@ * @author Thi Phuong Hao Nguyen (VNSC) * @author Minh Chinh Vu (VNSC) * @author Martin Desruisseaux (Geomatys) - * @version 1.1 + * @version 1.2 * @since 1.1 * @module */ diff --git a/storage/sis-earth-observation/src/test/java/org/apache/sis/storage/landsat/MetadataReaderTest.java b/storage/sis-earth-observation/src/test/java/org/apache/sis/storage/landsat/MetadataReaderTest.java index fe050d3..5355e5d 100644 --- a/storage/sis-earth-observation/src/test/java/org/apache/sis/storage/landsat/MetadataReaderTest.java +++ b/storage/sis-earth-observation/src/test/java/org/apache/sis/storage/landsat/MetadataReaderTest.java @@ -35,6 +35,7 @@ import org.opengis.metadata.spatial.DimensionNameType; import org.opengis.util.FactoryException; import org.opengis.test.dataset.ContentVerifier; import org.apache.sis.storage.DataStoreException; +import org.apache.sis.storage.event.StoreListeners; import org.apache.sis.test.TestCase; import org.junit.Test; @@ -79,7 +80,7 @@ public class MetadataReaderTest extends TestCase { try (BufferedReader in = new BufferedReader(new InputStreamReader( MetadataReaderTest.class.getResourceAsStream("LandsatTest.txt"), "UTF-8"))) { - final MetadataReader reader = new MetadataReader(null, "LandsatTest.txt", new AbstractResource(null)); + final MetadataReader reader = new MetadataReader(null, "LandsatTest.txt", createListeners()); reader.read(in); actual = reader.getMetadata(); } @@ -254,4 +255,25 @@ public class MetadataReaderTest extends TestCase { verifier.assertMetadataEquals(); } + + /** + * Creates a dummy set of store listeners. + * Used only for constructors that require a non-null {@link StoreListeners} instance. + * + * @return a dummy set of listeners. + */ + private static StoreListeners createListeners() { + final class DummyResource extends AbstractResource { + /** Creates a dummy resource without parent. */ + DummyResource() { + super(null); + } + + /** Makes listeners accessible to this package. */ + StoreListeners listeners() { + return listeners; + } + } + return new DummyResource().listeners(); + } } diff --git a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/CRSBuilder.java b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/CRSBuilder.java index 5bfc184..5781ee6 100644 --- a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/CRSBuilder.java +++ b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/CRSBuilder.java @@ -432,7 +432,7 @@ final class CRSBuilder extends ReferencingFactoryContainer { try { expected = Integer.parseInt(id.getCode()); } catch (NumberFormatException e) { - reader.store.warning(null, e); // Should not happen. + reader.store.listeners().warning(e); // Should not happen. return; } if (code != expected) { @@ -562,7 +562,7 @@ final class CRSBuilder extends ReferencingFactoryContainer { if (epsg != null) try { return getCSAuthorityFactory().createCartesianCS(epsg.toString()); } catch (NoSuchAuthorityCodeException e) { - reader.store.warning(null, e); + reader.store.listeners().warning(e); } return (CartesianCS) CoordinateSystems.replaceLinearUnit(cs, unit); } @@ -580,7 +580,7 @@ final class CRSBuilder extends ReferencingFactoryContainer { if (epsg != null) try { return getCSAuthorityFactory().createEllipsoidalCS(epsg.toString()); } catch (NoSuchAuthorityCodeException e) { - reader.store.warning(null, e); + reader.store.listeners().warning(e); } return (EllipsoidalCS) CoordinateSystems.replaceAngularUnit(cs, unit); } diff --git a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/DataCube.java b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/DataCube.java index 8bcefef..7cfa2cc 100644 --- a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/DataCube.java +++ b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/DataCube.java @@ -16,6 +16,7 @@ */ package org.apache.sis.storage.geotiff; +import java.util.Locale; import java.nio.file.Path; import java.awt.image.DataBuffer; import java.awt.image.SampleModel; @@ -79,6 +80,16 @@ abstract class DataCube extends TiledGridResource implements ResourceOnFileSyste } /** + * Returns the locale for warnings and error messages. + * + * <p><b>Warning:</b> do not implement {@link org.apache.sis.util.Localized}, + * as it may cause an infinite loop in {@code listeners.getLocale()} call.</p> + */ + final Locale getLocale() { + return listeners.getLocale(); + } + + /** * Shortcut for a frequently requested information. */ final String filename() { diff --git a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GeoTiffStore.java b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GeoTiffStore.java index 779c92d..8c935fa 100644 --- a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GeoTiffStore.java +++ b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GeoTiffStore.java @@ -237,6 +237,8 @@ public class GeoTiffStore extends DataStore implements Aggregate { /** * Opens access to listeners for {@link ImageFileDirectory}. + * + * @see #warning(LogRecord) */ final StoreListeners listeners() { return listeners; @@ -517,20 +519,9 @@ public class GeoTiffStore extends DataStore implements Aggregate { } /** - * Reports a warning represented by the given message and exception. - * At least one of {@code message} and {@code exception} shall be non-null. - * - * @param message the message to log, or {@code null} if none. - * @param exception the exception to log, or {@code null} if none. - */ - final void warning(final String message, final Exception exception) { - listeners.warning(message, exception); - } - - /** * Reports a warning contained in the given {@link LogRecord}. * Note that the given record will not necessarily be sent to the logging framework; - * if the user as registered at least one listener, then the record will be sent to the listeners instead. + * if the user has registered at least one listener, then the record will be sent to the listeners instead. * * <p>This method sets the {@linkplain LogRecord#setSourceClassName(String) source class name} and * {@linkplain LogRecord#setSourceMethodName(String) source method name} to hard-coded values. @@ -538,6 +529,8 @@ public class GeoTiffStore extends DataStore implements Aggregate { * in this class. We do not report private classes or methods as the source of warnings.</p> * * @param record the warning to report. + * + * @see #listeners() */ final void warning(final LogRecord record) { // Logger name will be set by listeners.warning(record). diff --git a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GridGeometryBuilder.java b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GridGeometryBuilder.java index 461464f..f4508ab 100644 --- a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GridGeometryBuilder.java +++ b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GridGeometryBuilder.java @@ -274,7 +274,7 @@ final class GridGeometryBuilder extends GeoKeysLoader { if (e instanceof NoSuchAuthorityCodeException) { key = Resources.Keys.UnknownCRS_1; } - reader.store.warning(reader.resources().getString(key, reader.store.getDisplayName()), e); + reader.store.listeners().warning(reader.resources().getString(key, reader.store.getDisplayName()), e); } catch (IllegalArgumentException | NoSuchElementException | ClassCastException e) { if (!helper.alreadyReported) { canNotCreate(reader, e); @@ -380,6 +380,7 @@ final class GridGeometryBuilder extends GeoKeysLoader { * Logs a warning telling that we can not create a grid geometry for the given reason. */ private static void canNotCreate(final Reader reader, final Exception e) { - reader.store.warning(reader.resources().getString(Resources.Keys.CanNotComputeGridGeometry_1, reader.input.filename), e); + reader.store.listeners().warning(reader.resources().getString( + Resources.Keys.CanNotComputeGridGeometry_1, reader.input.filename), e); } } diff --git a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/ImageFileDirectory.java b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/ImageFileDirectory.java index be48624..9ad8b50 100644 --- a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/ImageFileDirectory.java +++ b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/ImageFileDirectory.java @@ -1341,14 +1341,14 @@ final class ImageFileDirectory extends DataCube { if (gridGeometry.isDefined(GridGeometry.ENVELOPE)) try { metadata.addExtent(gridGeometry.getEnvelope()); } catch (TransformException e) { - warning(e); + listeners.warning(e); } referencing.completeMetadata(gridGeometry, metadata); } /* * End of metadata construction from TIFF tags. */ - metadata.finish(this); + metadata.finish(this, listeners); final DefaultMetadata md = metadata.build(false); if (isIndexValid) { final Metadata c = reader.store.customizer.customize(index, md); @@ -1698,14 +1698,24 @@ final class ImageFileDirectory extends DataCube { /** * Reports a warning with a message created from the given resource keys and parameters. + * Note that the log record will not necessarily be sent to the logging framework; + * if the user has registered at least one listener, then the record will be sent to the listeners instead. + * + * <p>This method sets the {@linkplain LogRecord#setSourceClassName(String) source class name} and + * {@linkplain LogRecord#setSourceMethodName(String) source method name} to hard-coded values. + * Those values assume that the warnings occurred indirectly from a call to {@link GeoTiffStore#components()}. + * We do not report private classes or methods as the source of warnings.</p> * * @param level the logging level for the message to log. * @param key the {@code Resources} key of the message to format. * @param parameters the parameters to put in the message. */ private void warning(final Level level, final short key, final Object... parameters) { - final LogRecord r = reader.resources().getLogRecord(level, key, parameters); - reader.store.warning(r); + final LogRecord record = reader.resources().getLogRecord(level, key, parameters); + record.setSourceClassName(GeoTiffStore.class.getName()); + record.setSourceMethodName("components()"); + // Logger name will be set by listeners.warning(record). + listeners.warning(record); } /** diff --git a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/ImageMetadataBuilder.java b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/ImageMetadataBuilder.java index 22db2a8..8870d00 100644 --- a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/ImageMetadataBuilder.java +++ b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/ImageMetadataBuilder.java @@ -21,6 +21,7 @@ import javax.measure.quantity.Length; import org.apache.sis.internal.geotiff.Resources; import org.apache.sis.internal.geotiff.Compression; import org.apache.sis.internal.storage.MetadataBuilder; +import org.apache.sis.storage.event.StoreListeners; import org.apache.sis.storage.DataStoreException; import org.apache.sis.util.resources.Errors; import org.apache.sis.util.CharSequences; @@ -168,7 +169,7 @@ final class ImageMetadataBuilder extends MetadataBuilder { * * @throws DataStoreException if an error occurred while reading metadata from the data store. */ - void finish(final ImageFileDirectory image) throws DataStoreException { + void finish(final ImageFileDirectory image, final StoreListeners listeners) throws DataStoreException { image.getIdentifier().ifPresent((id) -> addTitle(id.toString())); /* * Add information about the file format. @@ -228,7 +229,7 @@ final class ImageMetadataBuilder extends MetadataBuilder { while (complement != null) try { complement = complement.appendTo(this); } catch (Exception ex) { - image.warning(image.reader.errors().getString(Errors.Keys.CanNotSetPropertyValue_1, complement.tag()), ex); + listeners.warning(image.reader.errors().getString(Errors.Keys.CanNotSetPropertyValue_1, complement.tag()), ex); } } } diff --git a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/Reader.java b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/Reader.java index 3af73e0..c770d60 100644 --- a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/Reader.java +++ b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/Reader.java @@ -449,7 +449,7 @@ final class Reader extends GeoTIFF { exception = null; } args[0] = Tags.name(tag); - store.warning(errors().getString(key, args), exception); + store.listeners().warning(errors().getString(key, args), exception); } /** diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/DiscreteSampling.java b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/DiscreteSampling.java index affc0f8..1302cb7 100644 --- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/DiscreteSampling.java +++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/DiscreteSampling.java @@ -79,6 +79,6 @@ public abstract class DiscreteSampling extends AbstractFeatureSet { * @return default error message to use in exceptions. */ protected final String canNotReadFile() { - return Errors.getResources(getLocale()).getString(Errors.Keys.CanNotRead_1, getSourceName()); + return Errors.getResources(listeners.getLocale()).getString(Errors.Keys.CanNotRead_1, listeners.getSourceName()); } } diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/RasterResource.java b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/RasterResource.java index db1ece4..499f55e 100644 --- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/RasterResource.java +++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/RasterResource.java @@ -401,7 +401,7 @@ public final class RasterResource extends AbstractGridResource implements Resour if (title != null && !title.isEmpty()) { metadata.addTitle(CharSequences.camelCaseToSentence(title).toString()); } - metadata.addDefaultMetadata(this, this); + metadata.addDefaultMetadata(this, listeners); return metadata.build(true); } @@ -525,7 +525,7 @@ public final class RasterResource extends AbstractGridResource implements Resour * If we failed to do that, we will not add quantitative category. But we still can add * qualitative categories for "no data" sample values in the rest of this method. */ - warning(e); + listeners.warning(e); } /* * Adds the "missing value" or "fill value" as qualitative categories. If a value has both roles, use "missing value" @@ -584,7 +584,7 @@ public final class RasterResource extends AbstractGridResource implements Resour */ builder.categories().clear(); sd = builder.build(); - warning(e); + listeners.warning(e); } return sd; } @@ -630,7 +630,7 @@ public final class RasterResource extends AbstractGridResource implements Resour for (int i=0; i<rangeIndices.getNumBands(); i++) { final Variable variable = data[rangeIndices.getSourceIndex(i)]; if (!dataType.equals(variable.getDataType())) { - throw new DataStoreContentException(Resources.forLocale(getLocale()).getString( + throw new DataStoreContentException(Resources.forLocale(listeners.getLocale()).getString( Resources.Keys.MismatchedVariableType_3, getFilename(), first.getName(), variable.getName())); } } @@ -723,7 +723,7 @@ public final class RasterResource extends AbstractGridResource implements Resour * Provide to `Raster` all information needed for building a `RenderedImage` when requested. */ if (imageBuffer == null) { - throw new DataStoreContentException(Errors.getResources(getLocale()).getString(Errors.Keys.UnsupportedType_1, dataType.name())); + throw new DataStoreContentException(Errors.getResources(listeners.getLocale()).getString(Errors.Keys.UnsupportedType_1, dataType.name())); } final Variable main = data[visibleBand]; final Raster raster = new Raster(targetDomain, UnmodifiableArrayList.wrap(bands), imageBuffer, @@ -737,7 +737,7 @@ public final class RasterResource extends AbstractGridResource implements Resour * Returns the name of the netCDF file. This is used for error messages. */ private String getFilename() { - return (location != null) ? location.getFileName().toString() : getSourceName(); + return (location != null) ? location.getFileName().toString() : listeners.getSourceName(); } /** diff --git a/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/TestCase.java b/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/TestCase.java index fc8365a..86f8c4b 100644 --- a/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/TestCase.java +++ b/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/TestCase.java @@ -26,6 +26,7 @@ import org.apache.sis.storage.DataStoreException; import org.apache.sis.internal.storage.AbstractResource; import org.apache.sis.internal.netcdf.ucar.DecoderWrapper; import org.apache.sis.setup.GeometryLibrary; +import org.apache.sis.storage.event.StoreListeners; import org.opengis.test.dataset.TestData; import ucar.nc2.dataset.NetcdfDataset; import ucar.nc2.NetcdfFile; @@ -41,7 +42,7 @@ import static org.junit.Assert.*; * <p>This class is <strong>not</strong> thread safe - do not run subclasses in parallel.</p> * * @author Martin Desruisseaux (Geomatys) - * @version 1.0 + * @version 1.2 * @since 0.3 * @module */ @@ -110,7 +111,28 @@ public abstract strictfp class TestCase extends org.apache.sis.test.TestCase { * @throws DataStoreException if a logical error occurred. */ protected Decoder createDecoder(final TestData file) throws IOException, DataStoreException { - return new DecoderWrapper(new NetcdfDataset(createUCAR(file)), GeometryLibrary.JAVA2D, new AbstractResource(null)); + return new DecoderWrapper(new NetcdfDataset(createUCAR(file)), GeometryLibrary.JAVA2D, createListeners()); + } + + /** + * Creates a dummy set of store listeners. + * Used only for constructors that require a non-null {@link StoreListeners} instance. + * + * @return a dummy set of listeners. + */ + protected static StoreListeners createListeners() { + final class DummyResource extends AbstractResource { + /** Creates a dummy resource without parent. */ + DummyResource() { + super(null); + } + + /** Makes listeners accessible to this package. */ + StoreListeners listeners() { + return listeners; + } + } + return new DummyResource().listeners(); } /** diff --git a/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/impl/ChannelDecoderTest.java b/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/impl/ChannelDecoderTest.java index a18cf5e..d426ecd 100644 --- a/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/impl/ChannelDecoderTest.java +++ b/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/impl/ChannelDecoderTest.java @@ -22,7 +22,6 @@ import java.nio.ByteBuffer; import java.nio.channels.Channels; import org.apache.sis.internal.netcdf.Decoder; import org.apache.sis.internal.netcdf.DecoderTest; -import org.apache.sis.internal.storage.AbstractResource; import org.apache.sis.internal.storage.io.ChannelDataInput; import org.apache.sis.storage.DataStoreException; import org.apache.sis.setup.GeometryLibrary; @@ -71,6 +70,6 @@ public final strictfp class ChannelDecoderTest extends DecoderTest { final InputStream in = file.open(); final ChannelDataInput input = new ChannelDataInput(file.name(), Channels.newChannel(in), ByteBuffer.allocate(4096), false); - return new ChannelDecoder(input, null, GeometryLibrary.JAVA2D, new AbstractResource(null)); + return new ChannelDecoder(input, null, GeometryLibrary.JAVA2D, createListeners()); } } diff --git a/storage/sis-netcdf/src/test/java/org/apache/sis/storage/netcdf/NetcdfStoreProviderTest.java b/storage/sis-netcdf/src/test/java/org/apache/sis/storage/netcdf/NetcdfStoreProviderTest.java index 8acae77..ac33a21 100644 --- a/storage/sis-netcdf/src/test/java/org/apache/sis/storage/netcdf/NetcdfStoreProviderTest.java +++ b/storage/sis-netcdf/src/test/java/org/apache/sis/storage/netcdf/NetcdfStoreProviderTest.java @@ -23,7 +23,6 @@ import org.apache.sis.internal.netcdf.Decoder; import org.apache.sis.internal.netcdf.ucar.DecoderWrapper; import org.apache.sis.internal.netcdf.impl.ChannelDecoder; import org.apache.sis.internal.netcdf.impl.ChannelDecoderTest; -import org.apache.sis.internal.storage.AbstractResource; import org.apache.sis.storage.ProbeResult; import org.apache.sis.storage.StorageConnector; import org.apache.sis.storage.DataStoreException; @@ -92,7 +91,7 @@ public final strictfp class NetcdfStoreProviderTest extends TestCase { @Test public void testDecoderFromStream() throws IOException, DataStoreException { final StorageConnector c = new StorageConnector(TestData.NETCDF_2D_GEOGRAPHIC.open()); - try (Decoder decoder = NetcdfStoreProvider.decoder(new AbstractResource(null), c)) { + try (Decoder decoder = NetcdfStoreProvider.decoder(createListeners(), c)) { assertInstanceOf("decoder", ChannelDecoder.class, decoder); } } @@ -107,7 +106,7 @@ public final strictfp class NetcdfStoreProviderTest extends TestCase { @Test public void testDecoderFromUCAR() throws IOException, DataStoreException { final StorageConnector c = new StorageConnector(createUCAR(TestData.NETCDF_2D_GEOGRAPHIC)); - try (Decoder decoder = NetcdfStoreProvider.decoder(new AbstractResource(null), c)) { + try (Decoder decoder = NetcdfStoreProvider.decoder(createListeners(), c)) { assertInstanceOf("decoder", DecoderWrapper.class, decoder); } } diff --git a/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/Table.java b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/Table.java index 7e5d027..110d629 100644 --- a/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/Table.java +++ b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/Table.java @@ -194,7 +194,7 @@ final class Table extends AbstractFeatureSet { * A starting point is {@link org.apache.sis.storage.FeatureQuery#expectedType(FeatureType)}. */ Table(final Table parent) { - super(parent); + super(parent.listeners); database = parent.database; query = parent.query; name = parent.name; diff --git a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AbstractFeatureSet.java b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AbstractFeatureSet.java index 6062a9c..49ae01f 100644 --- a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AbstractFeatureSet.java +++ b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AbstractFeatureSet.java @@ -56,8 +56,7 @@ public abstract class AbstractFeatureSet extends AbstractResource implements Fea * Creates a new resource. * * @param parent listeners of the parent resource, or {@code null} if none. - * This is usually the listeners of the {@link org.apache.sis.storage.DataStore} - * that created this resource. + * This is usually the listeners of the {@link DataStore} that created this resource. */ protected AbstractFeatureSet(final StoreListeners parent) { super(parent); @@ -103,7 +102,7 @@ public abstract class AbstractFeatureSet extends AbstractResource implements Fea @Override protected Metadata createMetadata() throws DataStoreException { final MetadataBuilder builder = new MetadataBuilder(); - builder.addDefaultMetadata(this, this); + builder.addDefaultMetadata(this, listeners); return builder.build(true); } } diff --git a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AbstractGridResource.java b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AbstractGridResource.java index 0aaf6c3..dca3626 100644 --- a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AbstractGridResource.java +++ b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AbstractGridResource.java @@ -85,8 +85,7 @@ public abstract class AbstractGridResource extends AbstractResource implements G * Creates a new resource. * * @param parent listeners of the parent resource, or {@code null} if none. - * This is usually the listeners of the {@link org.apache.sis.storage.DataStore} - * that created this resource. + * This is usually the listeners of the {@link DataStore} that created this resource. */ protected AbstractGridResource(final StoreListeners parent) { super(parent); @@ -123,7 +122,7 @@ public abstract class AbstractGridResource extends AbstractResource implements G @Override protected Metadata createMetadata() throws DataStoreException { final MetadataBuilder builder = new MetadataBuilder(); - builder.addDefaultMetadata(this, this); + builder.addDefaultMetadata(this, listeners); return builder.build(true); } @@ -154,7 +153,7 @@ public abstract class AbstractGridResource extends AbstractResource implements G for (int i=0; i<range.length; i++) { final int r = range[i]; if (r < 0 || r >= numSampleDimensions) { - throw new IllegalArgumentException(Resources.forLocale(getLocale()).getString( + throw new IllegalArgumentException(Resources.forLocale(listeners.getLocale()).getString( Resources.Keys.InvalidSampleDimensionIndex_2, numSampleDimensions - 1, r)); } packed[i] = (((long) r) << Integer.SIZE) | i; @@ -169,7 +168,7 @@ public abstract class AbstractGridResource extends AbstractResource implements G // Never negative because of check in previous loop. final int r = (int) (packed[i] >>> Integer.SIZE); if (r == previous) { - throw new IllegalArgumentException(Resources.forLocale(getLocale()).getString( + throw new IllegalArgumentException(Resources.forLocale(listeners.getLocale()).getString( Resources.Keys.DuplicatedSampleDimensionIndex_1, r)); } previous = r; @@ -532,13 +531,13 @@ public abstract class AbstractGridResource extends AbstractResource implements G * @param startTime value of {@link System#nanoTime()} when the loading process started. */ protected final void logReadOperation(final Object file, final GridGeometry domain, final long startTime) { - final Logger logger = getLogger(); + final Logger logger = listeners.getLogger(); final long nanos = System.nanoTime() - startTime; final Level level = PerformanceLevel.forDuration(nanos, TimeUnit.NANOSECONDS); if (logger.isLoggable(level)) { - final Locale locale = getLocale(); + final Locale locale = listeners.getLocale(); final Object[] parameters = new Object[6]; - parameters[0] = IOUtilities.filename(file != null ? file : getSourceName()); + parameters[0] = IOUtilities.filename(file != null ? file : listeners.getSourceName()); parameters[5] = nanos / (double) StandardDateFormat.NANOS_PER_SECOND; JDK9.ifPresentOrElse(domain.getGeographicExtent(), (box) -> { final AngleFormat f = new AngleFormat(locale); diff --git a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AbstractResource.java b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AbstractResource.java index a16efba..ef5b9cf 100644 --- a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AbstractResource.java +++ b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AbstractResource.java @@ -16,6 +16,7 @@ */ package org.apache.sis.internal.storage; +import java.util.Locale; import java.util.Optional; import org.opengis.util.GenericName; import org.opengis.metadata.Metadata; @@ -26,11 +27,11 @@ import org.apache.sis.referencing.IdentifiedObjects; import org.apache.sis.geometry.Envelopes; import org.apache.sis.referencing.CRS; import org.apache.sis.storage.Resource; +import org.apache.sis.storage.DataStore; import org.apache.sis.storage.DataStoreException; import org.apache.sis.storage.event.StoreEvent; import org.apache.sis.storage.event.StoreListener; import org.apache.sis.storage.event.StoreListeners; -import org.apache.sis.storage.event.WarningEvent; import org.apache.sis.util.resources.Errors; import org.apache.sis.util.logging.Logging; import org.apache.sis.xml.NilReason; @@ -45,11 +46,9 @@ import org.apache.sis.xml.NilReason; * <li>{@link #getEnvelope()} (recommended)</li> * <li>{@link #createMetadata()} (optional)</li> * <li>{@link #getSynchronizationLock()} (optional)</li> + * <li>{@link #addListener(Class, StoreListener)} (if this resource is writable)</li> * </ul> * - * This class extends {@link StoreListeners} for convenience reasons. - * This implementation details may change in any future SIS version. - * * <h2>Thread safety</h2> * Default methods of this abstract class are thread-safe. * Synchronization, when needed, uses {@link #getSynchronizationLock()}. @@ -59,7 +58,18 @@ import org.apache.sis.xml.NilReason; * @since 0.8 * @module */ -public class AbstractResource extends StoreListeners implements Resource { +public abstract class AbstractResource implements Resource { + /* + * Warning: do not implement `org.apache.sis.util.Localized` as it + * may cause an infinite loop in calls to `listeners.getLocale()`. + */ + + /** + * The set of registered {@link StoreListener}s for this resources. + * This {@code StoreListeners} while typically have {@link DataStore#listeners} has a parent. + */ + protected final StoreListeners listeners; + /** * A description of this resource as an unmodifiable metadata, or {@code null} if not yet computed. * If non-null, this metadata should contain at least the resource {@linkplain #getIdentifier() identifier}. @@ -72,11 +82,10 @@ public class AbstractResource extends StoreListeners implements Resource { * but the listeners of the data store that created this resource will be notified as well. * * @param parent listeners of the parent resource, or {@code null} if none. - * This is usually the listeners of the {@link org.apache.sis.storage.DataStore} - * that created this resource. + * This is usually the listeners of the {@link DataStore} that created this resource. */ - public AbstractResource(final StoreListeners parent) { - super(parent, null); + protected AbstractResource(final StoreListeners parent) { + listeners = new StoreListeners(parent, this); } /** @@ -142,7 +151,7 @@ public class AbstractResource extends StoreListeners implements Resource { */ protected Metadata createMetadata() throws DataStoreException { final MetadataBuilder builder = new MetadataBuilder(); - builder.addDefaultMetadata(this, this); + builder.addDefaultMetadata(this, listeners); return builder.build(true); } @@ -157,6 +166,7 @@ public class AbstractResource extends StoreListeners implements Resource { /** * Returns the object on which to perform synchronizations for thread-safety. + * The default implementation returns {@code this}. * * @return the synchronization lock. */ @@ -165,15 +175,29 @@ public class AbstractResource extends StoreListeners implements Resource { } /** - * Registers only listeners for {@link WarningEvent}s on the assumption that most resources - * (at least the read-only ones) produce no change events. + * Registers a listener to notify when the specified kind of event occurs in this resource or in children. + * The default implementation forwards to <code>{@linkplain #listeners}.addListener(eventType, listener)</code>. + * + * @param <T> compile-time value of the {@code eventType} argument. + * @param listener listener to notify about events. + * @param eventType type of {@link StoreEvent}s to listen (can not be {@code null}). */ @Override public <T extends StoreEvent> void addListener(Class<T> eventType, StoreListener<? super T> listener) { - // If an argument is null, we let the parent class throws (indirectly) NullArgumentException. - if (listener == null || eventType == null || eventType.isAssignableFrom(WarningEvent.class)) { - super.addListener(eventType, listener); - } + listeners.addListener(eventType, listener); + } + + /** + * Unregisters a listener previously added to this resource for the given type of events. + * The default implementation forwards to <code>{@linkplain #listeners}.removeListener(eventType, listener)</code> + * + * @param <T> compile-time value of the {@code eventType} argument. + * @param listener listener to stop notifying about events. + * @param eventType type of {@link StoreEvent}s which were listened (can not be {@code null}). + */ + @Override + public <T extends StoreEvent> void removeListener(Class<T> eventType, StoreListener<? super T> listener) { + listeners.removeListener(eventType, listener); } /** @@ -186,7 +210,8 @@ public class AbstractResource extends StoreListeners implements Resource { * @return the message to provide in an exception. */ final String createExceptionMessage(final String filename, Envelope request) { - String message = Errors.getResources(getLocale()).getString(Errors.Keys.CanNotRead_1, filename); + final Locale locale = listeners.getLocale(); + String message = Errors.getResources(locale).getString(Errors.Keys.CanNotRead_1, filename); if (request != null) try { Envelope envelope = getEnvelope().orElse(null); if (envelope != null) { @@ -205,7 +230,7 @@ public class AbstractResource extends StoreListeners implements Resource { if (rmax < vmin || rmin > vmax) { final String axis; if (crs != null) { - axis = IdentifiedObjects.getDisplayName(crs.getCoordinateSystem().getAxis(i), getLocale()); + axis = IdentifiedObjects.getDisplayName(crs.getCoordinateSystem().getAxis(i), locale); } else if (i < 3) { axis = String.valueOf((char) ('x' + i)); } else { @@ -214,7 +239,7 @@ public class AbstractResource extends StoreListeners implements Resource { if (buffer == null) { buffer = new StringBuilder(message); } - buffer.append(System.lineSeparator()).append(" • ").append(Resources.forLocale(getLocale()) + buffer.append(System.lineSeparator()).append(" • ").append(Resources.forLocale(locale) .getString(Resources.Keys.RequestOutOfBounds_5, axis, vmin, vmax, rmin, rmax)); } } @@ -223,7 +248,7 @@ public class AbstractResource extends StoreListeners implements Resource { } } } catch (DataStoreException | TransformException e) { - Logging.ignorableException(getLogger(), AbstractResource.class, "createExceptionMessage", e); + Logging.ignorableException(listeners.getLogger(), AbstractResource.class, "createExceptionMessage", e); } return message; } diff --git a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AggregatedFeatureSet.java b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AggregatedFeatureSet.java index 9d9ad80..184f715 100644 --- a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AggregatedFeatureSet.java +++ b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/AggregatedFeatureSet.java @@ -125,7 +125,7 @@ abstract class AggregatedFeatureSet extends AbstractFeatureSet { if (getEnvelopes(envelopes)) try { envelope = ImmutableEnvelope.castOrCopy(Envelopes.union(envelopes.toArray(new Envelope[envelopes.size()]))); } catch (TransformException e) { - warning(e); + listeners.warning(e); } isEnvelopeComputed = true; } @@ -144,7 +144,7 @@ abstract class AggregatedFeatureSet extends AbstractFeatureSet { @Override protected Metadata createMetadata() throws DataStoreException { final MetadataBuilder metadata = new MetadataBuilder(); - metadata.addDefaultMetadata(this, this); + metadata.addDefaultMetadata(this, listeners); for (final FeatureSet fs : dependencies()) { final FeatureType type = fs.getType(); metadata.addSource(fs.getMetadata(), ScopeCode.FEATURE_TYPE, diff --git a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/ConcatenatedFeatureSet.java b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/ConcatenatedFeatureSet.java index 8e0d0dc..0428c6d 100644 --- a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/ConcatenatedFeatureSet.java +++ b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/ConcatenatedFeatureSet.java @@ -71,7 +71,7 @@ public class ConcatenatedFeatureSet extends AggregatedFeatureSet { * but different sources. This is used for creating {@linkplain #subset(Query) subsets}. */ private ConcatenatedFeatureSet(final FeatureSet[] sources, final ConcatenatedFeatureSet original) { - super(original); + super(original.listeners); this.sources = UnmodifiableArrayList.wrap(sources); commonType = original.commonType; } diff --git a/storage/sis-storage/src/main/java/org/apache/sis/storage/CoverageSubset.java b/storage/sis-storage/src/main/java/org/apache/sis/storage/CoverageSubset.java index 4c6aba3..fd8ebd6 100644 --- a/storage/sis-storage/src/main/java/org/apache/sis/storage/CoverageSubset.java +++ b/storage/sis-storage/src/main/java/org/apache/sis/storage/CoverageSubset.java @@ -115,8 +115,8 @@ final class CoverageSubset extends AbstractGridResource { } return derivation.subgrid(areaOfInterest).build(); } catch (IllegalArgumentException | IllegalStateException e) { - final String msg = Resources.forLocale(getLocale()) - .getString(Resources.Keys.CanNotIntersectDataWithQuery_1, getSourceName()); + final String msg = Resources.forLocale(listeners.getLocale()) + .getString(Resources.Keys.CanNotIntersectDataWithQuery_1, listeners.getSourceName()); final Throwable cause = e.getCause(); if (cause instanceof FactoryException || cause instanceof TransformException) { throw new DataStoreReferencingException(msg, cause); @@ -222,7 +222,7 @@ final class CoverageSubset extends AbstractGridResource { * @param index the index which is out of bounds. */ private String invalidRange(final int size, final int index) { - return Resources.forLocale(getLocale()).getString( + return Resources.forLocale(listeners.getLocale()).getString( Resources.Keys.InvalidSampleDimensionIndex_2, size - 1, index); } } diff --git a/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStore.java b/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStore.java index ffc73b6..2237b29 100644 --- a/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStore.java +++ b/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStore.java @@ -238,7 +238,7 @@ public abstract class DataStore implements Resource, Localized, AutoCloseable { // See class javadoc for a note on synchronization. /** - * The locale to use for formatting warnings and other messages. This locale if for user interfaces + * The locale to use for formatting warnings and other messages. This locale is for user interfaces * only – it has no effect on the data to be read or written from/to the data store. * * <p>The default value is the {@linkplain Locale#getDefault() system default locale}.</p> diff --git a/storage/sis-storage/src/main/java/org/apache/sis/storage/FeatureSubset.java b/storage/sis-storage/src/main/java/org/apache/sis/storage/FeatureSubset.java index 5ee4507..c0cfb4e 100644 --- a/storage/sis-storage/src/main/java/org/apache/sis/storage/FeatureSubset.java +++ b/storage/sis-storage/src/main/java/org/apache/sis/storage/FeatureSubset.java @@ -80,7 +80,7 @@ final class FeatureSubset extends AbstractFeatureSet { try { resultType = query.expectedType(type); } catch (IllegalArgumentException e) { - throw new DataStoreContentException(Resources.forLocale(getLocale()) + throw new DataStoreContentException(Resources.forLocale(listeners.getLocale()) .getString(Resources.Keys.CanNotDeriveTypeFromFeature_1, type.getName()), e); } } diff --git a/storage/sis-storage/src/main/java/org/apache/sis/storage/Resource.java b/storage/sis-storage/src/main/java/org/apache/sis/storage/Resource.java index 0ed22e5..42010c3 100644 --- a/storage/sis-storage/src/main/java/org/apache/sis/storage/Resource.java +++ b/storage/sis-storage/src/main/java/org/apache/sis/storage/Resource.java @@ -164,7 +164,7 @@ public interface Resource { * * @param <T> compile-time value of the {@code eventType} argument. * @param listener listener to notify about events. - * @param eventType type of {@link StoreEvent} to listen (can not be {@code null}). + * @param eventType type of {@link StoreEvent}s to listen (can not be {@code null}). */ <T extends StoreEvent> void addListener(Class<T> eventType, StoreListener<? super T> listener); @@ -186,7 +186,7 @@ public interface Resource { * * @param <T> compile-time value of the {@code eventType} argument. * @param listener listener to stop notifying about events. - * @param eventType type of {@link StoreEvent} which were listened (can not be {@code null}). + * @param eventType type of {@link StoreEvent}s which were listened (can not be {@code null}). */ <T extends StoreEvent> void removeListener(Class<T> eventType, StoreListener<? super T> listener); } diff --git a/storage/sis-storage/src/main/java/org/apache/sis/storage/event/StoreListeners.java b/storage/sis-storage/src/main/java/org/apache/sis/storage/event/StoreListeners.java index 30ce46a..97cfbc3 100644 --- a/storage/sis-storage/src/main/java/org/apache/sis/storage/event/StoreListeners.java +++ b/storage/sis-storage/src/main/java/org/apache/sis/storage/event/StoreListeners.java @@ -32,7 +32,6 @@ import org.apache.sis.util.logging.Logging; import org.apache.sis.util.resources.Vocabulary; import org.apache.sis.internal.storage.StoreResource; import org.apache.sis.internal.storage.StoreUtilities; -import org.apache.sis.internal.storage.AbstractResource; import org.apache.sis.storage.DataStoreProvider; import org.apache.sis.storage.DataStore; import org.apache.sis.storage.Resource; @@ -218,16 +217,7 @@ public class StoreListeners implements Localized { * @param source the source of events. Can not be null. */ public StoreListeners(final StoreListeners parent, Resource source) { - /* - * Undocumented feature for allowing subclass to specify `this` as the source resource. - * This is used as a convenience by AbstractResource internal class. We need this hack - * because subclasses can not reference `this` before super-class constructor completed. - */ - if (source == null && this instanceof AbstractResource) { - source = (Resource) this; - } else { - ArgumentChecks.ensureNonNull("source", source); - } + ArgumentChecks.ensureNonNull("source", source); this.source = source; this.parent = parent; } @@ -235,7 +225,7 @@ public class StoreListeners implements Localized { /** * Returns the source of events. This value is specified at construction time. * - * @return the source of events. Never {@code null} but may be {@code this}. + * @return the source of events (never {@code null}). */ public Resource getSource() { return source; diff --git a/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/AbstractGridResourceTest.java b/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/AbstractGridResourceTest.java index 37bfeea..11b85cd 100644 --- a/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/AbstractGridResourceTest.java +++ b/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/AbstractGridResourceTest.java @@ -31,7 +31,7 @@ import static org.junit.Assert.*; * Tests {@link AbstractGridResource} and {@link AbstractGridResource.RangeArgument}. * * @author Martin Desruisseaux (Geomatys) - * @version 1.0 + * @version 1.2 * @since 1.0 * @module */ @@ -39,7 +39,7 @@ public final strictfp class AbstractGridResourceTest extends TestCase { /** * A resource performing no operation. */ - private final AbstractGridResource resource = new AbstractGridResource((AbstractGridResource) null) { + private final AbstractGridResource resource = new AbstractGridResource(null) { @Override public GridGeometry getGridGeometry() {throw new UnsupportedOperationException();} @Override public List<SampleDimension> getSampleDimensions() {throw new UnsupportedOperationException();} @Override public GridCoverage read(GridGeometry d, int... r) {throw new UnsupportedOperationException();}