Author: desruisseaux
Date: Thu May 30 16:17:57 2013
New Revision: 1487913

URL: http://svn.apache.org/r1487913
Log:
Provides a WarningListener interface.

Added:
    
sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/logging/WarningListener.java
   (with props)
    
sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/WarningConsumer.java
   (with props)
    
sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/WarningProducer.java
      - copied, changed from r1487611, 
sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/WarningProducer.java
Removed:
    
sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/WarningProducer.java
Modified:
    
sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
    
sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
    
sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
    
sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Decoder.java
    
sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/ChannelDecoder.java
    
sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/DecoderWrapper.java
    
sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/MetadataReader.java
    
sis/branches/JDK7/storage/sis-netcdf/src/test/java/org/apache/sis/storage/netcdf/ConformanceTest.java
    
sis/branches/JDK7/storage/sis-netcdf/src/test/java/org/apache/sis/storage/netcdf/MetadataReaderTest.java
    
sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStore.java

Added: 
sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/logging/WarningListener.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/logging/WarningListener.java?rev=1487913&view=auto
==============================================================================
--- 
sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/logging/WarningListener.java
 (added)
+++ 
sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/logging/WarningListener.java
 [UTF-8] Thu May 30 16:17:57 2013
@@ -0,0 +1,65 @@
+/*
+ * 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.util.logging;
+
+import java.util.EventListener;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+
+
+/**
+ * Intercepts non-fatal error messages logged by {@link 
org.apache.sis.storage.DataStore} or other SIS objects.
+ * Warnings are encapsulated in {@link LogRecord} objects and logged at {@link 
Level#WARNING} if the emitter does not
+ * have any registered any {@code WarningListener}. This listener allows 
applications to intercept warning records for:
+ *
+ * <ul>
+ *   <li>displaying the warning in a dialog or performing any other action 
that the application may choose,</li>
+ *   <li>reducing the amount of records to be logged.</li>
+ * </ul>
+ *
+ * The difference between using this listener or configuring the logging 
{@link java.util.logging.Handler} is
+ * that listeners allow to handle the warnings on a per-{@code DataStore} (or 
any other emitter) instance.
+ *
+ * @param <T> The type of the warnings emitter.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.3
+ * @version 0.3
+ * @module
+ *
+ * @see org.apache.sis.storage.DataStore#addWarningListener(WarningListener)
+ */
+public interface WarningListener<T> extends EventListener {
+    /**
+     * Reports the occurrence of a non-fatal error. The emitter process (often 
a
+     * {@link org.apache.sis.storage.DataStore} in the midst of a reading 
process)
+     * will continue following the call to this method.
+     *
+     * <p>The {@code LogRecord} provides the warning {@linkplain 
LogRecord#getMessage() message} together with
+     * programmatic information like the {@linkplain 
LogRecord#getSourceClassName() source class name} and
+     * {@linkplain LogRecord#getSourceMethodName() method name} where the 
warning occurred. The log record
+     * may optionally contains the exception which has been {@linkplain 
LogRecord#getThrown() thrown}.</p>
+     *
+     * <p>Applications may choose to ignore the warning, display a dialog or 
take any other action they choose.
+     * Applications do not need to log the warning, since logging will be done 
automatically if the emitter has
+     * no registered warning listeners.</p>
+     *
+     * @param source  The object that emitted a warning.
+     * @param warning The warning message together with programmatic 
information.
+     */
+    void warningOccured(T source, LogRecord warning);
+}

Propchange: 
sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/logging/WarningListener.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/logging/WarningListener.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8

Modified: 
sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java?rev=1487913&r1=1487912&r2=1487913&view=diff
==============================================================================
--- 
sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
 [UTF-8] (original)
+++ 
sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.java
 [UTF-8] Thu May 30 16:17:57 2013
@@ -311,6 +311,11 @@ public final class Errors extends Indexe
         public static final int NegativeArrayLength_1 = 78;
 
         /**
+         * Element “{0}” has not been found.
+         */
+        public static final int NoSuchElement_1 = 96;
+
+        /**
          * No property named “{0}” has been found in “{1}”.
          */
         public static final int NoSuchProperty_2 = 73;

Modified: 
sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties?rev=1487913&r1=1487912&r2=1487913&view=diff
==============================================================================
--- 
sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
 [ISO-8859-1] (original)
