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:55 UTC
[sis] branch geoapi-4.0 updated (bc9df39 -> fa1e45b)
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 bc9df39 `FeatureSet` use `CRSBuilder` for constructing an horizontal and temporal CRS from the variables. The temporal CRS allows construction of the "datetimes" characteristics on the "trajectory" property.
new 4f89f2a Force the use of `Vector` (i.e. make sure that we don't get a `List<String>`) when reading coordinate variables. If the variable contains texts, those texts will be parsed as numbers.
new fa1e45b Share the same code for enumeration support between `RasterResource` et `FeatureSet`.
The 2 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/internal/netcdf/CRSBuilder.java | 4 +-
.../org/apache/sis/internal/netcdf/FeatureSet.java | 25 +--
.../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, 156 insertions(+), 170 deletions(-)
[sis] 01/02: Force the use of `Vector` (i.e. make sure that we
don't get a `List`) when reading coordinate variables. If the
variable contains texts, those texts will be parsed as numbers.
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 4f89f2abc21a0c4406f27dd9c7e6a79b26d34569
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Wed Oct 28 10:35:17 2020 +0100
Force the use of `Vector` (i.e. make sure that we don't get a `List<String>`) when reading coordinate variables.
If the variable contains texts, those texts will be parsed as numbers.
---
.../java/org/apache/sis/internal/netcdf/FeatureSet.java | 16 ++++++++++------
1 file changed, 10 insertions(+), 6 deletions(-)
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 c6cd189..7af08a2 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
@@ -722,7 +722,7 @@ skip: for (final Variable v : properties) {
*/
private void readNextPage() throws IOException, DataStoreException {
final int length = Math.min(size - currentLowerIndex, PAGE_SIZE);
- read(properties, currentLowerIndex, length, propertyValues);
+ read(properties, propertyIndexOffset, currentLowerIndex, length, propertyValues);
currentUpperIndex = currentLowerIndex + length;
}
@@ -777,8 +777,9 @@ skip: for (final Variable v : properties) {
final int n = dynamicProperties.length;
if (n != 0) {
final List<?>[] target = new List<?>[n];
- read(dynamicProperties, dynamicPropertyPosition, length, target);
- for (int i=getReferencingDimension(true); i<n; i++) {
+ int i = getReferencingDimension(true);
+ read(dynamicProperties, i, dynamicPropertyPosition, length, target);
+ for (/* i = first property after coordinate vectors */; i<n; i++) {
feature.setPropertyValue(dynamicProperties[i].getName(), target[i]);
}
if (isTrajectory) {
@@ -874,12 +875,13 @@ makeGeom: if (!isEmpty) {
* The same sub-region is read for all variables.
*
* @param variables the variables to read.
+ * @param refdim number of referencing dimensions in {@code variables}.
* @param position position of the first value to read in the netCDF variables.
* @param length number of property values to read.
* @param target where to store the results of read operations.
*/
- private void read(final Variable[] variables, final long position, final int length, final List<?>[] target)
- throws IOException, DataStoreException
+ private void read(final Variable[] variables, final int refdim, final long position, final int length,
+ final List<?>[] target) throws IOException, DataStoreException
{
final GridExtent extent = extent(null, 1, position, length);
List<Dimension> textDimensions = null;
@@ -895,8 +897,10 @@ makeGeom: if (!isEmpty) {
textDimensions = dimensions;
}
value = p.readAnyType(textExtent, null);
+ } else if (i >= refdim) {
+ value = p.readAnyType(extent, null); // May be `Vector` or `List<String>`.
} else {
- value = p.readAnyType(extent, null);
+ value = p.read(extent, null); // Force the type to `Vector`.
}
if (p.isEnumeration() && value instanceof Vector) {
final Vector data = (Vector) value;
[sis] 02/02: Share the same code for enumeration support between
`RasterResource` et `FeatureSet`.
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 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) {