You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2019/09/06 18:11:48 UTC
[sis] branch geoapi-4.0 updated (ae989c9 -> 4f46eeb)
This is an automated email from the ASF dual-hosted git repository.
desruisseaux pushed a change to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git.
from ae989c9 Add a note saying that Apache SIS can be built with Java version higher than 10. https://issues.apache.org/jira/browse/SIS-438
new 9abaedd Remove a version number which is already declared in dependency management.
new d22d15f Rename ChangeListener as StoreListener and ChangeEvent as StoreEvent. https://issues.apache.org/jira/browse/SIS-421
new 962ee26 Replace hard-coded string value by a new constant in the Modules class.
new b4b8bc3 Shapefile loggers should be package names, not class names.
new 4f46eeb Prepare replacement of WarningListeners by StoreListeners. https://issues.apache.org/jira/browse/SIS-421
The 5 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
.../org/apache/sis/console/TransformCommand.java | 3 +-
.../org/apache/sis/internal/system/Modules.java | 5 +
.../java/org/apache/sis/util/logging/Logging.java | 18 +-
.../apache/sis/util/logging/QuietLogRecord.java | 3 +
.../apache/sis/util/logging/WarningListener.java | 2 +-
.../apache/sis/util/logging/WarningListeners.java | 7 +-
pom.xml | 1 -
.../sis/storage/earthobservation/LandsatStore.java | 8 +-
.../apache/sis/storage/geotiff/GeoTiffStore.java | 8 +-
.../org/apache/sis/storage/netcdf/NetcdfStore.java | 8 +-
.../apache/sis/internal/shapefile/AutoChecker.java | 4 +-
.../internal/shapefile/ShapefileByteReader.java | 64 +--
.../sis/storage/shapefile/InputFeatureStream.java | 2 +-
.../jdbc/AbstractTestBaseForInternalJDBC.java | 2 +-
.../sis/storage/shapefile/ShapeFileTest.java | 4 +-
.../java/org/apache/sis/storage/sql/SQLStore.java | 8 +-
.../sis/internal/storage/AbstractResource.java | 8 +-
.../apache/sis/internal/storage/URIDataStore.java | 8 +-
.../apache/sis/internal/storage/folder/Store.java | 8 +-
.../java/org/apache/sis/storage/DataStore.java | 8 +
.../org/apache/sis/storage/DataStoreProvider.java | 22 +
.../main/java/org/apache/sis/storage/Resource.java | 62 ++-
.../org/apache/sis/storage/event/ChangeEvent.java | 60 --
.../apache/sis/storage/event/ChangeListener.java | 54 --
.../apache/sis/storage/event}/QuietLogRecord.java | 14 +-
.../org/apache/sis/storage/event/StoreEvent.java | 97 ++++
.../apache/sis/storage/event/StoreListener.java | 56 ++
.../apache/sis/storage/event/StoreListeners.java | 607 +++++++++++++++++++++
.../org/apache/sis/storage/event/WarningEvent.java | 90 +++
.../org/apache/sis/storage/event/package-info.java | 18 +-
.../java/org/apache/sis/storage/DataStoreMock.java | 8 +-
31 files changed, 1033 insertions(+), 234 deletions(-)
delete mode 100644 storage/sis-storage/src/main/java/org/apache/sis/storage/event/ChangeEvent.java
delete mode 100644 storage/sis-storage/src/main/java/org/apache/sis/storage/event/ChangeListener.java
copy {core/sis-utility/src/main/java/org/apache/sis/util/logging => storage/sis-storage/src/main/java/org/apache/sis/storage/event}/QuietLogRecord.java (84%)
create mode 100644 storage/sis-storage/src/main/java/org/apache/sis/storage/event/StoreEvent.java
create mode 100644 storage/sis-storage/src/main/java/org/apache/sis/storage/event/StoreListener.java
create mode 100644 storage/sis-storage/src/main/java/org/apache/sis/storage/event/StoreListeners.java
create mode 100644 storage/sis-storage/src/main/java/org/apache/sis/storage/event/WarningEvent.java
[sis] 01/05: Remove a version number which is already declared in
dependency management.
Posted by de...@apache.org.
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 9abaeddfccd23a524e5d9f9dbf47c017b86142a3
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Wed Sep 4 17:32:07 2019 +0200
Remove a version number which is already declared in dependency management.
---
pom.xml | 1 -
1 file changed, 1 deletion(-)
diff --git a/pom.xml b/pom.xml
index c793b97..ceba5e8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -543,7 +543,6 @@
<dependency> <!-- To be removed after https://issues.apache.org/jira/browse/SIS-469 is done. -->
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
- <version>${jaxb.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
[sis] 02/05: Rename ChangeListener as StoreListener and ChangeEvent
as StoreEvent. https://issues.apache.org/jira/browse/SIS-421
Posted by de...@apache.org.
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 d22d15fa8e6c9d1ff00844b637661db97b2025eb
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Wed Sep 4 17:50:38 2019 +0200
Rename ChangeListener as StoreListener and ChangeEvent as StoreEvent.
https://issues.apache.org/jira/browse/SIS-421
---
.../apache/sis/util/logging/WarningListener.java | 2 +-
.../sis/storage/earthobservation/LandsatStore.java | 8 +++----
.../apache/sis/storage/geotiff/GeoTiffStore.java | 8 +++----
.../org/apache/sis/storage/netcdf/NetcdfStore.java | 8 +++----
.../java/org/apache/sis/storage/sql/SQLStore.java | 8 +++----
.../sis/internal/storage/AbstractResource.java | 8 +++----
.../apache/sis/internal/storage/URIDataStore.java | 8 +++----
.../apache/sis/internal/storage/folder/Store.java | 8 +++----
.../main/java/org/apache/sis/storage/Resource.java | 26 +++++++++++-----------
.../event/{ChangeEvent.java => StoreEvent.java} | 9 ++++----
.../{ChangeListener.java => StoreListener.java} | 20 ++++++++---------
.../org/apache/sis/storage/event/package-info.java | 7 ++----
.../java/org/apache/sis/storage/DataStoreMock.java | 8 +++----
13 files changed, 63 insertions(+), 65 deletions(-)
diff --git a/core/sis-utility/src/main/java/org/apache/sis/util/logging/WarningListener.java b/core/sis-utility/src/main/java/org/apache/sis/util/logging/WarningListener.java
index 4781f09..17c9562 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/util/logging/WarningListener.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/util/logging/WarningListener.java
@@ -42,7 +42,7 @@ import java.util.logging.LogRecord;
*
* @see WarningListeners
* @see org.apache.sis.storage.DataStore#addWarningListener(WarningListener)
- * @see org.apache.sis.storage.event.ChangeListener
+ * @see org.apache.sis.storage.event.StoreListener
*
* @since 0.3
* @module
diff --git a/storage/sis-earth-observation/src/main/java/org/apache/sis/storage/earthobservation/LandsatStore.java b/storage/sis-earth-observation/src/main/java/org/apache/sis/storage/earthobservation/LandsatStore.java
index 574b940..c23a061 100644
--- a/storage/sis-earth-observation/src/main/java/org/apache/sis/storage/earthobservation/LandsatStore.java
+++ b/storage/sis-earth-observation/src/main/java/org/apache/sis/storage/earthobservation/LandsatStore.java
@@ -31,8 +31,8 @@ import org.apache.sis.storage.DataStoreException;
import org.apache.sis.storage.DataStoreReferencingException;
import org.apache.sis.storage.UnsupportedStorageException;
import org.apache.sis.storage.StorageConnector;
-import org.apache.sis.storage.event.ChangeEvent;
-import org.apache.sis.storage.event.ChangeListener;
+import org.apache.sis.storage.event.StoreEvent;
+import org.apache.sis.storage.event.StoreListener;
import org.apache.sis.internal.storage.URIDataStore;
import org.apache.sis.setup.OptionKey;
@@ -178,7 +178,7 @@ public class LandsatStore extends DataStore {
* @param eventType {@inheritDoc}
*/
@Override
- public <T extends ChangeEvent> void addListener(ChangeListener<? super T> listener, Class<T> eventType) {
+ public <T extends StoreEvent> void addListener(StoreListener<? super T> listener, Class<T> eventType) {
}
/**
@@ -189,7 +189,7 @@ public class LandsatStore extends DataStore {
* @param eventType {@inheritDoc}
*/
@Override
- public <T extends ChangeEvent> void removeListener(ChangeListener<? super T> listener, Class<T> eventType) {
+ public <T extends StoreEvent> void removeListener(StoreListener<? super T> listener, Class<T> eventType) {
}
/**
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 b476d01..065dbdb 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
@@ -42,8 +42,8 @@ import org.apache.sis.storage.DataStoreContentException;
import org.apache.sis.storage.UnsupportedStorageException;
import org.apache.sis.storage.DataStoreClosedException;
import org.apache.sis.storage.IllegalNameException;
-import org.apache.sis.storage.event.ChangeEvent;
-import org.apache.sis.storage.event.ChangeListener;
+import org.apache.sis.storage.event.StoreEvent;
+import org.apache.sis.storage.event.StoreListener;
import org.apache.sis.internal.storage.io.ChannelDataInput;
import org.apache.sis.internal.storage.io.IOUtilities;
import org.apache.sis.internal.storage.MetadataBuilder;
@@ -340,7 +340,7 @@ public class GeoTiffStore extends DataStore implements Aggregate {
* @param eventType {@inheritDoc}
*/
@Override
- public <T extends ChangeEvent> void addListener(ChangeListener<? super T> listener, Class<T> eventType) {
+ public <T extends StoreEvent> void addListener(StoreListener<? super T> listener, Class<T> eventType) {
}
/**
@@ -351,7 +351,7 @@ public class GeoTiffStore extends DataStore implements Aggregate {
* @param eventType {@inheritDoc}
*/
@Override
- public <T extends ChangeEvent> void removeListener(ChangeListener<? super T> listener, Class<T> eventType) {
+ public <T extends StoreEvent> void removeListener(StoreListener<? super T> listener, Class<T> eventType) {
}
/**
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/NetcdfStore.java b/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/NetcdfStore.java
index 653bdf8..0e33f37 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/NetcdfStore.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/NetcdfStore.java
@@ -39,8 +39,8 @@ import org.apache.sis.internal.util.UnmodifiableArrayList;
import org.apache.sis.internal.util.Strings;
import org.apache.sis.setup.OptionKey;
import org.apache.sis.storage.Resource;
-import org.apache.sis.storage.event.ChangeEvent;
-import org.apache.sis.storage.event.ChangeListener;
+import org.apache.sis.storage.event.StoreEvent;
+import org.apache.sis.storage.event.StoreListener;
import org.apache.sis.util.CharSequences;
import org.apache.sis.util.Version;
import ucar.nc2.constants.ACDD;
@@ -222,7 +222,7 @@ public class NetcdfStore extends DataStore implements Aggregate {
* @param eventType {@inheritDoc}
*/
@Override
- public <T extends ChangeEvent> void addListener(ChangeListener<? super T> listener, Class<T> eventType) {
+ public <T extends StoreEvent> void addListener(StoreListener<? super T> listener, Class<T> eventType) {
}
/**
@@ -233,7 +233,7 @@ public class NetcdfStore extends DataStore implements Aggregate {
* @param eventType {@inheritDoc}
*/
@Override
- public <T extends ChangeEvent> void removeListener(ChangeListener<? super T> listener, Class<T> eventType) {
+ public <T extends StoreEvent> void removeListener(StoreListener<? super T> listener, Class<T> eventType) {
}
/**
diff --git a/storage/sis-sqlstore/src/main/java/org/apache/sis/storage/sql/SQLStore.java b/storage/sis-sqlstore/src/main/java/org/apache/sis/storage/sql/SQLStore.java
index b7b1061..0d83fd4 100644
--- a/storage/sis-sqlstore/src/main/java/org/apache/sis/storage/sql/SQLStore.java
+++ b/storage/sis-sqlstore/src/main/java/org/apache/sis/storage/sql/SQLStore.java
@@ -32,8 +32,8 @@ import org.apache.sis.storage.DataStore;
import org.apache.sis.storage.DataStoreException;
import org.apache.sis.storage.IllegalNameException;
import org.apache.sis.storage.StorageConnector;
-import org.apache.sis.storage.event.ChangeEvent;
-import org.apache.sis.storage.event.ChangeListener;
+import org.apache.sis.storage.event.StoreEvent;
+import org.apache.sis.storage.event.StoreListener;
import org.apache.sis.internal.sql.feature.Database;
import org.apache.sis.internal.sql.feature.Resources;
import org.apache.sis.internal.storage.MetadataBuilder;
@@ -263,7 +263,7 @@ public class SQLStore extends DataStore implements Aggregate {
* @param eventType {@inheritDoc}
*/
@Override
- public <T extends ChangeEvent> void addListener(ChangeListener<? super T> listener, Class<T> eventType) {
+ public <T extends StoreEvent> void addListener(StoreListener<? super T> listener, Class<T> eventType) {
}
/**
@@ -274,7 +274,7 @@ public class SQLStore extends DataStore implements Aggregate {
* @param eventType {@inheritDoc}
*/
@Override
- public <T extends ChangeEvent> void removeListener(ChangeListener<? super T> listener, Class<T> eventType) {
+ public <T extends StoreEvent> void removeListener(StoreListener<? super T> listener, Class<T> eventType) {
}
/**
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 f279e9d..d3ca538 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
@@ -26,8 +26,8 @@ import org.apache.sis.util.logging.WarningListeners;
import org.apache.sis.storage.Resource;
import org.apache.sis.storage.DataStore;
import org.apache.sis.storage.DataStoreException;
-import org.apache.sis.storage.event.ChangeEvent;
-import org.apache.sis.storage.event.ChangeListener;
+import org.apache.sis.storage.event.StoreEvent;
+import org.apache.sis.storage.event.StoreListener;
/**
@@ -177,7 +177,7 @@ public abstract class AbstractResource implements Resource, Localized {
* @param eventType {@inheritDoc}
*/
@Override
- public <T extends ChangeEvent> void addListener(ChangeListener<? super T> listener, Class<T> eventType) {
+ public <T extends StoreEvent> void addListener(StoreListener<? super T> listener, Class<T> eventType) {
}
/**
@@ -188,6 +188,6 @@ public abstract class AbstractResource implements Resource, Localized {
* @param eventType {@inheritDoc}
*/
@Override
- public <T extends ChangeEvent> void removeListener(ChangeListener<? super T> listener, Class<T> eventType) {
+ public <T extends StoreEvent> void removeListener(StoreListener<? super T> listener, Class<T> eventType) {
}
}
diff --git a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/URIDataStore.java b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/URIDataStore.java
index 5137fbf..25f179e 100644
--- a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/URIDataStore.java
+++ b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/URIDataStore.java
@@ -31,8 +31,8 @@ import org.apache.sis.storage.DataStore;
import org.apache.sis.storage.DataStoreProvider;
import org.apache.sis.storage.DataStoreException;
import org.apache.sis.storage.IllegalOpenParameterException;
-import org.apache.sis.storage.event.ChangeEvent;
-import org.apache.sis.storage.event.ChangeListener;
+import org.apache.sis.storage.event.StoreEvent;
+import org.apache.sis.storage.event.StoreListener;
import org.apache.sis.internal.storage.io.IOUtilities;
@@ -276,7 +276,7 @@ public abstract class URIDataStore extends DataStore implements StoreResource, R
* @param eventType {@inheritDoc}
*/
@Override
- public <T extends ChangeEvent> void addListener(ChangeListener<? super T> listener, Class<T> eventType) {
+ public <T extends StoreEvent> void addListener(StoreListener<? super T> listener, Class<T> eventType) {
}
/**
@@ -288,6 +288,6 @@ public abstract class URIDataStore extends DataStore implements StoreResource, R
* @param eventType {@inheritDoc}
*/
@Override
- public <T extends ChangeEvent> void removeListener(ChangeListener<? super T> listener, Class<T> eventType) {
+ public <T extends StoreEvent> void removeListener(StoreListener<? super T> listener, Class<T> eventType) {
}
}
diff --git a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/Store.java b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/Store.java
index 3712d60..6b46c89 100644
--- a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/Store.java
+++ b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/Store.java
@@ -55,8 +55,8 @@ import org.apache.sis.internal.storage.MetadataBuilder;
import org.apache.sis.internal.storage.StoreUtilities;
import org.apache.sis.internal.storage.StoreResource;
import org.apache.sis.internal.storage.Resources;
-import org.apache.sis.storage.event.ChangeEvent;
-import org.apache.sis.storage.event.ChangeListener;
+import org.apache.sis.storage.event.StoreEvent;
+import org.apache.sis.storage.event.StoreListener;
/**
@@ -410,7 +410,7 @@ class Store extends DataStore implements StoreResource, Aggregate, DirectoryStre
* @param eventType {@inheritDoc}
*/
@Override
- public <T extends ChangeEvent> void addListener(ChangeListener<? super T> listener, Class<T> eventType) {
+ public <T extends StoreEvent> void addListener(StoreListener<? super T> listener, Class<T> eventType) {
}
/**
@@ -421,7 +421,7 @@ class Store extends DataStore implements StoreResource, Aggregate, DirectoryStre
* @param eventType {@inheritDoc}
*/
@Override
- public <T extends ChangeEvent> void removeListener(ChangeListener<? super T> listener, Class<T> eventType) {
+ public <T extends StoreEvent> void removeListener(StoreListener<? super T> listener, Class<T> eventType) {
}
/**
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 e8f2eed..9996ed4 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
@@ -19,8 +19,8 @@ package org.apache.sis.storage;
import java.util.Optional;
import org.opengis.util.GenericName;
import org.opengis.metadata.Metadata;
-import org.apache.sis.storage.event.ChangeEvent;
-import org.apache.sis.storage.event.ChangeListener;
+import org.apache.sis.storage.event.StoreEvent;
+import org.apache.sis.storage.event.StoreListener;
/**
@@ -134,12 +134,12 @@ public interface Resource {
Metadata getMetadata() throws DataStoreException;
/**
- * Registers a listener that is notified each time a change occurs in the resource content or structure.
- * The resource will call the {@link ChangeListener#changeOccured(ChangeEvent)}
+ * Registers a listener that is notified when some kind of events occur in the resource content or structure.
+ * The resource will call the {@link StoreListener#eventOccured(StoreEvent)}
* method when a new event matching the {@code eventType} is produced.
*
* <p>Registering a listener for a given {@code eventType} also register the listener for all sub-types.
- * The same listener can be added multiple times for different even type.
+ * The same listener can be added multiple times for different even types.
* Adding many times the same listener with the same even type has no effect:
* the listener will only be called once per event.</p>
*
@@ -150,26 +150,26 @@ public interface Resource {
* For example the resource may discard a listener if no event of the given type happen on this resource.</p>
*
* @param <T> compile-time value of the {@code eventType} argument.
- * @param listener listener to notify about changes.
- * @param eventType type of {@linkplain ChangeEvent} to listen (can not be {@code null}).
+ * @param listener listener to notify about events.
+ * @param eventType type of {@link StoreEvent} to listen (can not be {@code null}).
*/
- <T extends ChangeEvent> void addListener(ChangeListener<? super T> listener, Class<T> eventType);
+ <T extends StoreEvent> void addListener(StoreListener<? super T> listener, Class<T> eventType);
/**
* Unregisters a listener previously added to this resource for the given type of events.
* The {@code eventType} must be the exact same class than the one given to the {@code addListener(…)} method.
*
* <div class="note"><b>Example:</b>
- * if the same listener has been added for {@code ChangeEvent} and {@code StructuralChangeEvent}, that listener
- * will be notified only once for all {@code ChangeEvent}s. If that listener is removed for {@code ChangeEvent},
+ * if the same listener has been added for {@code StoreEvent} and {@code StructuralChangeEvent}, that listener
+ * will be notified only once for all {@code StoreEvent}s. If that listener is removed for {@code StoreEvent},
* then the listener will still receive {@code StructuralChangeEvent}s.</div>
*
* <p>Calling multiple times this method with the same listener and event type or a listener
* which is unknown to this resource will have no effect and will not raise an exception.</p>
*
* @param <T> compile-time value of the {@code eventType} argument.
- * @param listener listener to stop notifying about changes.
- * @param eventType type of {@linkplain ChangeEvent} which were listened (can not be {@code null}).
+ * @param listener listener to stop notifying about events.
+ * @param eventType type of {@link StoreEvent} which were listened (can not be {@code null}).
*/
- <T extends ChangeEvent> void removeListener(ChangeListener<? super T> listener, Class<T> eventType);
+ <T extends StoreEvent> void removeListener(StoreListener<? super T> listener, Class<T> eventType);
}
diff --git a/storage/sis-storage/src/main/java/org/apache/sis/storage/event/ChangeEvent.java b/storage/sis-storage/src/main/java/org/apache/sis/storage/event/StoreEvent.java
similarity index 86%
rename from storage/sis-storage/src/main/java/org/apache/sis/storage/event/ChangeEvent.java
rename to storage/sis-storage/src/main/java/org/apache/sis/storage/event/StoreEvent.java
index 6e2545a..8462e9d 100644
--- a/storage/sis-storage/src/main/java/org/apache/sis/storage/event/ChangeEvent.java
+++ b/storage/sis-storage/src/main/java/org/apache/sis/storage/event/StoreEvent.java
@@ -21,18 +21,19 @@ import org.apache.sis.storage.Resource;
/**
- * Parent class of all events related to a change in the metadata, content or structure of a resource.
+ * Parent class of events happening in a data store resource.
+ * The event may be a warning or a change in the metadata, content or structure of a resource.
* Those events are created by {@link Resource} implementations and sent to all registered listeners.
*
* @author Johann Sorel (Geomatys)
* @version 1.0
*
- * @see ChangeListener
+ * @see StoreListener
*
* @since 1.0
* @module
*/
-public abstract class ChangeEvent extends EventObject {
+public abstract class StoreEvent extends EventObject {
/**
* For cross-version compatibility.
*/
@@ -44,7 +45,7 @@ public abstract class ChangeEvent extends EventObject {
* @param source the resource on which the event initially occurred.
* @throws IllegalArgumentException if the given source is null.
*/
- public ChangeEvent(Resource source) {
+ public StoreEvent(Resource source) {
super(source);
}
diff --git a/storage/sis-storage/src/main/java/org/apache/sis/storage/event/ChangeListener.java b/storage/sis-storage/src/main/java/org/apache/sis/storage/event/StoreListener.java
similarity index 68%
rename from storage/sis-storage/src/main/java/org/apache/sis/storage/event/ChangeListener.java
rename to storage/sis-storage/src/main/java/org/apache/sis/storage/event/StoreListener.java
index a5892ab..4ba393a 100644
--- a/storage/sis-storage/src/main/java/org/apache/sis/storage/event/ChangeListener.java
+++ b/storage/sis-storage/src/main/java/org/apache/sis/storage/event/StoreListener.java
@@ -20,35 +20,35 @@ import org.apache.sis.storage.Resource;
/**
- * Defines an object which listens for changes in resources.
- * The changes in resources are described by {@link ChangeEvent} instances.
- * {@link Resource} implementations are responsible for instantiating the most specific {@code ChangeEvent} subclass
+ * Defines an object which listens for events in resources (changes or warnings).
+ * The events in resources are described by {@link StoreEvent} instances.
+ * {@link Resource} implementations are responsible for instantiating the most specific {@code StoreEvent} subclass
* for the type of event, for example:
*
* <ul>
+ * <li>When a warning occurred.</li>
* <li>When the data store content changed (e.g. new feature instance added or removed).</li>
* <li>When the data store structure changed (e.g. a column is added in tabular data).</li>
* <li>Any other change at implementation choice.</li>
* </ul>
*
- * Then, all {@code ChangeListener}s that declared an interest in {@code ChangeEvent}s of that kind are notified.
+ * Then, all {@code StoreListener}s that declared an interest in {@code StoreEvent}s of that kind are notified.
*
* @author Johann Sorel (Geomatys)
* @version 1.0
*
* @param <T> the type of events of interest to this listener.
*
- * @see ChangeEvent
- * @see org.apache.sis.util.logging.WarningListener
+ * @see StoreEvent
*
* @since 1.0
* @module
*/
-public interface ChangeListener<T extends ChangeEvent> {
+public interface StoreListener<T extends StoreEvent> {
/**
- * Invoked <em>after</em> a change occurred in a resource.
+ * Invoked <em>after</em> a warning or a change occurred in a resource.
*
- * @param event resource event, never null.
+ * @param event description of the change or warning that occurred in a resource. Shall not be {@code null}.
*/
- void changeOccured(T event);
+ void eventOccured(T event);
}
diff --git a/storage/sis-storage/src/main/java/org/apache/sis/storage/event/package-info.java b/storage/sis-storage/src/main/java/org/apache/sis/storage/event/package-info.java
index e2f3744..4525209 100644
--- a/storage/sis-storage/src/main/java/org/apache/sis/storage/event/package-info.java
+++ b/storage/sis-storage/src/main/java/org/apache/sis/storage/event/package-info.java
@@ -18,13 +18,10 @@
/**
* Provides interfaces and classes for dealing with different types of events fired by resources.
- * The different types of events are differentiated by the {@link ChangeEvent} subclasses.
- * There is different subclasses for structural changes or changes in resource content.
+ * The different types of events are differentiated by the {@link StoreEvent} subclasses.
+ * There is different subclasses for warnings, structural changes or changes in resource content.
* It is possible to register a listener for only some specific types of events.
*
- * <p>Note that warnings that may occur during exploitation of a resource are handled by an interface
- * defined in another package, {@link org.apache.sis.util.logging.WarningListener}.</p>
- *
* @author Johann Sorel (Geomatys)
* @since 1.0
* @version 1.0
diff --git a/storage/sis-storage/src/test/java/org/apache/sis/storage/DataStoreMock.java b/storage/sis-storage/src/test/java/org/apache/sis/storage/DataStoreMock.java
index 77cf42f..16b1f60 100644
--- a/storage/sis-storage/src/test/java/org/apache/sis/storage/DataStoreMock.java
+++ b/storage/sis-storage/src/test/java/org/apache/sis/storage/DataStoreMock.java
@@ -18,8 +18,8 @@ package org.apache.sis.storage;
import org.opengis.metadata.Metadata;
import org.opengis.parameter.ParameterValueGroup;
-import org.apache.sis.storage.event.ChangeEvent;
-import org.apache.sis.storage.event.ChangeListener;
+import org.apache.sis.storage.event.StoreEvent;
+import org.apache.sis.storage.event.StoreListener;
/**
@@ -62,11 +62,11 @@ final strictfp class DataStoreMock extends DataStore {
}
@Override
- public <T extends ChangeEvent> void addListener(ChangeListener<? super T> listener, Class<T> eventType) {
+ public <T extends StoreEvent> void addListener(StoreListener<? super T> listener, Class<T> eventType) {
}
@Override
- public <T extends ChangeEvent> void removeListener(ChangeListener<? super T> listener, Class<T> eventType) {
+ public <T extends StoreEvent> void removeListener(StoreListener<? super T> listener, Class<T> eventType) {
}
@Override
[sis] 05/05: Prepare replacement of WarningListeners by
StoreListeners. https://issues.apache.org/jira/browse/SIS-421
Posted by de...@apache.org.
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 4f46eebc12787609d3e7354c02ff73a6a8f2af47
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Fri Sep 6 20:11:07 2019 +0200
Prepare replacement of WarningListeners by StoreListeners.
https://issues.apache.org/jira/browse/SIS-421
---
.../apache/sis/util/logging/QuietLogRecord.java | 3 +
.../apache/sis/util/logging/WarningListeners.java | 7 +-
.../java/org/apache/sis/storage/DataStore.java | 8 +
.../org/apache/sis/storage/DataStoreProvider.java | 22 +
.../main/java/org/apache/sis/storage/Resource.java | 46 +-
.../apache/sis/storage/event}/QuietLogRecord.java | 14 +-
.../org/apache/sis/storage/event/StoreEvent.java | 50 +-
.../apache/sis/storage/event/StoreListener.java | 28 +-
.../apache/sis/storage/event/StoreListeners.java | 607 +++++++++++++++++++++
.../org/apache/sis/storage/event/WarningEvent.java | 90 +++
.../org/apache/sis/storage/event/package-info.java | 17 +-
11 files changed, 842 insertions(+), 50 deletions(-)
diff --git a/core/sis-utility/src/main/java/org/apache/sis/util/logging/QuietLogRecord.java b/core/sis-utility/src/main/java/org/apache/sis/util/logging/QuietLogRecord.java
index 1b225fe..d10ff94 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/util/logging/QuietLogRecord.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/util/logging/QuietLogRecord.java
@@ -27,7 +27,10 @@ import java.util.logging.LogRecord;
* @version 0.8
* @since 0.3
* @module
+ *
+ * @deprecated Moved to {@link org.apache.sis.storage.event.QuietLogRecord}.
*/
+@Deprecated
final class QuietLogRecord extends LogRecord {
/**
* For cross-version compatibility.
diff --git a/core/sis-utility/src/main/java/org/apache/sis/util/logging/WarningListeners.java b/core/sis-utility/src/main/java/org/apache/sis/util/logging/WarningListeners.java
index ac8709b..d7790cb 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/util/logging/WarningListeners.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/util/logging/WarningListeners.java
@@ -67,7 +67,10 @@ import org.apache.sis.internal.util.UnmodifiableArrayList;
*
* @since 0.3
* @module
+ *
+ * @deprecated Replaced by {@link org.apache.sis.storage.event.StoreListeners}.
*/
+@Deprecated
public class WarningListeners<S> implements Localized {
/**
* The declared source of warnings. This is not necessarily the real source,
@@ -176,12 +179,12 @@ public class WarningListeners<S> implements Localized {
* @param record the warning as a log record.
*/
public void warning(final LogRecord record) {
- final WarningListener<?>[] current;
+ final WarningListener<? super S>[] current;
synchronized (this) {
current = listeners;
}
if (current != null) {
- for (final WarningListener<? super S> listener : listeners) {
+ for (final WarningListener<? super S> listener : current) {
listener.warningOccured(source, record);
}
} else {
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 07d371a..3b4577e 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
@@ -225,6 +225,8 @@ public abstract class DataStore implements Resource, Localized, AutoCloseable {
* 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>
+ *
+ * @see org.apache.sis.storage.event.StoreEvent#getLocale()
*/
@Override
public synchronized Locale getLocale() {
@@ -444,7 +446,10 @@ public abstract class DataStore implements Resource, Localized, AutoCloseable {
*
* @param listener the listener to add.
* @throws IllegalArgumentException if the given listener is already registered in this data store.
+ *
+ * @deprecated Replaced by {@code addListener(listener, WarningEvent.class)}.
*/
+ @Deprecated
public void addWarningListener(final WarningListener<? super DataStore> listener)
throws IllegalArgumentException
{
@@ -456,7 +461,10 @@ public abstract class DataStore implements Resource, Localized, AutoCloseable {
*
* @param listener the listener to remove.
* @throws NoSuchElementException if the given listener is not registered in this data store.
+ *
+ * @deprecated Replaced by {@code removeListener(listener, WarningEvent.class)}.
*/
+ @Deprecated
public void removeWarningListener(final WarningListener<? super DataStore> listener)
throws NoSuchElementException
{
diff --git a/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreProvider.java b/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreProvider.java
index da71aba..09a7e4a 100644
--- a/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreProvider.java
+++ b/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreProvider.java
@@ -16,6 +16,7 @@
*/
package org.apache.sis.storage;
+import java.util.logging.Logger;
import org.opengis.parameter.ParameterValueGroup;
import org.opengis.parameter.ParameterDescriptorGroup;
import org.opengis.metadata.distribution.Format;
@@ -24,6 +25,7 @@ import org.apache.sis.internal.storage.URIDataStore;
import org.apache.sis.metadata.iso.citation.DefaultCitation;
import org.apache.sis.metadata.iso.distribution.DefaultFormat;
import org.apache.sis.measure.Range;
+import org.apache.sis.util.logging.Logging;
import org.apache.sis.util.ArgumentChecks;
import org.apache.sis.util.Version;
@@ -322,4 +324,24 @@ public abstract class DataStoreProvider {
ArgumentChecks.ensureNonNull("parameter", parameters);
return open(URIDataStore.Provider.connector(this, parameters));
}
+
+ /**
+ * Returns the logger where to report warnings. This logger is used only if no
+ * {@link org.apache.sis.storage.event.StoreListener} has been registered for
+ * {@link org.apache.sis.storage.event.WarningEvent}.
+ *
+ * <p>The default implementation returns a logger with the same name as the package name
+ * of the subclass of this {@code DataStoreProvider} instance. Subclasses should override
+ * this method if they can provide a more specific logger.</p>
+ *
+ * @return the logger to use as a fallback (when there is no listeners) for warning messages.
+ *
+ * @since 1.0
+ */
+ public Logger getLogger() {
+ String name = getClass().getName();
+ final int separator = name.lastIndexOf('.');
+ name = (separator >= 1) ? name.substring(0, separator) : "";
+ return Logging.getLogger(name);
+ }
}
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 9996ed4..85a80f5 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
@@ -134,20 +134,26 @@ public interface Resource {
Metadata getMetadata() throws DataStoreException;
/**
- * Registers a listener that is notified when some kind of events occur in the resource content or structure.
- * The resource will call the {@link StoreListener#eventOccured(StoreEvent)}
- * method when a new event matching the {@code eventType} is produced.
+ * Registers a listener to notify when the specified kind of event occurs in this resource or in children.
+ * The resource will call the {@link StoreListener#eventOccured(StoreEvent)} method when new events matching
+ * the {@code eventType} occur. An event may be a change in resource content or structure, or a warning that
+ * occurred during a read or write operation.
*
- * <p>Registering a listener for a given {@code eventType} also register the listener for all sub-types.
- * The same listener can be added multiple times for different even types.
- * Adding many times the same listener with the same even type has no effect:
- * the listener will only be called once per event.</p>
+ * <p>Registering a listener for a given {@code eventType} also register the listener for all event sub-types.
+ * The same listener can be registered many times, but its {@link StoreListener#eventOccured(StoreEvent)}
+ * method will be invoked only once per event. This filtering applies even if the listener is registered
+ * on different resources in the same tree, for example a parent and its children.</p>
*
- * @todo When adding a listener to an aggregate, should the listener be added to all components?
- * In other words, should listeners in a tree node also listen to events from all children?
+ * <p>If this resource may produce events of the given type, then the given listener is kept by strong reference;
+ * it will not be garbage collected unless {@linkplain #removeListener(StoreListener, Class) explicitly removed}
+ * or unless this {@code Resource} is itself garbage collected. However if the given type of events can never
+ * happen with this resource, then this method is not required to keep a reference to the given listener.</p>
*
- * <p>The resource is not required to keep a reference to the listener.
- * For example the resource may discard a listener if no event of the given type happen on this resource.</p>
+ * <div class="section">Warning events</div>
+ * If {@code eventType} is assignable from <code>{@linkplain org.apache.sis.storage.event.WarningEvent}.class</code>,
+ * then registering that listener turns off logging of warning messages for this resource.
+ * This side-effect is applied on the assumption that the registered listener will handle
+ * warnings in its own way, for example by showing warnings in a widget.
*
* @param <T> compile-time value of the {@code eventType} argument.
* @param listener listener to notify about events.
@@ -157,15 +163,19 @@ public interface Resource {
/**
* Unregisters a listener previously added to this resource for the given type of events.
- * The {@code eventType} must be the exact same class than the one given to the {@code addListener(…)} method.
+ * The {@code eventType} must be the exact same class than the one given to the {@code addListener(…)} method;
+ * this method does not remove listeners registered for subclasses and does not remove listeners registered in
+ * parent resources.
*
- * <div class="note"><b>Example:</b>
- * if the same listener has been added for {@code StoreEvent} and {@code StructuralChangeEvent}, that listener
- * will be notified only once for all {@code StoreEvent}s. If that listener is removed for {@code StoreEvent},
- * then the listener will still receive {@code StructuralChangeEvent}s.</div>
+ * <p>If the same listener has been registered many times for the same even type, then this method removes only
+ * the most recent registration. In other words if {@code addListener(ls, type)} has been invoked twice, then
+ * {@code removeListener(ls, type)} needs to be invoked twice in order to remove all instances of that listener.
+ * If the given listener is not found, then this method does nothing (no exception is thrown).</p>
*
- * <p>Calling multiple times this method with the same listener and event type or a listener
- * which is unknown to this resource will have no effect and will not raise an exception.</p>
+ * <div class="section">Warning events</div>
+ * If {@code eventType} is <code>{@linkplain org.apache.sis.storage.event.WarningEvent}.class</code>
+ * and if, after this method invocation, there is no remaining listener for warning events,
+ * then this {@code Resource} will send future warnings to the loggers.
*
* @param <T> compile-time value of the {@code eventType} argument.
* @param listener listener to stop notifying about events.
diff --git a/core/sis-utility/src/main/java/org/apache/sis/util/logging/QuietLogRecord.java b/storage/sis-storage/src/main/java/org/apache/sis/storage/event/QuietLogRecord.java
similarity index 84%
copy from core/sis-utility/src/main/java/org/apache/sis/util/logging/QuietLogRecord.java
copy to storage/sis-storage/src/main/java/org/apache/sis/storage/event/QuietLogRecord.java
index 1b225fe..06a3496 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/util/logging/QuietLogRecord.java
+++ b/storage/sis-storage/src/main/java/org/apache/sis/storage/event/QuietLogRecord.java
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package org.apache.sis.util.logging;
+package org.apache.sis.storage.event;
import java.util.logging.Level;
import java.util.logging.LogRecord;
@@ -24,7 +24,7 @@ import java.util.logging.LogRecord;
* A log record to be logged without stack trace, unless the user specified it explicitly.
*
* @author Martin Desruisseaux (Geomatys)
- * @version 0.8
+ * @version 1.0
* @since 0.3
* @module
*/
@@ -32,11 +32,11 @@ final class QuietLogRecord extends LogRecord {
/**
* For cross-version compatibility.
*/
- private static final long serialVersionUID = -8225936118310305206L;
+ private static final long serialVersionUID = 5652099235767670922L;
/**
- * {@code true} if the user invoked {@link #setThrown(Throwable)}.
- * In such case, {@link #clearThrown()} will not reset the throwable to null.
+ * {@code true} if the user invoked {@link #setThrown(Throwable)} explicitly.
+ * In such case, {@link #clearImplicitThrown()} will not reset the throwable to null.
*/
private boolean explicitThrown;
@@ -59,10 +59,10 @@ final class QuietLogRecord extends LogRecord {
}
/**
- * Clears the throwable if it has not been explicit set by the user.
+ * Clears the throwable if it has not been explicitly set by the user.
* Otherwise do nothing.
*/
- void clearThrown() {
+ void clearImplicitThrown() {
if (!explicitThrown) {
super.setThrown(null);
}
diff --git a/storage/sis-storage/src/main/java/org/apache/sis/storage/event/StoreEvent.java b/storage/sis-storage/src/main/java/org/apache/sis/storage/event/StoreEvent.java
index 8462e9d..3360ef9 100644
--- a/storage/sis-storage/src/main/java/org/apache/sis/storage/event/StoreEvent.java
+++ b/storage/sis-storage/src/main/java/org/apache/sis/storage/event/StoreEvent.java
@@ -16,8 +16,12 @@
*/
package org.apache.sis.storage.event;
+import java.util.Locale;
import java.util.EventObject;
+import org.apache.sis.util.Localized;
import org.apache.sis.storage.Resource;
+import org.apache.sis.storage.DataStore;
+import org.apache.sis.internal.storage.StoreResource;
/**
@@ -33,7 +37,7 @@ import org.apache.sis.storage.Resource;
* @since 1.0
* @module
*/
-public abstract class StoreEvent extends EventObject {
+public abstract class StoreEvent extends EventObject implements Localized {
/**
* For cross-version compatibility.
*/
@@ -42,20 +46,52 @@ public abstract class StoreEvent extends EventObject {
/**
* Constructs an event that occurred in the given resource.
*
- * @param source the resource on which the event initially occurred.
- * @throws IllegalArgumentException if the given source is null.
+ * @param source the resource where the event occurred.
+ * @throws IllegalArgumentException if the given source is null.
*/
- public StoreEvent(Resource source) {
+ protected StoreEvent(Resource source) {
super(source);
}
/**
- * Returns the resource on which the event initially occurred.
+ * Returns the resource where the event occurred. It is not necessarily the {@linkplain Resource#addListener
+ * resource in which listeners have been registered}; it may be one of the resource children.
*
- * @return the resource on which the Event initially occurred.
+ * @return the resource where the event occurred.
*/
@Override
public Resource getSource() {
- return (Resource) source;
+ return (Resource) super.getSource();
+ }
+
+ /**
+ * Returns the locale associated to this event, or {@code null} if unspecified.
+ * That locale may be used for formatting messages related to this event.
+ * The event locale is typically inherited from the {@link DataStore} locale.
+ *
+ * @return the locale associated to this event (typically specified by the data store),
+ * or {@code null} if unknown.
+ *
+ * @see DataStore#getLocale()
+ */
+ @Override
+ public Locale getLocale() {
+ return getLocale(source);
+ }
+
+ /**
+ * {@link #getLocale()} implementation shared with {@link StoreListeners#getLocale()}.
+ */
+ static Locale getLocale(final Object source) {
+ if (source instanceof Localized) {
+ return ((Localized) source).getLocale();
+ }
+ if (source instanceof StoreResource) {
+ final DataStore ds = ((StoreResource) source).getOriginator();
+ if (ds != null) {
+ return ds.getLocale();
+ }
+ }
+ return null;
}
}
diff --git a/storage/sis-storage/src/main/java/org/apache/sis/storage/event/StoreListener.java b/storage/sis-storage/src/main/java/org/apache/sis/storage/event/StoreListener.java
index 4ba393a..7d49d22 100644
--- a/storage/sis-storage/src/main/java/org/apache/sis/storage/event/StoreListener.java
+++ b/storage/sis-storage/src/main/java/org/apache/sis/storage/event/StoreListener.java
@@ -16,23 +16,21 @@
*/
package org.apache.sis.storage.event;
+import java.util.EventListener;
import org.apache.sis.storage.Resource;
/**
- * Defines an object which listens for events in resources (changes or warnings).
- * The events in resources are described by {@link StoreEvent} instances.
- * {@link Resource} implementations are responsible for instantiating the most specific {@code StoreEvent} subclass
- * for the type of event, for example:
+ * An object which listens for events (typically changes or warnings) occurring in a resource
+ * or one of its children. The kind of event is defined by the subclass of the {@link StoreEvent}
+ * instance given to the {@link #eventOccured(StoreEvent)} method. For example if a warning occurred
+ * while reading data from a file, then the event will be an instance of {@link WarningEvent}.
*
- * <ul>
- * <li>When a warning occurred.</li>
- * <li>When the data store content changed (e.g. new feature instance added or removed).</li>
- * <li>When the data store structure changed (e.g. a column is added in tabular data).</li>
- * <li>Any other change at implementation choice.</li>
- * </ul>
- *
- * Then, all {@code StoreListener}s that declared an interest in {@code StoreEvent}s of that kind are notified.
+ * <p>{@link Resource} implementations are responsible for instantiating the most specific
+ * {@code StoreEvent} subclass for the type of events. Then, all {@code StoreListener}s that
+ * {@linkplain Resource#addListener(StoreListener, Class) declared an interest} for
+ * {@code StoreEvent}s of that kind are notified, including listeners in parent resources.
+ * Each listener is notified only once per event even if the listener is registered twice.</p>
*
* @author Johann Sorel (Geomatys)
* @version 1.0
@@ -40,13 +38,17 @@ import org.apache.sis.storage.Resource;
* @param <T> the type of events of interest to this listener.
*
* @see StoreEvent
+ * @see Resource#addListener(StoreListener, Class)
*
* @since 1.0
* @module
*/
-public interface StoreListener<T extends StoreEvent> {
+public interface StoreListener<T extends StoreEvent> extends EventListener {
/**
* Invoked <em>after</em> a warning or a change occurred in a resource.
+ * The {@link StoreEvent#getSource()} method gives the resource where the event occurred.
+ * It is not necessarily the {@linkplain Resource#addListener resource in which this
+ * listener has been registered}; it may be one of the resource children.
*
* @param event description of the change or warning that occurred in a resource. Shall not be {@code null}.
*/
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
new file mode 100644
index 0000000..65e274b
--- /dev/null
+++ b/storage/sis-storage/src/main/java/org/apache/sis/storage/event/StoreListeners.java
@@ -0,0 +1,607 @@
+/*
+ * 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.storage.event;
+
+import java.util.Map;
+import java.util.Locale;
+import java.util.IdentityHashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.logging.LogRecord;
+import java.lang.reflect.Method;
+import org.apache.sis.util.ArraysExt;
+import org.apache.sis.util.Localized;
+import org.apache.sis.util.Exceptions;
+import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.util.logging.Logging;
+import org.apache.sis.util.logging.WarningListener;
+import org.apache.sis.internal.system.Modules;
+import org.apache.sis.internal.storage.StoreResource;
+import org.apache.sis.storage.DataStoreProvider;
+import org.apache.sis.storage.DataStore;
+import org.apache.sis.storage.Resource;
+
+
+/**
+ * Holds a list of {@link StoreListener} instances and provides convenience methods for sending events.
+ * This is a helper class for {@link DataStore} and {@link Resource} implementations.
+ *
+ * <p>Observers can {@linkplain #addListener add listeners} for being notified about events,
+ * and producers can invoke one of the {@code warning(…)} and other methods for emitting events.
+ *
+ * <div class="section">Warning events</div>
+ * All warnings are given to the listeners as {@link LogRecord} instances (this allows localizable messages
+ * and additional information like {@linkplain LogRecord#getThrown() stack trace}, timestamp, <i>etc.</i>).
+ * This {@code StoreListeners} class provides convenience methods like {@link #warning(String, Exception)},
+ * which build {@code LogRecord} from an exception or from a string. But all those {@code warning(…)} methods
+ * ultimately delegate to {@link #warning(LogRecord)}, thus providing a single point that subclasses can override.
+ * When a warning is emitted, the default behavior is:
+ *
+ * <ul>
+ * <li>Notify all listeners registered for {@link WarningEvent} type
+ * in this {@code StoreListeners} and in the parent managers.</li>
+ * <li>If previous step found no listener registered for {@code WarningEvent},
+ * then log the warning in the first logger found in following choices:
+ * <ol>
+ * <li>The logger specified by {@link LogRecord#getLoggerName()} if non-null.</li>
+ * <li>Otherwise the logger specified by {@link org.apache.sis.storage.DataStoreProvider#getLogger()}
+ * if the provider can be found.</li>
+ * <li>Otherwise a logger whose name is the source {@link DataStore} package name.</li>
+ * </ol>
+ * </li>
+ * </ul>
+ *
+ * <div class="section">Thread safety</div>
+ * The same {@code StoreListeners} instance can be safely used by many threads without synchronization
+ * on the part of the caller. Subclasses should make sure that any overridden methods remain safe to call
+ * from multiple threads.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.0
+ * @since 1.0
+ * @module
+ */
+public class StoreListeners extends org.apache.sis.util.logging.WarningListeners<Resource> implements Localized {
+ /**
+ * Parent manager to notify in addition to this manager.
+ */
+ private final StoreListeners parent;
+
+ /**
+ * The declared source of events. This is not necessarily the real source,
+ * but this is the source that the implementer wants to declare as public API.
+ */
+ private final Resource source;
+
+ /**
+ * The head of a chained list of listeners, or {@code null} if none.
+ * Each element in this chain contains all listeners for a given even type.
+ */
+ private volatile ForType<?> listeners;
+
+ /**
+ * All listeners for a given even type.
+ *
+ * @param <T> the type of events of interest to the listeners.
+ */
+ private static final class ForType<T extends StoreEvent> {
+ /**
+ * The types for which listeners have been registered.
+ */
+ final Class<T> type;
+
+ /**
+ * The listeners for the {@linkplain #type event type}, or {@code null} if none.
+ * This is a <cite>copy on write</cite> array: no elements are modified after an array has been created.
+ */
+ @SuppressWarnings("VolatileArrayField")
+ private volatile StoreListener<? super T>[] listeners;
+
+ /**
+ * Next element in the chain of listeners. Intentionally final; if we want to remove an element
+ * then we need to recreate all previous elements with new {@code next} values. We do that for
+ * avoiding the need to synchronize iterations over the elements.
+ */
+ final ForType<?> next;
+
+ /**
+ * Creates a new element in the chained list of listeners.
+ *
+ * @param type type of events of interest for listeners in this element.
+ * @param next the next element in the chained list, or {@code null} if none.
+ */
+ ForType(final Class<T> type, final ForType<?> next) {
+ this.type = type;
+ this.next = next;
+ }
+
+ /**
+ * Adds the given listener to the list of listeners for this type.
+ * This method does not check if the given listener was already registered;
+ * it a listener is registered twice, it will need to be removed twice.
+ *
+ * <p>It is caller responsibility to perform synchronization and to verify that the listener is non-null.</p>
+ */
+ final void add(final StoreListener<? super T> listener) {
+ final StoreListener<? super T>[] list = listeners;
+ final int length = (list != null) ? list.length : 0;
+ @SuppressWarnings({"unchecked", "rawtypes"}) // Generic array creation.
+ final StoreListener<? super T>[] copy = new StoreListener[length + 1];
+ if (list != null) {
+ System.arraycopy(list, 0, copy, 0, length);
+ }
+ copy[length] = listener;
+ listeners = copy;
+ }
+
+ /**
+ * Removes a previously registered listener.
+ * It the listener has been registered twice, only the most recent registration is removed.
+ *
+ * <p>It is caller responsibility to perform synchronization.</p>
+ */
+ final void remove(final StoreListener<? super T> listener) {
+ StoreListener<? super T>[] list = listeners;
+ if (list != null) {
+ for (int i=list.length; --i >= 0;) {
+ if (list[i] == listener) {
+ if (list.length == 1) {
+ list = null;
+ } else {
+ list = ArraysExt.remove(list, i, 1);
+ }
+ listeners = list;
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns {@code true} if this element has at least one listener.
+ */
+ final boolean hasListeners() {
+ return listeners != null;
+ }
+
+ /**
+ * Sends the given event to all listeners registered in this element.
+ *
+ * @param event the event to send to listeners.
+ * @param done listeners who were already notified, for avoiding to notify them twice.
+ * @return the {@code done} map, created when first needed.
+ */
+ final Map<StoreListener<?>,Boolean> eventOccured(final T event, Map<StoreListener<?>,Boolean> done) {
+ final StoreListener<? super T>[] list = listeners;
+ if (list != null) {
+ if (done == null) {
+ done = new IdentityHashMap<>(list.length);
+ }
+ for (final StoreListener<? super T> listener : list) {
+ if (done.put(listener, Boolean.TRUE) == null) {
+ listener.eventOccured(event);
+ }
+ }
+ }
+ return done;
+ }
+ }
+
+ /**
+ * Creates a new instance with the given parent and initially no listener.
+ * The parent is typically the listeners of the {@link DataStore} that created a resource.
+ *
+ * @param parent the manager to notify in addition to this manager, or {@code null} if none.
+ * @param source the source of events. Can not be null.
+ */
+ public StoreListeners(final StoreListeners parent, final Resource source) {
+ super(source);
+ ArgumentChecks.ensureNonNull("source", source);
+ this.source = source;
+ this.parent = parent;
+ }
+
+ /**
+ * Returns the source of events. This value is specified at construction time.
+ *
+ * @return the source of events.
+ */
+ @Override
+ public Resource getSource() {
+ return source;
+ }
+
+ /**
+ * Returns the data store of the source, or {@code null} if unknown.
+ */
+ private static DataStore getDataStore(StoreListeners m) {
+ do {
+ final Resource source = m.source;
+ if (source instanceof DataStore) {
+ return (DataStore) source;
+ }
+ if (source instanceof StoreResource) {
+ final DataStore ds = ((StoreResource) source).getOriginator();
+ if (ds != null) return ds;
+ }
+ m = m.parent;
+ } while (m != null);
+ return null;
+ }
+
+ /**
+ * Returns the locale used by this manager, or {@code null} if unspecified.
+ * That locale is typically inherited from the {@link DataStore} locale
+ * and can be used for formatting messages.
+ *
+ * @return the locale for messages (typically specified by the data store), or {@code null} if unknown.
+ *
+ * @see DataStore#getLocale()
+ * @see StoreEvent#getLocale()
+ */
+ @Override
+ public Locale getLocale() {
+ return StoreEvent.getLocale(source);
+ }
+
+ /**
+ * Returns the logger where to send warnings when no other destination is specified.
+ * This method tries to get the logger from {@link DataStoreProvider#getLogger()}.
+ * If that logger can not be found, then this method infers a logger name from the
+ * package name of the source data store. The returned logger is used when:
+ *
+ * <ul>
+ * <li>no listener has been {@linkplain #addListener registered} for the {@link WarningEvent} type, and</li>
+ * <li>the {@code LogRecord} does not {@linkplain LogRecord#getLoggerName() specify a logger}.</li>
+ * </ul>
+ *
+ * @return the logger where to send the warnings when there is no other destination.
+ */
+ private Logger logger() {
+ Resource src = source;
+ final DataStore ds = getDataStore(this);
+ if (ds != null) {
+ final DataStoreProvider provider = ds.getProvider();
+ if (provider != null) {
+ final Logger logger = provider.getLogger();
+ if (logger != null) {
+ return logger;
+ }
+ }
+ src = ds;
+ }
+ return Logging.getLogger(src.getClass());
+ }
+
+ /**
+ * Reports a warning described by the given message.
+ *
+ * <p>This method is a shortcut for <code>{@linkplain #warning(Level, String, Exception)
+ * warning}({@linkplain Level#WARNING}, message, null)</code>.
+ *
+ * @param message the warning message to report.
+ */
+ public void warning(final String message) {
+ ArgumentChecks.ensureNonNull("message", message);
+ warning(Level.WARNING, message, null);
+ }
+
+ /**
+ * Reports a warning described by the given exception.
+ * The exception stack trace will be omitted at logging time for avoiding to pollute console output
+ * (keeping in mind that this method should be invoked only for non-fatal warnings).
+ * See {@linkplain #warning(Level, String, Exception) below} for more explanation.
+ *
+ * <p>This method is a shortcut for <code>{@linkplain #warning(Level, String, Exception)
+ * warning}({@linkplain Level#WARNING}, null, exception)</code>.
+ *
+ * @param exception the exception to report.
+ */
+ public void warning(final Exception exception) {
+ ArgumentChecks.ensureNonNull("exception", exception);
+ warning(Level.WARNING, null, exception);
+ }
+
+ /**
+ * Reports a warning described by the given message and exception.
+ * At least one of {@code message} and {@code exception} arguments shall be non-null.
+ * If both are non-null, then the exception message will be concatenated after the given message.
+ * If the exception is non-null, its stack trace will be omitted at logging time for avoiding to
+ * pollute console output (keeping in mind that this method should be invoked only for non-fatal
+ * warnings). See {@linkplain #warning(Level, String, Exception) below} for more explanation.
+ *
+ * <p>This method is a shortcut for <code>{@linkplain #warning(Level, String, Exception)
+ * warning}({@linkplain Level#WARNING}, message, exception)</code>.
+ *
+ * @param message the warning message to report, or {@code null} if none.
+ * @param exception the exception to report, or {@code null} if none.
+ */
+ @Override
+ public void warning(String message, Exception exception) {
+ warning(Level.WARNING, message, exception);
+ }
+
+ /**
+ * Reports a warning at the given level represented by the given message and exception.
+ * At least one of {@code message} and {@code exception} arguments shall be non-null.
+ * If both are non-null, then the exception message will be concatenated after the given message.
+ *
+ * <div class="section">Stack trace omission</div>
+ * If there is no registered listener for the {@link WarningEvent} type, then the {@link #warning(LogRecord)}
+ * method will send the record to a logger but <em>without</em> the stack trace.
+ * This is done that way because stack traces consume lot of space in the logging files, while being considered
+ * implementation details in the context of {@code StoreListeners} (on the assumption that the logging message
+ * provides sufficient information). If the stack trace is desired, then users can either:
+ * <ul>
+ * <li>invoke {@code warning(LogRecord)} directly, or</li>
+ * <li>override {@code warning(LogRecord)} and invoke {@link LogRecord#setThrown(Throwable)} explicitly, or</li>
+ * <li>register a listener which will log the record itself.</li>
+ * </ul>
+ *
+ * @param level the warning level.
+ * @param message the message to log, or {@code null} if none.
+ * @param exception the exception to log, or {@code null} if none.
+ */
+ @Override
+ public void warning(final Level level, String message, final Exception exception) {
+ ArgumentChecks.ensureNonNull("level", level);
+ final LogRecord record;
+ final StackTraceElement[] trace;
+ if (exception != null) {
+ trace = exception.getStackTrace();
+ message = Exceptions.formatChainedMessages(getLocale(), message, exception);
+ if (message == null) {
+ message = exception.toString();
+ }
+ record = new QuietLogRecord(level, message, exception);
+ } else {
+ ArgumentChecks.ensureNonEmpty("message", message);
+ trace = Thread.currentThread().getStackTrace(); // TODO: on JDK9, use StackWalker instead.
+ record = new LogRecord(level, message);
+ }
+ try {
+ for (final StackTraceElement e : trace) {
+ if (setPublicSource(record, Class.forName(e.getClassName()), e.getMethodName())) {
+ break;
+ }
+ }
+ } catch (ClassNotFoundException | SecurityException e) {
+ Logging.ignorableException(Logging.getLogger(Modules.STORAGE), StoreListeners.class, "warning", e);
+ }
+ warning(record);
+ }
+
+ /**
+ * Eventually sets the class name and method name in the given record,
+ * and returns {@code true} if the method is public resource method.
+ *
+ * @param record the record where to set the source class/method name.
+ * @param type the source class. This method does nothing if the class is not a {@link Resource}.
+ * @param methodName the source method.
+ * @return whether the source is a public method of a {@link Resource}.
+ * @throws SecurityException if this method is not allowed to get the list of public methods.
+ */
+ private static boolean setPublicSource(final LogRecord record, final Class<?> type, final String methodName) {
+ if (Resource.class.isAssignableFrom(type)) {
+ record.setSourceClassName(type.getCanonicalName());
+ record.setSourceMethodName(methodName);
+ for (final Method m : type.getMethods()) { // List of public methods, ignoring parameters.
+ if (methodName.equals(m.getName())) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Reports a warning described by the given log record. The default implementation forwards
+ * the given record to one of the following destinations, in preference order:
+ *
+ * <ul>
+ * <li><code>{@linkplain StoreListener#eventOccured StoreListener.eventOccured}(new
+ * {@linkplain WarningEvent}(source, record))</code> on all listeners registered for this kind of event.</li>
+ * <li>Only if above step found no listener, then <code>{@linkplain Logging#getLogger(String)
+ * Logging.getLogger}(record.loggerName).{@linkplain Logger#log(LogRecord) log}(record)</code>
+ * where {@code loggerName} is one of the following:
+ * <ul>
+ * <li><code>record.{@linkplain LogRecord#getLoggerName() getLoggerName()}</code> if that value is non-null.</li>
+ * <li>Otherwise the value of {@link DataStoreProvider#getLogger()} if the provider is found.</li>
+ * <li>Otherwise the source {@link DataStore} package name.</li>
+ * </ul>
+ * </li>
+ * </ul>
+ *
+ * @param description warning details provided as a log record.
+ */
+ @Override
+ @SuppressWarnings("unchecked")
+ public void warning(final LogRecord description) {
+ if (!fire(new WarningEvent(source, description), WarningEvent.class)) {
+ final String name = description.getLoggerName();
+ final Logger logger;
+ if (name != null) {
+ logger = Logging.getLogger(name);
+ } else {
+ logger = logger();
+ description.setLoggerName(logger.getName());
+ }
+ if (description instanceof QuietLogRecord) {
+ ((QuietLogRecord) description).clearImplicitThrown();
+ }
+ logger.log(description);
+ }
+ }
+
+ /**
+ * Sends the given event to all listeners registered for the given type or for a super-type.
+ * This method first notifies the listeners registered in this {@code StoreListeners}, then
+ * notifies listeners registered in parent {@code StoreListeners}s. Each listener will be
+ * notified only once even if it has been registered many times.
+ *
+ * @param <T> compile-time value of the {@code eventType} argument.
+ * @param event the event to fire.
+ * @param eventType the type of events to be fired.
+ * @return {@code true} if the event has been sent to at least one listener.
+ */
+ @SuppressWarnings("unchecked")
+ public <T extends StoreEvent> boolean fire(final T event, final Class<T> eventType) {
+ ArgumentChecks.ensureNonNull("event", event);
+ ArgumentChecks.ensureNonNull("eventType", eventType);
+ Map<StoreListener<?>,Boolean> done = null;
+ StoreListeners m = this;
+ do {
+ for (ForType<?> e = m.listeners; e != null; e = e.next) {
+ if (e.type.isAssignableFrom(eventType)) {
+ done = ((ForType<? super T>) e).eventOccured(event, done);
+ }
+ }
+ m = m.parent;
+ } while (m != null);
+ return (done != null) && !done.isEmpty();
+ }
+
+ /**
+ * Registers a listener to notify when the specified kind of event occurs.
+ * Registering a listener for a given {@code eventType} also register the listener for all event sub-types.
+ * The same listener can be registered many times, but its {@link StoreListener#eventOccured(StoreEvent)}
+ * method will be invoked only once per event. This filtering applies even if the listener is registered
+ * on different resources in the same tree, for example a parent and its children.
+ *
+ * <div class="section">Warning events</div>
+ * If {@code eventType} is assignable from <code>{@linkplain WarningEvent}.class</code>,
+ * then registering that listener turns off logging of warning messages for this manager.
+ * This side-effect is applied on the assumption that the registered listener will handle
+ * warnings in its own way, for example by showing warnings in a widget.
+ *
+ * @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}).
+ *
+ * @see Resource#addListener(StoreListener, Class)
+ */
+ @SuppressWarnings("unchecked")
+ public synchronized <T extends StoreEvent> void addListener(final StoreListener<? super T> listener, final Class<T> eventType) {
+ ArgumentChecks.ensureNonNull("listener", listener);
+ ArgumentChecks.ensureNonNull("eventType", eventType);
+ ForType<T> ce = null;
+ for (ForType<?> e = listeners; e != null; e = e.next) {
+ if (e.type.equals(eventType)) {
+ ce = (ForType<T>) e;
+ break;
+ }
+ }
+ if (ce == null) {
+ ce = new ForType<>(eventType, listeners);
+ }
+ ce.add(listener);
+ }
+
+ /**
+ * Unregisters a listener previously added for the given type of events.
+ * The {@code eventType} must be the exact same class than the one given to the {@code addListener(…)} method;
+ * this method does not remove listeners registered for subclasses and does not remove listeners registered in
+ * parent manager.
+ *
+ * <p>If the same listener has been registered many times for the same even type, then this method removes only
+ * the most recent registration. In other words if {@code addListener(ls, type)} has been invoked twice, then
+ * {@code removeListener(ls, type)} needs to be invoked twice in order to remove all instances of that listener.
+ * If the given listener is not found, then this method does nothing (no exception is thrown).</p>
+ *
+ * <div class="section">Warning events</div>
+ * If {@code eventType} is <code>{@linkplain WarningEvent}.class</code> and if, after this method invocation,
+ * there is no remaining listener for warning events, then this {@code StoreListeners} will send future warnings
+ * to the loggers.
+ *
+ * @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}).
+ *
+ * @see Resource#removeListener(StoreListener, Class)
+ */
+ @SuppressWarnings("unchecked")
+ public synchronized <T extends StoreEvent> void removeListener(StoreListener<? super T> listener, Class<T> eventType) {
+ ArgumentChecks.ensureNonNull("listener", listener);
+ ArgumentChecks.ensureNonNull("eventType", eventType);
+ for (ForType<?> e = listeners; e != null; e = e.next) {
+ if (e.type.equals(eventType)) {
+ ((ForType<T>) e).remove(listener);
+ break;
+ }
+ }
+ }
+
+ /**
+ * Returns {@code true} if this object contains at least one listener.
+ *
+ * @return {@code true} if this object contains at least one listener, {@code false} otherwise.
+ */
+ @Override
+ public boolean hasListeners() {
+ StoreListeners m = this;
+ do {
+ if (listeners != null && listeners.hasListeners()) {
+ return true;
+ }
+ m = m.parent;
+ } while (m != null);
+ return false;
+ }
+
+ /**
+ * @deprecated Replaced by {@code addListener(listener, WarningEvent.class)}.
+ */
+ @Override
+ @Deprecated
+ public void addWarningListener(final WarningListener<? super Resource> listener) {
+ addListener(new Legacy(listener), WarningEvent.class);
+ }
+
+ /**
+ * @deprecated Replaced by {@code removeListener(listener, WarningEvent.class)}.
+ */
+ @Override
+ @Deprecated
+ public void removeWarningListener(final WarningListener<? super Resource> listener) {
+ for (ForType<?> e = listeners; e != null; e = e.next) {
+ if (e.type.equals(WarningEvent.class)) {
+ StoreListener<?>[] list = e.listeners;
+ if (list != null) {
+ for (final StoreListener<?> c : list) {
+ if (c instanceof Legacy && ((Legacy) c).delegate == listener) {
+ removeListener((StoreListener<WarningEvent>) c, WarningEvent.class);
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Deprecated
+ private static final class Legacy implements StoreListener<WarningEvent> {
+ final WarningListener<? super Resource> delegate;
+
+ Legacy(final WarningListener<? super Resource> delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override public void eventOccured(WarningEvent event) {
+ delegate.warningOccured(event.getSource(), event.getDescription());
+ }
+ }
+}
diff --git a/storage/sis-storage/src/main/java/org/apache/sis/storage/event/WarningEvent.java b/storage/sis-storage/src/main/java/org/apache/sis/storage/event/WarningEvent.java
new file mode 100644
index 0000000..9d97135
--- /dev/null
+++ b/storage/sis-storage/src/main/java/org/apache/sis/storage/event/WarningEvent.java
@@ -0,0 +1,90 @@
+/*
+ * 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.storage.event;
+
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+import org.apache.sis.storage.Resource;
+import org.apache.sis.util.ArgumentChecks;
+
+
+/**
+ * Describes non-fatal errors that occurred in a resource or a data store.
+ * The warning message is encapsulated in a {@link LogRecord} object, which allows the storage of various information
+ * ({@linkplain LogRecord#getThrown() stack trace}, {@linkplain LogRecord#getThreadID() thread identifier},
+ * {@linkplain LogRecord#getInstant() log time}, <i>etc.</i>) in addition of warning message.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @since 1.0
+ * @version 1.0
+ * @module
+ */
+public class WarningEvent extends StoreEvent {
+ /**
+ * For cross-version compatibility.
+ */
+ private static final long serialVersionUID = 3825327888379868663L;
+
+ /**
+ * The warning message together with its severity level, source method/class name,
+ * stack trace, thread identifier, <i>etc</i>.
+ */
+ private final LogRecord description;
+
+ /**
+ * Constructs an event for a warning that occurred in the given resource.
+ *
+ * @param source the resource on which the warning initially occurred.
+ * @param description log record containing warning message, stack trace (if any) and other information.
+ * @throws IllegalArgumentException if the given source is null.
+ * @throws NullPointerException if the given description is null.
+ */
+ public WarningEvent(final Resource source, final LogRecord description) {
+ super(source);
+ ArgumentChecks.ensureNonNull("description", description);
+ this.description = description;
+ }
+
+ /**
+ * Returns the warning message together with stack trace (if any) and other information.
+ *
+ * @return the log record containing warning message, stack trace and other information.
+ */
+ public LogRecord getDescription() {
+ return description;
+ }
+
+ /**
+ * Returns a string representation of this warning for debugging purpose.
+ *
+ * @return a string representation of this warning.
+ */
+ @Override
+ public String toString() {
+ final StringBuilder b = new StringBuilder();
+ final Level level = description.getLevel();
+ if (level != null) {
+ b.append(level.getLocalizedName()).append(": ");
+ }
+ b.append(description.getMessage());
+ final Throwable cause = description.getThrown();
+ if (cause != null) {
+ b.append(System.lineSeparator()).append("Caused by ").append(cause);
+ }
+ return b.toString();
+ }
+}
diff --git a/storage/sis-storage/src/main/java/org/apache/sis/storage/event/package-info.java b/storage/sis-storage/src/main/java/org/apache/sis/storage/event/package-info.java
index 4525209..5745fca 100644
--- a/storage/sis-storage/src/main/java/org/apache/sis/storage/event/package-info.java
+++ b/storage/sis-storage/src/main/java/org/apache/sis/storage/event/package-info.java
@@ -18,9 +18,20 @@
/**
* Provides interfaces and classes for dealing with different types of events fired by resources.
- * The different types of events are differentiated by the {@link StoreEvent} subclasses.
- * There is different subclasses for warnings, structural changes or changes in resource content.
- * It is possible to register a listener for only some specific types of events.
+ * The different types of events are specified by the {@link StoreEvent} subclasses.
+ * For example if a warning occurred while reading data from a file,
+ * then the {@link org.apache.sis.storage.DataStore} implementation should fire a {@link WarningEvent}.
+ *
+ * <p>Events may occur in the following situations:</p>
+ * <ul>
+ * <li>When a warning occurred.</li>
+ * <li>When the data store content changed (e.g. new feature instance added or removed).</li>
+ * <li>When the data store structure changed (e.g. a column is added in tabular data).</li>
+ * <li>Any other change at implementation choice.</li>
+ * </ul>
+ *
+ * Users can {@linkplain org.apache.sis.storage.Resource#addListener declare their interest
+ * to a specific kind of event}.
*
* @author Johann Sorel (Geomatys)
* @since 1.0
[sis] 03/05: Replace hard-coded string value by a new constant in
the Modules class.
Posted by de...@apache.org.
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 962ee26275bf87bf94acb948402fec3167d8a73c
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Fri Sep 6 19:25:29 2019 +0200
Replace hard-coded string value by a new constant in the Modules class.
---
.../src/main/java/org/apache/sis/console/TransformCommand.java | 3 ++-
.../src/main/java/org/apache/sis/internal/system/Modules.java | 5 +++++
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/application/sis-console/src/main/java/org/apache/sis/console/TransformCommand.java b/application/sis-console/src/main/java/org/apache/sis/console/TransformCommand.java
index 8c8cbe0..f0c7d9d 100644
--- a/application/sis-console/src/main/java/org/apache/sis/console/TransformCommand.java
+++ b/application/sis-console/src/main/java/org/apache/sis/console/TransformCommand.java
@@ -52,6 +52,7 @@ import org.apache.sis.internal.referencing.Formulas;
import org.apache.sis.internal.referencing.DirectPositionView;
import org.apache.sis.internal.referencing.ReferencingUtilities;
import org.apache.sis.internal.storage.CodeType;
+import org.apache.sis.internal.system.Modules;
import org.apache.sis.referencing.IdentifiedObjects;
import org.apache.sis.referencing.CRS;
import org.apache.sis.internal.util.X364;
@@ -649,6 +650,6 @@ final class TransformCommand extends FormattedOutputCommand {
* considered as an indication that the point is outside the domain of validity.
*/
private static void warning(final Exception e) {
- Logging.recoverableException(Logging.getLogger("org.apache.sis.console"), TransformCommand.class, "run", e);
+ Logging.recoverableException(Logging.getLogger(Modules.CONSOLE), TransformCommand.class, "run", e);
}
}
diff --git a/core/sis-utility/src/main/java/org/apache/sis/internal/system/Modules.java b/core/sis-utility/src/main/java/org/apache/sis/internal/system/Modules.java
index c3da41f..bf5d60a 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/internal/system/Modules.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/internal/system/Modules.java
@@ -93,6 +93,11 @@ public final class Modules {
public static final String SQL = "org.apache.sis.sql";
/**
+ * The {@value} module name.
+ */
+ public static final String CONSOLE = "org.apache.sis.console";
+
+ /**
* The major version number of all Apache SIS modules.
*
* @see org.apache.sis.util.Version
[sis] 04/05: Shapefile loggers should be package names,
not class names.
Posted by de...@apache.org.
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 b4b8bc395b85a3dbb00543fc84f519deee6d8d07
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Fri Sep 6 19:26:14 2019 +0200
Shapefile loggers should be package names, not class names.
---
.../java/org/apache/sis/util/logging/Logging.java | 18 +++---
.../apache/sis/internal/shapefile/AutoChecker.java | 4 +-
.../internal/shapefile/ShapefileByteReader.java | 64 +++++++++++-----------
.../sis/storage/shapefile/InputFeatureStream.java | 2 +-
.../jdbc/AbstractTestBaseForInternalJDBC.java | 2 +-
.../sis/storage/shapefile/ShapeFileTest.java | 4 +-
6 files changed, 49 insertions(+), 45 deletions(-)
diff --git a/core/sis-utility/src/main/java/org/apache/sis/util/logging/Logging.java b/core/sis-utility/src/main/java/org/apache/sis/util/logging/Logging.java
index 18e3489..df73cb4 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/util/logging/Logging.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/util/logging/Logging.java
@@ -153,6 +153,7 @@ public final class Logging extends Static {
* @return a logger for the specified name.
*/
public static Logger getLogger(final String name) {
+ ArgumentChecks.ensureNonNull("name", name);
final LoggerFactory<?> factory = Logging.factory;
if (factory != null) {
final Logger logger = factory.getLogger(name);
@@ -164,18 +165,21 @@ public final class Logging extends Static {
}
/**
- * Returns a logger for the specified class. This convenience method invokes
- * {@link #getLogger(String)} with the package name as the logger name.
+ * Returns a logger for the package of the specified class. This convenience method invokes
+ * {@link #getLogger(String)} with the package name of the given class taken as the logger name.
*
- * @param classe the class for which to obtain a logger.
+ * @param source the class which will emit a logging message.
* @return a logger for the specified class.
+ *
+ * @since 1.0
*/
- static Logger getLogger(Class<?> classe) {
+ public static Logger getLogger(Class<?> source) {
+ ArgumentChecks.ensureNonNull("source", source);
Class<?> outer;
- while ((outer = classe.getEnclosingClass()) != null) {
- classe = outer;
+ while ((outer = source.getEnclosingClass()) != null) {
+ source = outer;
}
- String name = classe.getName();
+ String name = source.getName();
final int separator = name.lastIndexOf('.');
name = (separator >= 1) ? name.substring(0, separator) : "";
if (name.startsWith(Modules.INTERNAL_CLASSNAME_PREFIX)) {
diff --git a/storage/sis-shapefile/src/main/java/org/apache/sis/internal/shapefile/AutoChecker.java b/storage/sis-shapefile/src/main/java/org/apache/sis/internal/shapefile/AutoChecker.java
index 9f69d96..abf386f 100644
--- a/storage/sis-shapefile/src/main/java/org/apache/sis/internal/shapefile/AutoChecker.java
+++ b/storage/sis-shapefile/src/main/java/org/apache/sis/internal/shapefile/AutoChecker.java
@@ -34,7 +34,7 @@ import org.apache.sis.util.logging.Logging;
*/
public abstract class AutoChecker {
/** Logger. */
- private static Logger LOGGER = Logging.getLogger(AutoChecker.class.getSimpleName());
+ private static Logger LOGGER = Logging.getLogger(AutoChecker.class);
/**
* Format a resource bundle message.
@@ -124,7 +124,7 @@ public abstract class AutoChecker {
protected boolean isLoggable(Level level) {
return LOGGER.isLoggable(level);
}
-
+
/**
* Logs (and take the time to format an entry log) only if the logger accepts the message.
* @param logLevel Log level.
diff --git a/storage/sis-shapefile/src/main/java/org/apache/sis/internal/shapefile/ShapefileByteReader.java b/storage/sis-shapefile/src/main/java/org/apache/sis/internal/shapefile/ShapefileByteReader.java
index 0e46f07..e1fad49 100644
--- a/storage/sis-shapefile/src/main/java/org/apache/sis/internal/shapefile/ShapefileByteReader.java
+++ b/storage/sis-shapefile/src/main/java/org/apache/sis/internal/shapefile/ShapefileByteReader.java
@@ -55,16 +55,16 @@ public class ShapefileByteReader extends CommonByteReader<InvalidShapefileFormat
/** Type of the features contained in this shapefile. */
private DefaultFeatureType featuresType;
-
+
/** Shapefile index. */
private File shapeFileIndex;
-
+
/** Shapefile indexes (loaded from .SHX file, if any found). */
private ArrayList<Integer> indexes;
-
+
/** Shapefile records lengths (loaded from .SHX file, if any found). */
private ArrayList<Integer> recordsLengths;
-
+
/**
* Construct a shapefile byte reader.
* @param shapefile Shapefile.
@@ -78,10 +78,10 @@ public class ShapefileByteReader extends CommonByteReader<InvalidShapefileFormat
public ShapefileByteReader(File shapefile, File dbaseFile, File shapefileIndex) throws InvalidShapefileFormatException, SQLInvalidDbaseFileFormatException, SQLShapefileNotFoundException, SQLDbaseFileNotFoundException {
super(shapefile, InvalidShapefileFormatException.class, SQLShapefileNotFoundException.class);
this.shapeFileIndex = shapefileIndex;
-
+
loadDatabaseFieldDescriptors(dbaseFile);
loadDescriptor();
-
+
if (this.shapeFileIndex != null) {
loadShapefileIndexes();
}
@@ -156,23 +156,23 @@ public class ShapefileByteReader extends CommonByteReader<InvalidShapefileFormat
if (this.shapeFileIndex == null) {
return false;
}
-
+
try(FileInputStream fis = new FileInputStream(this.shapeFileIndex); FileChannel fc = fis.getChannel()) {
try {
int fsize = (int)fc.size();
MappedByteBuffer indexesByteBuffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fsize);
-
+
// Indexes entries follow.
this.indexes = new ArrayList<>();
this.recordsLengths = new ArrayList<>();
indexesByteBuffer.position(100);
indexesByteBuffer.order(ByteOrder.BIG_ENDIAN);
-
+
while(indexesByteBuffer.hasRemaining()) {
this.indexes.add(indexesByteBuffer.getInt()); // Data offset : the position of the record in the main shapefile, expressed in words (16 bits).
this.recordsLengths.add(indexesByteBuffer.getInt()); // Length of this shapefile record.
}
-
+
log(Level.INFO, "log.index_has_been_read", this.shapeFileIndex.getAbsolutePath(), this.indexes.size(), this.getFile().getAbsolutePath());
return true;
}
@@ -236,15 +236,15 @@ public class ShapefileByteReader extends CommonByteReader<InvalidShapefileFormat
String message = format(Level.SEVERE, "excp.no_direct_access", getFile().getAbsolutePath());
throw new SQLNoDirectAccessAvailableException(message);
}
-
+
int position = this.indexes.get(recordNumber - 1) * 2; // Indexes unit are words (16 bits).
-
+
// Check that the asked record number is not after the last.
if (position >= this.getByteBuffer().capacity()) {
String message = format(Level.SEVERE, "excp.wrong_direct_access_after_last", recordNumber, getFile().getAbsolutePath());
throw new SQLInvalidRecordNumberForDirectAccessException(recordNumber, message);
}
-
+
try {
getByteBuffer().position(position);
}
@@ -253,7 +253,7 @@ public class ShapefileByteReader extends CommonByteReader<InvalidShapefileFormat
throw new RuntimeException(message, e);
}
}
-
+
/**
* Complete a feature with shapefile content.
* @param feature Feature to complete.
@@ -320,24 +320,24 @@ public class ShapefileByteReader extends CommonByteReader<InvalidShapefileFormat
// Handle multiple polygon parts.
if (numParts > 1) {
- Logger log = Logging.getLogger(ShapefileByteReader.class.getName());
-
+ Logger log = Logging.getLogger(ShapefileByteReader.class);
+
if (log.isLoggable(Level.FINER)) {
String format = "Polygon with multiple linear rings encountered at position {0,number} with {1,number} parts.";
String message = MessageFormat.format(format, getByteBuffer().position(), numParts);
log.finer(message);
}
-
+
poly = readMultiplePolygonParts(numParts, numPoints);
}
else {
// Polygon with an unique part.
poly = readUniquePolygonPart(numPoints);
}
-
+
feature.setPropertyValue(GEOMETRY_NAME, poly);
}
-
+
/**
* Read a polygon that has a unique part.
* @param numPoints Number of the points of the polygon.
@@ -346,7 +346,7 @@ public class ShapefileByteReader extends CommonByteReader<InvalidShapefileFormat
@Deprecated // As soon as the readMultiplePolygonParts method proofs working well, this readUniquePolygonPart method can be removed and all calls be deferred to readMultiplePolygonParts.
private Polygon readUniquePolygonPart(int numPoints) {
/*int part = */ getByteBuffer().getInt();
-
+
Polygon poly = new Polygon();
// create a line from the points
@@ -360,10 +360,10 @@ public class ShapefileByteReader extends CommonByteReader<InvalidShapefileFormat
ypnt = getByteBuffer().getDouble();
poly.lineTo(xpnt, ypnt);
}
-
+
return poly;
}
-
+
/**
* Read a polygon that has multiple parts.
* @param numParts Number of parts of this polygon.
@@ -371,8 +371,8 @@ public class ShapefileByteReader extends CommonByteReader<InvalidShapefileFormat
* @return a multiple part polygon.
*/
private Polygon readMultiplePolygonParts(int numParts, int numPoints) {
- /**
- * From ESRI Specification :
+ /**
+ * From ESRI Specification :
* Parts : 0 5 (meaning : 0 designs the first v1, 5 designs the first v5 on the points list below).
* Points : v1 v2 v3 v4 v1 v5 v8 v7 v6 v5
*
@@ -382,10 +382,10 @@ public class ShapefileByteReader extends CommonByteReader<InvalidShapefileFormat
* Byte 36 NumParts NumParts Integer 1 Little
* Byte 40 NumPoints NumPoints Integer 1 Little
* Byte 44 Parts Parts Integer NumParts Little
- * Byte X Points Points Point NumPoints Little
+ * Byte X Points Points Point NumPoints Little
*/
int[] partsIndexes = new int[numParts];
-
+
// Read all the parts indexes (starting at byte 44).
for(int index=0; index < numParts; index ++) {
partsIndexes[index] = getByteBuffer().getInt();
@@ -399,7 +399,7 @@ public class ShapefileByteReader extends CommonByteReader<InvalidShapefileFormat
xPoints[index] = getByteBuffer().getDouble();
yPoints[index] = getByteBuffer().getDouble();
}
-
+
// Create the polygon from the points.
Polygon poly = new Polygon();
@@ -414,18 +414,18 @@ public class ShapefileByteReader extends CommonByteReader<InvalidShapefileFormat
break;
}
}
-
+
if (newPolygon) {
- poly.startPath(xPoints[index], yPoints[index]);
+ poly.startPath(xPoints[index], yPoints[index]);
}
else {
- poly.lineTo(xPoints[index], yPoints[index]);
+ poly.lineTo(xPoints[index], yPoints[index]);
}
}
-
+
return poly;
}
-
+
/**
* Load polyline feature.
* @param feature Feature to fill.
diff --git a/storage/sis-shapefile/src/main/java/org/apache/sis/storage/shapefile/InputFeatureStream.java b/storage/sis-shapefile/src/main/java/org/apache/sis/storage/shapefile/InputFeatureStream.java
index e8da687..1d19f09 100644
--- a/storage/sis-shapefile/src/main/java/org/apache/sis/storage/shapefile/InputFeatureStream.java
+++ b/storage/sis-shapefile/src/main/java/org/apache/sis/storage/shapefile/InputFeatureStream.java
@@ -53,7 +53,7 @@ import org.opengis.feature.Feature;
*/
public class InputFeatureStream extends InputStream {
/** Logger. */
- private static Logger LOGGER = Logging.getLogger(InputFeatureStream.class.getSimpleName());
+ private static Logger LOGGER = Logging.getLogger(InputFeatureStream.class);
/** Resource bundle. */
private ResourceBundle rsc = ResourceBundle.getBundle(InputFeatureStream.class.getName());
diff --git a/storage/sis-shapefile/src/test/java/org/apache/sis/internal/shapefile/jdbc/AbstractTestBaseForInternalJDBC.java b/storage/sis-shapefile/src/test/java/org/apache/sis/internal/shapefile/jdbc/AbstractTestBaseForInternalJDBC.java
index fefbf2d..ffa2220 100644
--- a/storage/sis-shapefile/src/test/java/org/apache/sis/internal/shapefile/jdbc/AbstractTestBaseForInternalJDBC.java
+++ b/storage/sis-shapefile/src/test/java/org/apache/sis/internal/shapefile/jdbc/AbstractTestBaseForInternalJDBC.java
@@ -39,7 +39,7 @@ import org.junit.Before;
*/
public abstract class AbstractTestBaseForInternalJDBC extends TestCase {
/** Logger. */
- protected Logger log = Logging.getLogger(getClass().getName());
+ protected Logger log = Logging.getLogger(getClass());
/** The database file to use for testing purpose. */
protected File dbfFile;
diff --git a/storage/sis-shapefile/src/test/java/org/apache/sis/storage/shapefile/ShapeFileTest.java b/storage/sis-shapefile/src/test/java/org/apache/sis/storage/shapefile/ShapeFileTest.java
index 26b529a..6605b24 100644
--- a/storage/sis-shapefile/src/test/java/org/apache/sis/storage/shapefile/ShapeFileTest.java
+++ b/storage/sis-shapefile/src/test/java/org/apache/sis/storage/shapefile/ShapeFileTest.java
@@ -91,7 +91,7 @@ public final strictfp class ShapeFileTest extends TestCase {
*/
@Test
public void testDescriptors() throws URISyntaxException, DataStoreException {
- Logger log = org.apache.sis.util.logging.Logging.getLogger(ShapeFileTest.class.getName());
+ Logger log = org.apache.sis.util.logging.Logging.getLogger(ShapeFileTest.class);
ShapeFile shp = new ShapeFile(path("ABRALicenseePt_4326_clipped.shp"));
shp.loadDescriptors();
@@ -119,7 +119,7 @@ public final strictfp class ShapeFileTest extends TestCase {
ShapeFile shp = new ShapeFile(path("DEPARTEMENT.SHP"));
Feature first = null, last = null;
- Logger log = org.apache.sis.util.logging.Logging.getLogger(ShapeFileTest.class.getName());
+ Logger log = org.apache.sis.util.logging.Logging.getLogger(ShapeFileTest.class);
try(InputFeatureStream is = shp.findAll()) {
Feature feature = is.readFeature();