+++ 
sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors.properties
 [ISO-8859-1] Thu May 30 16:17:57 2013
@@ -88,6 +88,7 @@ NonTemporalUnit_1               = \u201c
 NotANumber_1                    = Argument \u2018{0}\u2019 shall not be NaN 
(Not-a-Number).
 NotAPrimitiveWrapper_1          = Class \u2018{0}\u2019 is not a primitive 
type wrapper.
 NotComparableClass_1            = Class \u2018{0}\u2019 is not a comparable.
+NoSuchElement_1                 = Element \u201c{0}\u201d has not been found.
 NoSuchProperty_2                = No property named \u201c{0}\u201d has been 
found in \u201c{1}\u201d.
 NoUnit                          = No unit of measurement has been specified.
 NullArgument_1                  = Argument \u2018{0}\u2019 shall not be null.

Modified: 
sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties?rev=1487913&r1=1487912&r2=1487913&view=diff
==============================================================================
--- 
sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
 [ISO-8859-1] (original)
+++ 
sis/branches/JDK7/core/sis-utility/src/main/java/org/apache/sis/util/resources/Errors_fr.properties
 [ISO-8859-1] Thu May 30 16:17:57 2013
@@ -78,6 +78,7 @@ NonTemporalUnit_1               = \u201c
 NotANumber_1                    = L\u2019argument \u2018{0}\u2019 ne doit pas 
\u00eatre NaN (Not-a-Number).
 NotAPrimitiveWrapper_1          = La classe \u2018{0}\u2019 n\u2019est pas un 
adaptateur d\u2019un type primitif.
 NotComparableClass_1            = La classe \u2018{0}\u2019 n\u2019est pas 
comparable.
+NoSuchElement_1                 = L\u2019\u00e9lement \u201c{0}\u201d n\u2019a 
pas \u00e9t\u00e9 trouv\u00e9.
 NoSuchProperty_2                = Aucune propri\u00e9t\u00e9 nomm\u00e9e 
\u201c{0}\u201d n\u2019a \u00e9t\u00e9 trouv\u00e9e dans \u201c{1}\u201d.
 NoUnit                          = Aucune unit\u00e9 de mesure n\u2019a 
\u00e9t\u00e9 sp\u00e9cifi\u00e9e.
 NullArgument_1                  = L\u2019argument \u2018{0}\u2019 ne doit pas 
\u00eatre nul.

Modified: 
sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Decoder.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Decoder.java?rev=1487913&r1=1487912&r2=1487913&view=diff
==============================================================================
--- 
sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Decoder.java
 [UTF-8] (original)
+++ 
sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Decoder.java
 [UTF-8] Thu May 30 16:17:57 2013
@@ -21,6 +21,7 @@ import java.io.Closeable;
 import java.io.IOException;
 import javax.measure.unit.Unit;
 import org.apache.sis.measure.Units;
