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/02/21 12:04:33 UTC

[sis] 03/04: Merge branch 'feat/netcdf-attribute-names' of https://github.com/alexismanin/sis into geoapi-4.0. Changes have been applied regarding methods order, documentation, and where the search path is set.

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 a972b833d92ed1675032770e5204575ae23a9ecc
Merge: f8a6001 bc1c422
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Thu Feb 21 12:58:37 2019 +0100

    Merge branch 'feat/netcdf-attribute-names' of https://github.com/alexismanin/sis into geoapi-4.0.
    Changes have been applied regarding methods order, documentation, and where the search path is set.

 .../org/apache/sis/internal/netcdf/Convention.java | 57 +++++++++++++++++++++-
 .../org/apache/sis/internal/netcdf/Decoder.java    |  4 +-
 .../sis/internal/netcdf/impl/ChannelDecoder.java   | 30 ++++++++++--
 .../sis/internal/netcdf/ucar/DecoderWrapper.java   | 17 ++++++-
 .../apache/sis/storage/netcdf/MetadataReader.java  | 15 ++----
 5 files changed, 102 insertions(+), 21 deletions(-)

diff --cc storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Convention.java
index fc79692,ae501ef..01baef3
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Convention.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Convention.java
@@@ -66,6 -69,6 +66,14 @@@ public class Convention 
      private static final Convention DEFAULT = new Convention();
  
      /**
++     * Names of groups where to search for metadata, in precedence order.
++     * The {@code null} value stands for global attributes.
++     *
++     * <p>REMINDER: if modified, update {@link org.apache.sis.storage.netcdf.MetadataReader} class javadoc too.</p>
++     */
++    private static final String[] SEARCH_PATH = {"NCISOMetadata", "CFMetadata", null, "THREDDSMetadata"};
++
++    /**
       * Names of attributes where to fetch minimum and maximum sample values, in preference order.
       *
       * @see #getValidValues(Variable)
@@@ -85,35 -96,66 +93,80 @@@
  
      /**
       * Finds the convention to apply to the file opened by the given decoder, or {@code null} if none.
++     * This method does not change the state of the given {@link Decoder}.
++     *
++     * @todo this method is temporarily synchronized because of a {@link java.util.ServiceLoader} bug in JDK 8,
++     *       which does not support the use of a new {@link Iterator} while another iteration is in progress,
++     *       even if they are in the same thread. We will remove this synchronization in JDK9 if that bug is fixed.
++     *       Only the {@code synchronized (AVAILABLES)} statements should stay.
       */
 -    static Convention find(final Decoder decoder) {
 +    static synchronized Convention find(final Decoder decoder) {
          final Iterator<Convention> it;
          Convention c;
          synchronized (AVAILABLES) {
              it = AVAILABLES.iterator();
-             if (!it.hasNext()) return DEFAULT;
++            if (!it.hasNext()) {
++                return DEFAULT;
++            }
 +            c = it.next();
          }
 -
 -        do {
++        /*
++         * We want the call to isApplicableTo(…) to be outside the synchronized block in order to avoid contentions.
++         * This is also a safety against dead locks if that method acquire other locks. Only Iterator methods should
++         * be invoked inside the synchronized block.
++         */
 +        while (!c.isApplicableTo(decoder)) {
              synchronized (AVAILABLES) {
-                 if (!it.hasNext()) return DEFAULT;
+                 if (it.hasNext()) {
 -                    c = it.next();
 -                } else {
 -                    decoder.setSearchPath(DEFAULT.getSearchPath());
 -                    return DEFAULT;
++                    c = DEFAULT;
++                    break;
+                 }
 +                c = it.next();
              }
 -
 -            decoder.setSearchPath(c.getSearchPath());
 -
 -        } while (!c.isApplicableTo(decoder));
 -
 +        }
          return c;
      }
  
      /**
 -     * Specify a list of groups to focus on when searching for attribute values.
 +     * Detects if this set of conventions applies to the given netCDF file.
++     * This method shall not change the state of the given {@link Decoder}.
       *
 -     * @return Groups we should search in for global data attributes. Never null, never empty, but can contain null
 -     * values to specify root as search path.
 +     * @param  decoder  the netCDF file to test.
 +     * @return {@code true} if this set of conventions can apply.
       */
 -    public final String[] getSearchPath() {
 -        String[] paths = getSearchPathImpl();
 -        if (paths == null || paths.length < 1) {
 -            return new String[1];
 -        }
 -
 -        return Arrays.copyOf(paths, paths.length);
 +    protected boolean isApplicableTo(final Decoder decoder) {
 +        return false;
      }
  
      /**
 -     * An abstraction over {@link #getSearchPath() }, allowing subclasses to specify their own groups. The abstraction
 -     * is needed as a control mechanism to avoid invalid paths (null or empty).
 -     * @return Groups to look at for attribute values.
++     * Specifies a list of groups where to search for named attributes, in preference order.
++     * The {@code null} name stands for the root group.
++     *
++     * @return  name of groups where to search in for global attributes, in preference order.
++     *          Never null, never empty, but can contain null values to specify root as search path.
++     *
++     * @see Decoder#setSearchPath(String...)
+      */
 -    protected String[] getSearchPathImpl() {
 -        return SEARCH_PATH;
++    public String[] getSearchPath() {
++        return SEARCH_PATH.clone();
+     }
+ 
+     /**
 -     * Detects if this set of conventions applies to the given netCDF file.
++     * Returns the name of an attribute in this convention which is equivalent to the attribute of given name in CF-convention.
++     * The given parameter is a name from <cite>CF conventions</cite> or from <cite>Attribute Convention for Dataset Discovery
++     * (ACDD)</cite>. Some of those attribute names are listed in the {@link org.apache.sis.storage.netcdf.AttributeNames} class.
+      *
 -     * @param  decoder  the netCDF file to test.
 -     * @return {@code true} if this set of conventions can apply.
++     * <p>The default implementation returns {@code name} unchanged.</p>
++     *
++     * @param  name  an attribute name from CF or ACDD convention.
++     * @return the attribute name expected to be found in a netCDF file structured according this {@code Convention}.
++     *         If this convention does not know about attribute of the given name, then {@code name} is returned unchanged.
+      */
 -    protected boolean isApplicableTo(final Decoder decoder) {
 -        return false;
++    public String mapAttributeName(final String name) {
++        return name;
+     }
+ 
+     /**
       * Returns whether the given variable is used as a coordinate system axis, a coverage or something else.
       * In particular this method shall return {@link VariableRole#AXIS} if the given variable seems to be a
       * coordinate system axis instead than the actual data. By netCDF convention, coordinate system axes
diff --cc storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Decoder.java
index f2bb45e,f2bb45e..d4d0cc4
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Decoder.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Decoder.java
@@@ -159,9 -159,9 +159,11 @@@ public abstract class Decoder extends R
  
      /**
       * Defines the groups where to search for named attributes, in preference order.
--     * The {@code null} group name stands for the global attributes.
++     * The {@code null} group name stands for attributes in the root group.
       *
       * @param  groupNames  the name of the group where to search, in preference order.
++     *
++     * @see Convention#getSearchPath()
       */
      public abstract void setSearchPath(String... groupNames);
  
diff --cc storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/ChannelDecoder.java
index f135d66,ac065f0..42f0d92
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/ChannelDecoder.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/ChannelDecoder.java
@@@ -724,7 -725,7 +724,9 @@@ public final class ChannelDecoder exten
      }
  
      /**
--     * Returns the netCDF attribute of the given name, or {@code null} if none.
++     * Returns the netCDF attribute of the given name, or {@code null} if none. This method is invoked
++     * for every global attributes to be read by this class (but not {@linkplain VariableInfo variable}
++     * attributes), thus providing a single point where we can filter the attributes to be read.
       * The {@code name} argument is typically (but is not restricted to) one of the constants
       * defined in the {@link org.apache.sis.storage.netcdf.AttributeNames} class.
       *
@@@ -736,12 -737,21 +738,30 @@@
      private Object findAttribute(final String name) {
          Object value = attributeMap.get(name);
          if (value == null && name != null) {
--            final String lower = name.toLowerCase(NAME_LOCALE);
--            // Identity comparison is ok since following check is only an optimization for a common case.
--            if (lower != name) {
--                value = attributeMap.get(lower);
 -            }
 -        }
 -
 -        // Check if a custom convention could be applied.
 -        if (value == null && convention() != null) {
 -            final String mappedName = convention().mapAttributeName(name);
 -            if (!Objects.equals(name, mappedName)) {
 -                value = findAttribute(mappedName);
++            /*
++             * If no value were found for the given name, tries the following alternatives:
++             *
++             *   - Same name but in lower cases.
++             *   - Alternative name specific to the non-standard convention used by current file.
++             *   - Same alternative name but in lower cases.
++             *
++             * Identity comparisons performed between String instances below are okay since they
++             * are only optimizations for skipping calls to Map.get(Object) in common cases.
++             */
++            String lower = name.toLowerCase(NAME_LOCALE);
++            if (lower == name || (value = attributeMap.get(lower)) == null) {
++                final String mappedName = convention().mapAttributeName(name);
++                if (mappedName != name) {
++                    value = attributeMap.get(mappedName);
++                    if (value == null) {
++                        lower = name.toLowerCase(NAME_LOCALE);
++                        if (lower != mappedName) {
++                            value = attributeMap.get(lower);
++                        }
++                    }
++                }
              }
          }
 -
          return value;
      }
  
diff --cc storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/DecoderWrapper.java
index f2b264d,6df5a66..5dd644e
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/DecoderWrapper.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/DecoderWrapper.java
@@@ -224,7 -226,7 +224,7 @@@ public final class DecoderWrapper exten
       * Returns the netCDF attribute of the given name in the given group, or {@code null} if none.
       * This method is invoked for every global and group attributes to be read by this class (but
       * not {@linkplain ucar.nc2.VariableSimpleIF variable} attributes), thus providing a single point
--     * where we can filter the attributes to be read - if we want to do that in a future version.
++     * where we can filter the attributes to be read.
       *
       * <p>The {@code name} argument is typically (but is not restricted too) one of the constants
       * defined in the {@link org.apache.sis.storage.netcdf.AttributeNames} class.</p>
@@@ -234,7 -236,17 +234,20 @@@
       * @return the attribute, or {@code null} if none.
       */
      private Attribute findAttribute(final Group group, final String name) {
-         return (group != null) ? group.findAttributeIgnoreCase(name) : file.findGlobalAttributeIgnoreCase(name);
 -        Attribute value = (group != null) ?
 -                group.findAttributeIgnoreCase(name) : file.findGlobalAttributeIgnoreCase(name);
 -
 -        if (value == null && convention() != null) {
 -            String mappedName = convention().mapAttributeName(name);
 -            if (!Objects.equals(name, mappedName)) {
 -                value = findAttribute(group, mappedName);
++        Attribute value = (group != null) ? group.findAttributeIgnoreCase(name)
++                                          : file.findGlobalAttributeIgnoreCase(name);
++        if (value == null) {
++            final String mappedName = convention().mapAttributeName(name);
++            /*
++             * Identity check between String instances below is okay
++             * since this is only an optimization for a common case.
++             */
++            if (mappedName != name) {
++                value = (group != null) ? group.findAttributeIgnoreCase(mappedName)
++                                        : file.findGlobalAttributeIgnoreCase(mappedName);
+             }
+         }
 -
+         return value;
      }
  
      /**
diff --cc storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/MetadataReader.java
index bf3e41b,d835802..ca8f55a
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/MetadataReader.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/MetadataReader.java
@@@ -110,6 -110,6 +110,7 @@@ import static org.apache.sis.internal.u
   *
   * @author  Martin Desruisseaux (Geomatys)
   * @author  Thi Phuong Hao Nguyen (VNSC)
++ * @author  Alexis Manin (Geomatys)
   * @version 1.0
   * @since   0.3
   * @module
@@@ -159,8 -151,8 +152,8 @@@ final class MetadataReader extends Meta
      private final Decoder decoder;
  
      /**
--     * The actual search path, as a subset of {@link #SEARCH_PATH} with only the name of the groups
--     * which have been found in the NeCDF file.
++     * The actual search path, as a subset of {@link org.apache.sis.internal.netcdf.Convention#SEARCH_PATH}
++     * with only the name of the groups which have been found in the NeCDF file.
       */
      private final String[] searchPath;
  
@@@ -194,7 -186,6 +187,7 @@@
       */
      MetadataReader(final Decoder decoder) {
          this.decoder = decoder;
-         decoder.setSearchPath(SEARCH_PATH);
++        decoder.setSearchPath(decoder.convention().getSearchPath());
          searchPath = decoder.getSearchPath();
      }