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 2020/10/28 13:46:57 UTC

[sis] 02/02: Share the same code for enumeration support between `RasterResource` et `FeatureSet`.

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 fa1e45bb27119fd27294c6c132727944a4bbe916
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Wed Oct 28 14:44:28 2020 +0100

    Share the same code for enumeration support between `RasterResource` et `FeatureSet`.
---
 .../org/apache/sis/internal/netcdf/CRSBuilder.java |   4 +-
 .../org/apache/sis/internal/netcdf/FeatureSet.java |   9 +-
 .../java/org/apache/sis/internal/netcdf/Node.java  |  33 ++--
 .../apache/sis/internal/netcdf/RasterResource.java |  30 ++--
 .../org/apache/sis/internal/netcdf/Variable.java   | 173 +++++++++++----------
 .../sis/internal/netcdf/impl/VariableInfo.java     |  22 ++-
 .../sis/internal/netcdf/ucar/VariableWrapper.java  |  39 +----
 7 files changed, 146 insertions(+), 164 deletions(-)

diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/CRSBuilder.java b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/CRSBuilder.java
index 1329169..782f947 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/CRSBuilder.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/CRSBuilder.java
@@ -176,7 +176,7 @@ abstract class CRSBuilder<D extends Datum, CS extends CoordinateSystem> {
     public static CoordinateReferenceSystem assemble(final Decoder decoder, final Grid grid)
             throws DataStoreException, FactoryException, IOException
     {
-        final List<CRSBuilder<?,?>> builders = new ArrayList<>();
+        final List<CRSBuilder<?,?>> builders = new ArrayList<>(4);
         for (final Axis axis : grid.getAxes(decoder)) {
             dispatch(builders, axis);
         }
@@ -206,7 +206,7 @@ abstract class CRSBuilder<D extends Datum, CS extends CoordinateSystem> {
     static CoordinateReferenceSystem assemble(final Decoder decoder, final Iterable<Variable> axes, final SingleCRS[] time)
             throws DataStoreException, FactoryException, IOException
     {
-        final List<CRSBuilder<?,?>> builders = new ArrayList<>();
+        final List<CRSBuilder<?,?>> builders = new ArrayList<>(4);
         for (final Variable axis : axes) {
             dispatch(builders, new Axis(axis));
         }
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/FeatureSet.java b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/FeatureSet.java
index 7af08a2..fbc5bd4 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/FeatureSet.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/FeatureSet.java
@@ -212,7 +212,7 @@ final class FeatureSet extends DiscreteSampling {
         for (int i = getReferencingDimension(false); i < properties.length; i++) {
             final Variable v = properties[i];
             final Class<?> type;
-            if (v.isEnumeration()) {
+            if (v.getEnumeration() != null) {
                 type = String.class;
             } else {
                 type = v.getDataType().getClass(v.getNumDimensions() > 1);
@@ -251,7 +251,7 @@ final class FeatureSet extends DiscreteSampling {
          */
         for (int i = getReferencingDimension(true); i < dynamicProperties.length; i++) {
             final Variable v = dynamicProperties[i];
-            final Class<?> type = (v.isEnumeration() || v.isString()) ? String.class : Number.class;
+            final Class<?> type = (v.getEnumeration() != null || v.isString()) ? String.class : Number.class;
             describe(v, builder.addAttribute(type).setMaximumOccurs(Integer.MAX_VALUE));
         }
         /*
@@ -902,11 +902,12 @@ makeGeom:   if (!isEmpty) {
                     } else {
                         value = p.read(extent, null);               // Force the type to `Vector`.
                     }
-                    if (p.isEnumeration() && value instanceof Vector) {
+                    final Map<Integer,String> enumeration = p.getEnumeration();
+                    if (enumeration != null && value instanceof Vector) {
                         final Vector data = (Vector) value;
                         final String[] meanings = new String[data.size()];
                         for (int j=0; j<meanings.length; j++) {
-                            String m = p.meaning(data.intValue(j));
+                            String m = enumeration.get(data.intValue(j));
                             meanings[j] = (m != null) ? m : "";
                         }
                         value = Arrays.asList(meanings);
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Node.java b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Node.java
index ea30af4..3210871 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Node.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Node.java
@@ -88,6 +88,8 @@ public abstract class Node extends NamedElement {
 
     /**
      * Returns the value of the given attribute as a non-blank string with leading/trailing spaces removed.
+     * If the attribute value is an array, this method returns a non-null value only if the array contains
+     * a single value (possibly duplicated), ignoring null or empty values.
      *
      * @param  attributeName  the name of the attribute for which to get the value.
      * @return the singleton attribute value, or {@code null} if none, empty, blank or ambiguous.
@@ -103,10 +105,12 @@ public abstract class Node extends NamedElement {
         }
         String singleton = null;
         for (final String c : values) {
-            if (singleton == null) {
-                singleton = c;
-            } else if (!singleton.equals(c)) {
-                return null;
+            if (c != null) {
+                if (singleton == null) {
+                    singleton = c;
+                } else if (!singleton.equals(c)) {
+                    return null;
+                }
             }
         }
         return singleton;
@@ -116,10 +120,14 @@ public abstract class Node extends NamedElement {
      * Returns the values of the given attribute as an array of non-blank texts.
      * If the attribute is not stored as an array in the netCDF file, then this
      * method splits the single {@link String} value around the given separator.
+     * Empty or blank strings are replaced by {@code null} values.
+     *
+     * <p>This method may return a direct reference to an internal array;
+     * <strong>do not modify array content</strong>.</p>
      *
      * @param  attributeName  the name of the attribute for which to get the values.
      * @param  separator      separator to use for splitting a single {@link String} value into a list of values.
-     * @return the attribute values, or {@code null} if none.
+     * @return the attribute values, or {@code null} if none. The array may contain {@code null} elements.
      */
     public final CharSequence[] getAttributeAsStrings(final String attributeName, final char separator) {
         final Object value = getAttributeValue(attributeName);
@@ -138,10 +146,17 @@ public abstract class Node extends NamedElement {
     /**
      * Converts the given value into an array of strings, or returns {@code null}
      * if the given value is not an array or a vector or contains only null values.
+     * The returned array contains no blank strings, but may contain null values.
+     *
+     * <p>This method may return a direct reference to an internal array;
+     * <strong>do not modify array content</strong>.</p>
      */
     private static String[] toArray(final Object value) {
         final String[] array;
-        if (value instanceof Object[]) {
+        if (value instanceof String[]) {
+            // Empty strings are already replaced by null values.
+            return (String[]) value;
+        } else if (value instanceof Object[]) {
             final Object[] values = (Object[]) value;
             array = new String[values.length];
             for (int i=0; i<array.length; i++) {
@@ -161,11 +176,7 @@ public abstract class Node extends NamedElement {
         }
         boolean hasValues = false;
         for (int i=0; i<array.length; i++) {
-            String e = Strings.trimOrNull(array[i]);
-            if (e != null) {
-                hasValues = true;
-                array[i] = e;
-            }
+            hasValues |= (array[i] = Strings.trimOrNull(array[i])) != null;
         }
         return hasValues ? array : null;
     }
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/RasterResource.java b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/RasterResource.java
index e02059c..43f524d 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/RasterResource.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/RasterResource.java
@@ -43,7 +43,6 @@ import org.apache.sis.coverage.grid.GridDerivation;
 import org.apache.sis.coverage.grid.GridRoundingMode;
 import org.apache.sis.coverage.grid.DisjointExtentException;
 import org.apache.sis.coverage.IllegalSampleDimensionException;
-import org.apache.sis.storage.netcdf.AttributeNames;
 import org.apache.sis.storage.DataStoreException;
 import org.apache.sis.storage.DataStoreContentException;
 import org.apache.sis.storage.DataStoreReferencingException;
@@ -52,7 +51,6 @@ import org.apache.sis.storage.Resource;
 import org.apache.sis.math.MathFunctions;
 import org.apache.sis.measure.MeasurementRange;
 import org.apache.sis.measure.NumberRange;
-import org.apache.sis.math.Vector;
 import org.apache.sis.util.Numbers;
 import org.apache.sis.util.CharSequences;
 import org.apache.sis.util.resources.Errors;
@@ -466,7 +464,7 @@ public final class RasterResource extends AbstractGridResource implements Resour
          * by UCAR because we need the range of packed values instead than the range of converted values.
          */
         NumberRange<?> range;
-        if (!createEnumeration(builder, band, index) && (range = band.getValidRange()) != null) try {
+        if (!createEnumeration(builder, band) && (range = band.getValidRange()) != null) try {
             final MathTransform1D mt = band.getTransferFunction().getTransform();
             if (!mt.isIdentity() && range instanceof MeasurementRange<?>) {
                 /*
@@ -583,27 +581,17 @@ public final class RasterResource extends AbstractGridResource implements Resour
      *
      * @param  builder  the builder to use for creating the sample dimension.
      * @param  band     the data for which to create a sample dimension.
-     * @param  index    index in the variable dimension identified by {@link #bandDimension}.
      * @return {@code true} if flag attributes have been found, or {@code false} otherwise.
      */
-    private static boolean createEnumeration(final SampleDimension.Builder builder, final Variable band, final int index) {
-        CharSequence[] names = band.getAttributeAsStrings(AttributeNames.FLAG_NAMES, ' ');
-        if (names == null) {
-            names = band.getAttributeAsStrings(AttributeNames.FLAG_MEANINGS, ' ');
-            if (names == null) return false;
-        }
-        Vector values = band.getAttributeAsVector(AttributeNames.FLAG_VALUES);
-        if (values == null) {
-            values = band.getAttributeAsVector(AttributeNames.FLAG_MASKS);
-            if (values == null) return false;
+    private static boolean createEnumeration(final SampleDimension.Builder builder, final Variable band) {
+        final Map<Integer,String> enumeration = band.getEnumeration();
+        if (enumeration == null) {
+            return false;
         }
-        final int length = values.size();
-        for (int i=0; i<length; i++) {
-            final Number value = values.get(i);
-            final CharSequence name;
-            if (i < names.length) {
-                name = names[i];
-            } else {
+        for (final Map.Entry<Integer,String> entry : enumeration.entrySet()) {
+            final Number value = entry.getKey();
+            CharSequence name = entry.getValue();
+            if (name == null) {
                 name = Vocabulary.formatInternational(Vocabulary.Keys.Unnamed);
             }
             builder.addQualitative(name, value, value);
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Variable.java b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Variable.java
index 1ffddad..133bf00 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Variable.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/Variable.java
@@ -38,7 +38,6 @@ import org.apache.sis.math.MathFunctions;
 import org.apache.sis.measure.NumberRange;
 import org.apache.sis.util.Numbers;
 import org.apache.sis.util.ArraysExt;
-import org.apache.sis.util.CharSequences;
 import org.apache.sis.util.collection.Containers;
 import org.apache.sis.util.collection.WeakHashSet;
 import org.apache.sis.internal.util.Numerics;
@@ -125,11 +124,10 @@ public abstract class Variable extends Node {
      * The {@code flag_meanings} values (used for enumeration values),
      * or {@code null} if this variable is not an enumeration.
      *
-     * @see #isEnumeration()
-     * @see #meaning(int)
-     * @see #setFlagMeanings(Object, Object)
+     * @see #setEnumeration(Map)
+     * @see #getEnumeration()
      */
-    private Map<Integer,String> meanings;
+    private Map<Integer,String> enumeration;
 
     /**
      * The grid associated to this variable, or {@code null} if none or not yet computed.
@@ -192,73 +190,85 @@ public abstract class Variable extends Node {
     }
 
     /**
-     * If {@code flags} is non-null, declares this variable as an enumeration.
-     * This method stores the information needed for {@link #meaning(int)} default implementation.
-     * This method is invoked by subclass constructors for completing {@code Variable} creation.
+     * Initializes the map of enumeration values. If the given map is non-null, then the enumerations are set
+     * to the specified map (by direct reference; the map is not cloned). Otherwise this method auto-detects
+     * if this variable is an enumeration.
      *
-     * @param  flags   the flag meanings as a space-separated string, or {@code null} if none.
-     * @param  values  the flag values as a vector of integer values, or {@code null} if none.
+     * <p>This method is invoked by subclass constructors for completing {@code Variable} creation.
+     * It should not be invoked after creation, for keeping {@link Variable} immutable.</p>
      *
-     * @see #isEnumeration()
-     * @see #meaning(int)
+     * @param  enumeration  the enumeration map, or {@code null} for auto-detection.
+     *
+     * @see #getEnumeration()
      */
-    @SuppressWarnings("null")
-    protected final void setFlagMeanings(final Object flags, final Object values) {
-        if (flags == null) {
-            return;
-        }
-        final String[] labels = (String[]) CharSequences.split(flags.toString(), ' ');
-        int count = labels.length;
-        meanings = new HashMap<>(Containers.hashMapCapacity(count));
-        final Vector numbers;
-        if (values instanceof Vector) {
-            numbers = (Vector) values;
-            final int n = numbers.size();
-            if (n != count) {
-                warning(Variable.class, "setFlagMeanings", Resources.Keys.MismatchedAttributeLength_5,
-                        getName(), AttributeNames.FLAG_VALUES, AttributeNames.FLAG_MEANINGS, n, count);
-                if (n < count) count = n;
+    @SuppressWarnings("AssignmentToCollectionOrArrayFieldFromParameter")
+    protected final void setEnumeration(Map<Integer,String> enumeration) {
+        if (enumeration == null) {
+            String srcLabels, srcNumbers;           // For more accurate message in case of warning.
+            CharSequence[] labels = getAttributeAsStrings(srcLabels = AttributeNames.FLAG_NAMES, ' ');
+            if (labels == null) {
+                labels = getAttributeAsStrings(srcLabels = AttributeNames.FLAG_MEANINGS, ' ');
+                if (labels == null) return;
             }
-        } else {
-            numbers = Vector.createSequence(0, 1, count);
-            warning(Variable.class, "setFlagMeanings", Resources.Keys.MissingVariableAttribute_3,
-                    getFilename(), getName(), AttributeNames.FLAG_VALUES);
-        }
-        /*
-         * Copy (numbers, labels) entries in an HashMap with keys converted to 32-bits signed integer.
-         * If a key can not be converted, we will log a warning after all errors have been collected
-         * in order to produce only one log message. We put a limit on the amount of reported errors
-         * for avoiding to flood the logger.
-         */
-        Exception     error    = null;
-        StringBuilder invalids = null;
-        for (int i=0; i<count; i++) try {
-            meanings.merge(numbers.intValue(i), labels[i], (o,n) -> o + " | " + n);
-        } catch (NumberFormatException | ArithmeticException e) {
-            if (error == null) {
-                error = e;
-                invalids = new StringBuilder();
+            Vector numbers = getAttributeAsVector(srcNumbers = AttributeNames.FLAG_VALUES);
+            if (numbers == null) {
+                numbers = getAttributeAsVector(srcNumbers = AttributeNames.FLAG_MASKS);
+            }
+            int count = labels.length;
+            if (numbers != null) {
+                final int n = numbers.size();
+                if (n != count) {
+                    warning(Variable.class, "setEnumeration", Resources.Keys.MismatchedAttributeLength_5,
+                            getName(), srcNumbers, srcLabels, n, count);
+                    if (n < count) count = n;
+                }
             } else {
-                final int length = invalids.length();
-                final boolean tooManyErrors = (length > 100);                   // Arbitrary limit.
-                if (tooManyErrors && invalids.charAt(length - 1) == '…') {
-                    continue;
+                numbers = Vector.createSequence(0, 1, count);
+                warning(Variable.class, "setEnumeration", Resources.Keys.MissingVariableAttribute_3,
+                        getFilename(), getName(), AttributeNames.FLAG_VALUES);
+            }
+            /*
+             * Copy (numbers, labels) entries in an HashMap with keys converted to 32-bits signed integer.
+             * If a key can not be converted, we will log a warning after all errors have been collected
+             * in order to produce only one log message. We put a limit on the amount of reported errors
+             * for avoiding to flood the logger.
+             */
+            Exception     error    = null;
+            StringBuilder invalids = null;
+            enumeration = new HashMap<>(Containers.hashMapCapacity(count));
+            for (int i=0; i<count; i++) try {
+                final CharSequence label = labels[i];
+                if (label != null) {
+                    enumeration.merge(numbers.intValue(i), label.toString(), (o,n) -> {
+                        return o.equals(n) ? o : o + " | " + n;
+                    });
                 }
-                error.addSuppressed(e);
-                invalids.append(", ");
-                if (tooManyErrors) {
-                    invalids.append('…');
-                    continue;
+            } catch (NumberFormatException | ArithmeticException e) {
+                if (error == null) {
+                    error = e;
+                    invalids = new StringBuilder();
+                } else {
+                    final int length = invalids.length();
+                    final boolean tooManyErrors = (length > 100);                   // Arbitrary limit.
+                    if (tooManyErrors && invalids.charAt(length - 1) == '…') {
+                        continue;
+                    }
+                    error.addSuppressed(e);
+                    invalids.append(", ");
+                    if (tooManyErrors) {
+                        invalids.append('…');
+                        continue;
+                    }
                 }
+                invalids.append(numbers.stringValue(i));
+            }
+            if (invalids != null) {
+                error(Variable.class, "setEnumeration", error,
+                      Errors.Keys.CanNotConvertValue_2, invalids, numbers.getElementType());
             }
-            invalids.append(numbers.stringValue(i));
-        }
-        if (invalids != null) {
-            error(Variable.class, "setFlagMeanings", error,
-                  Errors.Keys.CanNotConvertValue_2, invalids, numbers.getElementType());
         }
-        if (meanings.isEmpty()) {
-            meanings = null;
+        if (!enumeration.isEmpty()) {
+            this.enumeration = enumeration;
         }
     }
 
@@ -458,17 +468,6 @@ public abstract class Variable extends Node {
     }
 
     /**
-     * Returns {@code true} if this variable is an enumeration.
-     *
-     * @return whether this variable is an enumeration.
-     *
-     * @see #meaning(int)
-     */
-    protected boolean isEnumeration() {
-        return meanings != null;
-    }
-
-    /**
      * Returns whether this variable can grow. A variable is unlimited if at least one of its dimension is unlimited.
      * In netCDF 3 classic format, only the first dimension can be unlimited.
      *
@@ -871,6 +870,20 @@ public abstract class Variable extends Node {
     }
 
     /**
+     * Returns enumeration values (keys) and their meanings (values), or {@code null} if this
+     * variable is not an enumeration. This method returns a direct reference to internal map
+     * (no clone, no unmodifiable wrapper); <strong>Do not modify the returned map.</strong>
+     *
+     * @return the ordinals and values associated to ordinals, or {@code null} if none.
+     *
+     * @see #setEnumeration(Map)
+     */
+    @SuppressWarnings("ReturnOfCollectionOrArrayField")
+    final Map<Integer,String> getEnumeration() {
+        return enumeration;
+    }
+
+    /**
      * Returns all no-data values declared for this variable, or an empty map if none.
      * The map keys are the no-data values (pad sample values or missing sample values).
      * The map values can be either {@link String} or {@link org.opengis.util.InternationalString} values
@@ -1243,18 +1256,6 @@ public abstract class Variable extends Node {
     }
 
     /**
-     * Returns the meaning of the given ordinal value, or {@code null} if none.
-     * Callers must have verified that {@link #isEnumeration()} returned {@code true}
-     * before to invoke this method
-     *
-     * @param  ordinal  the ordinal of the enumeration for which to get the value.
-     * @return the value associated to the given ordinal, or {@code null} if none.
-     */
-    protected String meaning(final int ordinal) {
-        return meanings.get(ordinal);
-    }
-
-    /**
      * Returns a string representation of this variable for debugging purpose.
      *
      * @return a string representation of this variable.
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/VariableInfo.java b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/VariableInfo.java
index 1746fa7..f067fc3 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/VariableInfo.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/VariableInfo.java
@@ -131,6 +131,8 @@ final class VariableInfo extends Variable implements Comparable<VariableInfo> {
      *   <li>{@link String} if the attribute contains a single textual value.</li>
      *   <li>{@link Number} if the attribute contains a single numerical value.</li>
      *   <li>{@link Vector} if the attribute contains many numerical values.</li>
+     *   <li>{@code String[]} if the attribute is one of predefined attributes
+     *       for which many text values are expected (e.g. an enumeration).</li>
      * </ul>
      *
      * If the value is a {@code String}, then leading and trailing spaces and control characters
@@ -247,12 +249,22 @@ final class VariableInfo extends Variable implements Comparable<VariableInfo> {
             isCoordinateSystemAxis = dimensions[0].name.equals(value);
         }
         /*
-         * Verify if this variable is an enumeration. If yes, we remove the attributes that define the
-         * enumeration since those attributes may be verbose and "pollute" the variable definition.
+         * Rewrite the enumeration names as an array for avoiding to parse the string if this information
+         * is asked twice (e.g. in `setEnumeration(…)` and in `MetadataReader`). Note that there is no need
+         * to perform similar operation for vectors of numbers.
          */
-        if (!attributes.isEmpty()) {    // For avoiding UnsupportedOperationException if unmodifiable map.
-            setFlagMeanings(attributes.remove(AttributeNames.FLAG_MEANINGS),
-                            attributes.remove(AttributeNames.FLAG_VALUES));
+        split(AttributeNames.FLAG_NAMES);
+        split(AttributeNames.FLAG_MEANINGS);
+        setEnumeration(null);
+    }
+
+    /**
+     * Splits a space-separated attribute value into an array of strings.
+     */
+    private void split(final String attribute) {
+        final CharSequence[] values = getAttributeAsStrings(attribute, ' ');
+        if (values != null) {
+            attributes.put(attribute, values);
         }
     }
 
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/VariableWrapper.java b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/VariableWrapper.java
index 82b6912..b277617 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/VariableWrapper.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/VariableWrapper.java
@@ -16,6 +16,7 @@
  */
 package org.apache.sis.internal.netcdf.ucar;
 
+import java.util.Map;
 import java.util.List;
 import java.util.Collection;
 import java.io.File;
@@ -49,7 +50,6 @@ import org.apache.sis.internal.util.UnmodifiableArrayList;
 import org.apache.sis.internal.util.Strings;
 import org.apache.sis.internal.jdk9.JDK9;
 import org.apache.sis.storage.DataStoreException;
-import org.apache.sis.storage.netcdf.AttributeNames;
 import org.apache.sis.measure.MeasurementRange;
 import org.apache.sis.measure.NumberRange;
 import org.apache.sis.measure.Units;
@@ -81,11 +81,6 @@ final class VariableWrapper extends Variable {
     private final VariableIF raw;
 
     /**
-     * {@code true} if this variable is an enumeration.
-     */
-    private final boolean isEnumeration;
-
-    /**
      * Creates a new variable wrapping the given netCDF interface.
      */
     VariableWrapper(final Decoder decoder, VariableIF v) {
@@ -102,13 +97,11 @@ final class VariableWrapper extends Variable {
          * If the UCAR library recognizes this variable as an enumeration, we will use UCAR services.
          * Only if UCAR did not recognized the enumeration, fallback on Apache SIS implementation.
          */
+        Map<Integer,String> enumeration = null;
         if (variable.getDataType().isEnum() && (variable instanceof ucar.nc2.Variable)) {
-            isEnumeration = true;
-        } else {
-            setFlagMeanings(getAttributeAsString(AttributeNames.FLAG_MEANINGS),
-                            getAttributeAsVector(AttributeNames.FLAG_VALUES));
-            isEnumeration = super.isEnumeration();
+            enumeration = ((ucar.nc2.Variable) variable).getEnumTypedef().getMap();
         }
+        setEnumeration(enumeration);        // Use SIS fallback if `enumeration` is null.
     }
 
     /**
@@ -226,16 +219,6 @@ final class VariableWrapper extends Variable {
     }
 
     /**
-     * Returns {@code true} if this variable is an enumeration.
-     *
-     * @see #meaning(int)
-     */
-    @Override
-    protected boolean isEnumeration() {
-        return isEnumeration;
-    }
-
-    /**
      * Returns whether this variable can grow. A variable is unlimited if at least one of its dimension is unlimited.
      */
     @Override
@@ -637,20 +620,6 @@ final class VariableWrapper extends Variable {
     }
 
     /**
-     * Returns the meaning of the given ordinal value, or {@code null} if none.
-     * Callers must have verified that {@link #isEnumeration()} returned {@code true}
-     * before to invoke this method
-     *
-     * @param  ordinal  the ordinal of the enumeration for which to get the value.
-     * @return the value associated to the given ordinal, or {@code null} if none.
-     */
-    @Override
-    protected String meaning(final int ordinal) {
-        return super.isEnumeration() ? super.meaning(ordinal)
-                : ((ucar.nc2.Variable) variable).lookupEnumString(ordinal);
-    }
-
-    /**
      * Returns {@code true} if this Apache SIS variable is a wrapper for the given UCAR variable.
      */
     final boolean isWrapperFor(final VariableIF v) {