+import org.apache.sis.internal.storage.WarningProducer;
 
 
 /**
@@ -33,12 +34,18 @@ import org.apache.sis.measure.Units;
  */
 public abstract class Decoder extends WarningProducer implements Closeable {
     /**
+     * Sets to {@code true} for canceling a reading process.
+     * This flag is honored on a <cite>best effort</cite> basis only.
+     */
+    public volatile boolean canceled;
+
+    /**
      * Creates a new decoder.
      *
-     * @param parent Where to send the warnings, or {@code null} if none.
+     * @param sink Where to send the warnings, or {@code null} if none.
      */
-    protected Decoder(final WarningProducer parent) {
-        super(parent);
+    protected Decoder(final WarningProducer sink) {
+        super(sink);
     }
 
     /**

Modified: 
sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/ChannelDecoder.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/ChannelDecoder.java?rev=1487913&r1=1487912&r2=1487913&view=diff
==============================================================================
--- 
sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/ChannelDecoder.java
 [UTF-8] (original)
+++ 
sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/ChannelDecoder.java
 [UTF-8] Thu May 30 16:17:57 2013
@@ -37,7 +37,7 @@ import org.apache.sis.internal.jdk8.Func
 import org.apache.sis.internal.netcdf.Decoder;
 import org.apache.sis.internal.netcdf.Variable;
 import org.apache.sis.internal.netcdf.GridGeometry;
-import org.apache.sis.internal.netcdf.WarningProducer;
+import org.apache.sis.internal.storage.WarningProducer;
 import org.apache.sis.internal.storage.ChannelDataInput;
 import org.apache.sis.internal.util.CollectionsExt;
 import org.apache.sis.internal.jdk8.JDK8;
@@ -173,16 +173,16 @@ public final class ChannelDecoder extend
      * Creates a new decoder for the given file.
      * This constructor parses immediately the header.
      *
-     * @param  parent   Where to send the warnings, or {@code null} if none.
+     * @param  sink     Where to send the warnings, or {@code null} if none.
      * @param  filename A file identifier used only for formatting error 
message.
      * @param  channel  The channel from where data are read.
      * @throws IOException If an error occurred while reading the channel.
      * @throws DataStoreException If the content of the given channel is not a 
NetCDF file.
      */
-    public ChannelDecoder(final WarningProducer parent, final String filename, 
final ReadableByteChannel channel)
+    public ChannelDecoder(final WarningProducer sink, final String filename, 
final ReadableByteChannel channel)
             throws IOException, DataStoreException
     {
-        super(parent);
+        super(sink);
         // The buffer must be backed by a Java {@code byte[]} array,
         // because we will occasionally reference that array.
         input = new ChannelDataInput(filename, channel, 
ByteBuffer.allocate(4096), false);

Modified: 
sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/DecoderWrapper.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/DecoderWrapper.java?rev=1487913&r1=1487912&r2=1487913&view=diff
==============================================================================
--- 
sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/DecoderWrapper.java
 [UTF-8] (original)
+++ 
sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/DecoderWrapper.java
 [UTF-8] Thu May 30 16:17:57 2013
@@ -21,11 +21,13 @@ import java.util.List;
 import java.util.EnumSet;
 import java.io.IOException;
 import ucar.nc2.Group;
+import ucar.nc2.Dimension;
 import ucar.nc2.Attribute;
 import ucar.nc2.VariableIF;
 import ucar.nc2.NetcdfFile;
 import ucar.nc2.dataset.NetcdfDataset;
 import ucar.nc2.dataset.CoordinateSystem;
+import ucar.nc2.util.CancelTask;
 import ucar.nc2.units.DateUnit;
 import ucar.nc2.time.Calendar;
 import ucar.nc2.time.CalendarDate;
@@ -34,8 +36,7 @@ import org.apache.sis.util.ArraysExt;
 import org.apache.sis.internal.netcdf.Decoder;
 import org.apache.sis.internal.netcdf.Variable;
 import org.apache.sis.internal.netcdf.GridGeometry;
-import org.apache.sis.internal.netcdf.WarningProducer;
-import ucar.nc2.Dimension;
+import org.apache.sis.internal.storage.WarningProducer;
 
 
 /**
@@ -46,7 +47,7 @@ import ucar.nc2.Dimension;
  * @version 0.3
  * @module
  */
-public final class DecoderWrapper extends Decoder {
+public final class DecoderWrapper extends Decoder implements CancelTask {
     /**
      * The NetCDF file to read.
      * This file is set at construction time.
@@ -85,15 +86,27 @@ public final class DecoderWrapper extend
      * {@link NetcdfFile} instance, the {@link NetcdfDataset} subclass is 
necessary in order to
      * get coordinate system information.
      *
-     * @param parent Where to send the warnings, or {@code null} if none.
-     * @param file The NetCDF file from which to parse metadata.
+     * @param sink Where to send the warnings, or {@code null} if none.
+     * @param file The NetCDF file from which to read data.
      */
-    public DecoderWrapper(final WarningProducer parent, final NetcdfFile file) 
{
-        super(parent);
+    public DecoderWrapper(final WarningProducer sink, final NetcdfFile file) {
+        super(sink);
         this.file = file;
     }
 
     /**
+     * Creates a new decoder for the given filename.
+     *
+     * @param  sink     Where to send the warnings, or {@code null} if none.
+     * @param  filename The name of the NetCDF file from which to read data.
+     * @throws IOException If an error occurred while opening the NetCDF file.
+     */
+    public DecoderWrapper(final WarningProducer sink, final String filename) 
throws IOException {
+        super(sink);
+        file = NetcdfDataset.openDataset(filename, false, this);
+    }
+
+    /**
      * Defines the groups where to search for named attributes, in preference 
order.
      * The {@code null} group name stands for the global attributes.
      */
@@ -311,6 +324,27 @@ public final class DecoderWrapper extend
     }
 
     /**
+     * Invoked by the UCAR NetCDF library for checking if the reading process 
has been canceled.
+     * This method returns the {@link #canceled} flag.
+     *
+     * @return The {@link #canceled} flag.
+     */
+    @Override
+    public boolean isCancel() {
+        return canceled;
+    }
+
+    /**
+     * Invoked by the UCAR NetCDF library when an error occurred.
+     *
+     * @param message The error message.
+     */
+    @Override
+    public void setError(final String message) {
+        warning(null, message);
+    }
+
+    /**
      * Closes the NetCDF file.
      *
      * @throws IOException If an error occurred while closing the file.

Modified: 
sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/MetadataReader.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/MetadataReader.java?rev=1487913&r1=1487912&r2=1487913&view=diff
==============================================================================
--- 
sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/MetadataReader.java
 [UTF-8] (original)
+++ 
sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/MetadataReader.java
 [UTF-8] Thu May 30 16:17:57 2013
@@ -64,7 +64,7 @@ import org.apache.sis.internal.netcdf.Ax
 import org.apache.sis.internal.netcdf.Decoder;
 import org.apache.sis.internal.netcdf.Variable;
 import org.apache.sis.internal.netcdf.GridGeometry;
-import org.apache.sis.internal.netcdf.WarningProducer;
+import org.apache.sis.internal.storage.WarningProducer;
 import org.apache.sis.internal.util.DefaultFactories;
 import org.apache.sis.internal.metadata.MetadataUtilities;
 
@@ -169,12 +169,11 @@ final class MetadataReader extends Warni
     /**
      * Creates a new <cite>NetCDF to ISO</cite> mapper for the given source.
      *
-     * @param  parent Where to send the warnings, or {@code null} if none.
      * @param  decoder The source of NetCDF attributes.
      * @throws IOException If an I/O operation was necessary but failed.
      */
-    MetadataReader(final WarningProducer parent, final Decoder decoder) throws 
IOException {
-        super(parent);
+    MetadataReader(final Decoder decoder) throws IOException {
+        super(decoder.sink);
         this.decoder = decoder;
         decoder.setSearchPath(SEARCH_PATH);
         searchPath = decoder.getSearchPath();

Modified: 
sis/branches/JDK7/storage/sis-netcdf/src/test/java/org/apache/sis/storage/netcdf/ConformanceTest.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-netcdf/src/test/java/org/apache/sis/storage/netcdf/ConformanceTest.java?rev=1487913&r1=1487912&r2=1487913&view=diff
==============================================================================
--- 
sis/branches/JDK7/storage/sis-netcdf/src/test/java/org/apache/sis/storage/netcdf/ConformanceTest.java
 [UTF-8] (original)
+++ 
sis/branches/JDK7/storage/sis-netcdf/src/test/java/org/apache/sis/storage/netcdf/ConformanceTest.java
 [UTF-8] Thu May 30 16:17:57 2013
@@ -66,7 +66,7 @@ public final strictfp class ConformanceT
     @Override
     protected Metadata wrap(final NetcdfFile file) throws IOException {
         final Decoder decoder = new DecoderWrapper(null, file);
-        final MetadataReader ncISO = new MetadataReader(null, decoder);
+        final MetadataReader ncISO = new MetadataReader(decoder);
         return ncISO.read();
         // Do not close the file, as this will be done by the parent test 
class.
     }

Modified: 
sis/branches/JDK7/storage/sis-netcdf/src/test/java/org/apache/sis/storage/netcdf/MetadataReaderTest.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-netcdf/src/test/java/org/apache/sis/storage/netcdf/MetadataReaderTest.java?rev=1487913&r1=1487912&r2=1487913&view=diff
==============================================================================
--- 
sis/branches/JDK7/storage/sis-netcdf/src/test/java/org/apache/sis/storage/netcdf/MetadataReaderTest.java
 [UTF-8] (original)
+++ 
sis/branches/JDK7/storage/sis-netcdf/src/test/java/org/apache/sis/storage/netcdf/MetadataReaderTest.java
 [UTF-8] Thu May 30 16:17:57 2013
@@ -53,7 +53,7 @@ public final strictfp class MetadataRead
     public void testEmbedded() throws IOException {
         final Metadata metadata;
         try (Decoder input = ChannelDecoderTest.createChannelDecoder(NCEP)) {
-            metadata = new MetadataReader(null, input).read();
+            metadata = new MetadataReader(input).read();
         }
         compareToExpected(metadata);
     }
@@ -68,7 +68,7 @@ public final strictfp class MetadataRead
     public void testUCAR() throws IOException {
         final Metadata metadata;
         try (Decoder input = new DecoderWrapper(null, new 
NetcdfDataset(open(NCEP)))) {
-            metadata = new MetadataReader(null, input).read();
+            metadata = new MetadataReader(input).read();
         }
         compareToExpected(metadata);
     }

Added: 
sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/WarningConsumer.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/WarningConsumer.java?rev=1487913&view=auto
==============================================================================
--- 
sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/WarningConsumer.java
 (added)
+++ 
sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/WarningConsumer.java
 [UTF-8] Thu May 30 16:17:57 2013
@@ -0,0 +1,136 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.internal.storage;
+
+import java.util.logging.Logger;
+import java.util.logging.LogRecord;
+import java.util.NoSuchElementException;
+import org.apache.sis.util.ArgumentChecks;
+import org.apache.sis.util.resources.Errors;
+import org.apache.sis.util.logging.WarningListener;
+
+
+/**
+ * The leaf of a chain of {@link WarningProducer}, which hold the list of 
{@link WarningListener}s to notify.
+ *
+ * @param <T> The type of the object declared as warnings emitter.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @since   0.3
+ * @version 0.3
+ * @module
+ */
+public final class WarningConsumer<T> extends WarningProducer {
+    /**
+     * The declared source of warnings. This is not necessarily the real 
source,
+     * but this is the source that we declare in public API.
+     */
+    private final T source;
+
+    /**
+     * Where to log the warnings when there is no registered listener.
+     */
+    private final Logger logger;
+
+    /**
+     * The listeners, or {@code null} if none. This is a <cite>copy on 
write</cite> array:
+     * no elements are modified once the array have been created.
+     */
+    private volatile WarningListener<? super T>[] listeners;
+
+    /**
+     * Creates a new instance with initially no listener.
+     * Warnings will be logger to the given logger, unless at least one 
listener is registered.
+     *
+     * @param source The declared source of warnings. This is not necessarily 
the real source,
+     *               but this is the source that we declare in public API.
+     * @param logger Where to log the warnings when there is no registered 
listener.
+     */
+    public WarningConsumer(final T source, final Logger logger) {
+        super(null);
+        this.source = source;
+        this.logger = logger;
+    }
+
+    /**
+     * Invoked when a new warning has been emitted. This method notifies the 
listeners if any,
+     * or log the warning otherwise.
+     */
+    @Override
+    void sendWarning(final LogRecord record) {
+        final WarningListener[] current = listeners;
+        if (current != null) {
+            for (final WarningListener<? super T> listener : listeners) {
+                listener.warningOccured(source, record);
+            }
+        } else {
+            record.setLoggerName(logger.getName());
+            logger.log(record);
+        }
+    }
+
+    /**
+     * Adds a listener to be notified when a warning occurred while reading 
from or writing to the storage.
+     *
+     * @param  listener The listener to add.
+     * @throws IllegalArgumentException If the given listener is already 
registered in this data store.
+     */
+    public void addWarningListener(final WarningListener<? super T> listener) 
throws IllegalArgumentException {
+        ArgumentChecks.ensureNonNull("listener", listener);
+        final WarningListener<? super T>[] current = listeners;
+        final int length = (current != null) ? current.length : 0;
+
+        @SuppressWarnings({"unchecked", "rawtypes"}) // Generic array creation.
+        final WarningListener<? super T>[] copy = new WarningListener[length + 
1];
+        for (int i=0; i<length; i++) {
+            final WarningListener<? super T> c = current[i];
+            if (c == listener) {
+                throw new 
IllegalArgumentException(Errors.format(Errors.Keys.ElementAlreadyPresent_1, 
listener));
+            }
+            copy[i] = c;
+        }
+        copy[length] = listener;
+        listeners = copy;
+    }
+
+    /**
+     * Removes a previously registered listener.
+     *
+     * @param  listener The listener to remove.
+     * @throws NoSuchElementException If the given listener is not registered 
in this data store.
+     */
+    public void removeWarningListener(final WarningListener<? super T> 
listener) throws NoSuchElementException {
+        final WarningListener<? super T>[] current = listeners;
+        if (current != null) {
+            for (int i=0; i<current.length; i++) {
+                if (current[i] == listener) {
+                    if (current.length == 1) {
+                        listeners = null;
+                    } else {
+                        @SuppressWarnings({"unchecked", "rawtypes"}) // 
Generic array creation.
+                        final WarningListener<? super T>[] copy = new 
WarningListener[current.length - 1];
+                        System.arraycopy(current, 0, copy, 0, i);
+                        System.arraycopy(current, i+1, copy, i, copy.length - 
i);
+                        listeners = copy;
+                    }
+                    return;
+                }
+            }
+        }
+        throw new 
NoSuchElementException(Errors.format(Errors.Keys.NoSuchElement_1, listener));
+    }
+}

Propchange: 
sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/WarningConsumer.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/WarningConsumer.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain;charset=UTF-8

Copied: 
sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/WarningProducer.java
 (from r1487611, 
sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/WarningProducer.java)
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/WarningProducer.java?p2=sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/WarningProducer.java&p1=sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/WarningProducer.java&r1=1487611&r2=1487913&rev=1487913&view=diff
==============================================================================
--- 
sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/WarningProducer.java
 [UTF-8] (original)
+++ 
sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/WarningProducer.java
 [UTF-8] Thu May 30 16:17:57 2013
@@ -14,17 +14,31 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.sis.internal.netcdf;
+package org.apache.sis.internal.storage;
 
 import java.util.logging.Level;
-import java.util.logging.Logger;
 import java.util.logging.LogRecord;
 import org.apache.sis.util.Exceptions;
 import org.apache.sis.util.logging.Logging;
+import org.apache.sis.util.logging.WarningListener;
 
 
 /**
- * Base class of NetCDF classes which may produce warnings.
+ * Base class of storage classes which may produce warnings. Warnings are 
emitted by invoking one of the
+ * {@code warning(…)} methods and encapsulated in {@link LogRecord} instances. 
All warnings ultimately
+ * go through the {@link #sendWarning(LogRecord)} method, thus providing a 
single method to override if
+ * some additional handling is needed. When a warning is emitted, there is a 
choice:
+ *
+ * <ul>
+ *   <li>If this {@code WarningProducer} is part of a larger process 
represented by an other {@code WarningProducer}
+ *       instance (i.e. if this instance has a non-null {@link #sink}), then 
the {@link #sendWarning(LogRecord)}
+ *       method will delegate its work to that other {@code WarningProducer} 
instance (the sink).</li>
+ *
+ *   <li>Otherwise, if there is any {@link WarningListener}, then those 
listeners are notified and the warning is
+ *       <strong>not</strong> logged. This case is actually implemented by the 
{@link WarningConsumer} subclass.</li>
+ *
+ *   <li>Otherwise the warning is logged to the logger given to the {@link 
WarningConsumer} constructor.</li>
+ * </ul>
  *
  * @author  Martin Desruisseaux (Geomatys)
  * @since   0.3 (derived from geotk-3.08)
@@ -33,45 +47,56 @@ import org.apache.sis.util.logging.Loggi
  */
 public class WarningProducer {
     /**
-     * Where to send the warnings, or {@code null} if none.
+     * Where to send the warnings, or {@code null} if none. This field is 
always {@code null} for
+     * {@link WarningConsumer}, since the later will redirect warnings to the 
listeners (if any).
      */
-    private final WarningProducer parent;
+    public final WarningProducer sink;
 
     /**
-     * Creates a new instance.
+     * Creates a new instance which will send the warnings to the given object.
      *
-     * @param parent Where to send the warnings, or {@code null} if none.
+     * @param sink Where to send the warnings, or {@code null} if none.
      */
-    protected WarningProducer(final WarningProducer parent) {
-        this.parent = parent;
+    protected WarningProducer(final WarningProducer sink) {
+        this.sink = sink;
     }
 
     /**
-     * Reports a warning represented by the given log record. The current 
implementation just logs the warning.
-     * However if we want to implement a listener mechanism in a future 
version, this could be done here.
+     * Reports a warning represented by the given log record. The default 
implementation delegates to the
+     * {@link #sink} if any, or logs the message to a default logger 
otherwise. The {@link WarningConsumer}
+     * subclass overrides this method in order to notify listeners or use a 
different logger.
      *
      * @param record The warning as a log record.
      */
-    private void warning(final LogRecord record) {
-        if (parent != null) {
-            parent.warning(record);
+    void sendWarning(final LogRecord record) {
+        if (sink != null) {
+            sink.sendWarning(record);
         } else {
-            final Logger logger = Logging.getLogger(WarningProducer.class);
-            record.setLoggerName(logger.getName());
-            logger.log(record);
+            record.setLoggerName("org.apache.sis.storage");
+            Logging.getLogger("org.apache.sis.storage").log(record);
         }
     }
 
     /**
+     * Reports a warning represented by the given message.
+     *
+     * @param methodName The name of the method in which the warning occurred.
+     * @param message    The message to log.
+     */
+    protected final void warning(final String methodName, final String 
message) {
+        final LogRecord record = new LogRecord(Level.WARNING, message);
+        record.setSourceClassName(getClass().getCanonicalName());
+        record.setSourceMethodName(methodName);
+        sendWarning(record);
+    }
+
+    /**
      * Reports a warning represented by the given exception.
      *
      * @param methodName The name of the method in which the warning occurred.
      * @param exception  The exception to log.
      */
     protected final void warning(final String methodName, final Exception 
exception) {
-        final LogRecord record = new LogRecord(Level.WARNING, 
Exceptions.formatChainedMessages(null, null, exception));
-        record.setSourceClassName(getClass().getCanonicalName());
-        record.setSourceMethodName(methodName);
-        warning(record);
+        warning(methodName, Exceptions.formatChainedMessages(null, null, 
exception));
     }
 }

Modified: 
sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStore.java
URL: 
http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStore.java?rev=1487913&r1=1487912&r2=1487913&view=diff
==============================================================================
--- 
sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStore.java
 [UTF-8] (original)
+++ 
sis/branches/JDK7/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStore.java
 [UTF-8] Thu May 30 16:17:57 2013
@@ -16,7 +16,9 @@
  */
 package org.apache.sis.storage;
 
+import java.util.NoSuchElementException;
 import org.opengis.metadata.Metadata;
+import org.apache.sis.util.logging.WarningListener;
 
 
 /**
@@ -40,6 +42,42 @@ public interface DataStore extends AutoC
     Metadata getMetadata() throws DataStoreException;
 
     /**
+     * Adds a listener to be notified when a warning occurred while reading 
from or writing to the storage.
+     * When a warning occurs, there is a choice:
+     *
+     * <ul>
+     *   <li>If this data store has no warning listener, then the warning is 
logged at
+     *       {@link java.util.logging.Level#WARNING}.</li>
+     *   <li>If this data store has at least one warning listener, then all 
listeners are notified
+     *       and the warning is <strong>not</strong> logged by this data store 
instance.</li>
+     * </ul>
+     *
+     * Consider invoking this method in a {@code try} … {@code finally} block 
if the {@code DataStore}
+     * lifetime is longer than the listener lifetime, as below:
+     *
+     * {@preformat java
+     *     datastore.addWarningListener(listener);
+     *     try {
+     *         // Do some work...
+     *     } finally {
+     *         datastore.removeWarningListener(listener);
+     *     }
+     * }
+     *
+     * @param  listener The listener to add.
+     * @throws IllegalArgumentException If the given listener is already 
registered in this data store.
+     */
+    void addWarningListener(WarningListener<? super DataStore> listener) 
throws IllegalArgumentException;
+
+    /**
+     * Removes a previously registered listener.
+     *
+     * @param  listener The listener to remove.
+     * @throws NoSuchElementException If the given listener is not registered 
in this data store.
+     */
+    void removeWarningListener(WarningListener<? super DataStore> listener) 
throws NoSuchElementException;
+
+    /**
      * Closes this data store and releases any underlying resources.
      *
      * @throws DataStoreException If an error occurred while closing this data 
store.


Reply via email to