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 2016/10/07 21:35:45 UTC
svn commit: r1763840 [11/12] - in /sis/branches/JDK7: ./ core/
core/sis-feature/src/main/java/org/apache/sis/feature/
core/sis-feature/src/main/java/org/apache/sis/feature/builder/
core/sis-feature/src/main/java/org/apache/sis/internal/feature/ core/si...
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=1763840&r1=1763839&r2=1763840&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] Fri Oct 7 21:35:43 2016
@@ -18,24 +18,29 @@ package org.apache.sis.internal.netcdf.i
import java.util.Set;
import java.util.Map;
+import java.util.AbstractMap;
import java.util.LinkedHashSet;
import java.util.LinkedHashMap;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Arrays;
+import java.util.ArrayList;
import java.util.Date;
import java.util.Locale;
import java.util.regex.Pattern;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
-import java.lang.reflect.Array;
import javax.measure.converter.UnitConverter;
import javax.measure.converter.ConversionException;
import org.opengis.parameter.InvalidParameterCardinalityException;
+import org.apache.sis.internal.netcdf.DataType;
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.NamedElement;
+import org.apache.sis.internal.netcdf.DiscreteSampling;
+import org.apache.sis.internal.netcdf.Resources;
import org.apache.sis.internal.storage.ChannelDataInput;
import org.apache.sis.internal.util.CollectionsExt;
import org.apache.sis.internal.util.StandardDateFormat;
@@ -45,13 +50,12 @@ import org.apache.sis.util.iso.DefaultNa
import org.apache.sis.util.resources.Errors;
import org.apache.sis.util.resources.Vocabulary;
import org.apache.sis.util.logging.WarningListeners;
-import org.apache.sis.util.ArraysExt;
import org.apache.sis.util.Debug;
import org.apache.sis.measure.Units;
+import ucar.nc2.constants.CF;
// Branch-dependent imports
import org.apache.sis.internal.jdk8.JDK8;
-import org.apache.sis.internal.jdk8.Function;
import org.apache.sis.internal.jdk8.DateTimeException;
@@ -100,7 +104,7 @@ public final class ChannelDecoder extend
*
* @see #findAttribute(String)
*/
- private static final Locale NAME_LOCALE = Locale.US;
+ static final Locale NAME_LOCALE = Locale.US;
/**
* The pattern to use for separating the component of a time unit.
@@ -169,14 +173,28 @@ public final class ChannelDecoder extend
*
* @see #getVariables()
*/
- private final VariableInfo[] variables;
+ final VariableInfo[] variables;
+
+ /**
+ * Same as {@link #variables}, but as a map for faster search.
+ */
+ private final Map<String,VariableInfo> variableMap;
/**
* The attributes found in the NetCDF file.
+ * Values in this map give directly the attribute value (there is no {@code Attribute} object).
*
* @see #findAttribute(String)
*/
- private final Map<String,Attribute> attributeMap;
+ private final Map<String,Object> attributeMap;
+
+ /**
+ * All dimensions in the NetCDF files.
+ *
+ * @see #readDimensions(int)
+ * @see #findDimension(String)
+ */
+ private Map<String,Dimension> dimensionMap;
/**
* The grid geometries, created when first needed.
@@ -187,7 +205,16 @@ public final class ChannelDecoder extend
/**
* Creates a new decoder for the given file.
- * This constructor parses immediately the header.
+ * This constructor parses immediately the header, which shall have the following structure:
+ *
+ * <ul>
+ * <li>Magic number: 'C','D','F'</li>
+ * <li>Version number: 1 or 2</li>
+ * <li>Number of records</li>
+ * <li>List of NetCDF dimensions (see {@link #readDimensions(int)})</li>
+ * <li>List of global attributes (see {@link #readAttributes(int)})</li>
+ * <li>List of variables (see {@link #readVariables(int, Dimension[])})</li>
+ * </ul>
*
* @param listeners where to send the warnings.
* @param input the channel and the buffer from where data are read.
@@ -205,7 +232,7 @@ public final class ChannelDecoder extend
*/
int version = input.readInt();
if ((version & 0xFFFFFF00) != MAGIC_NUMBER) {
- throw new DataStoreContentException(errors().getString(Errors.Keys.UnexpectedFileFormat_2, "NetCDF", input.filename));
+ throw new DataStoreContentException(errors().getString(Errors.Keys.UnexpectedFileFormat_2, "NetCDF", getFilename()));
}
/*
* Check the version number.
@@ -214,7 +241,7 @@ public final class ChannelDecoder extend
switch (version) {
case 1: is64bits = false; break;
case 2: is64bits = true; break;
- default: throw new DataStoreContentException(errors().getString(Errors.Keys.UnsupportedVersion_1, version));
+ default: throw new DataStoreContentException(errors().getString(Errors.Keys.UnsupportedFormatVersion_2, "NetCDF", version));
// If more cases are added, remember to increment the MAX_VERSION constant.
}
numrecs = input.readInt();
@@ -222,25 +249,30 @@ public final class ChannelDecoder extend
* Read the dimension, attribute and variable declarations. We expect exactly 3 lists,
* where any of them can be flagged as absent by a long (64 bits) 0.
*/
- Dimension[] dimensions = null;
- VariableInfo[] variables = null;
- Attribute[] attributes = null;
+ Dimension[] dimensions = null;
+ VariableInfo[] variables = null;
+ Map<String,Object> attributes = null;
for (int i=0; i<3; i++) {
final long tn = input.readLong(); // Combination of tag and nelems
if (tn != 0) {
final int tag = (int) (tn >>> Integer.SIZE);
final int nelems = (int) tn;
ensureNonNegative(nelems, tag);
- switch (tag) {
- case DIMENSION: dimensions = readDimensions(nelems); break;
- case VARIABLE: variables = readVariables (nelems, dimensions); break;
- case ATTRIBUTE: attributes = readAttributes(nelems); break;
- default: throw malformedHeader();
+ try {
+ switch (tag) {
+ case DIMENSION: dimensions = readDimensions(nelems); break;
+ case VARIABLE: variables = readVariables (nelems, dimensions); break;
+ case ATTRIBUTE: attributes = readAttributes(nelems); break;
+ default: throw malformedHeader();
+ }
+ } catch (InvalidParameterCardinalityException e) {
+ throw new DataStoreContentException(e.getLocalizedMessage());
}
}
}
- attributeMap = toMap(attributes, Attribute.NAME_FUNCTION);
+ attributeMap = attributes;
this.variables = variables;
+ variableMap = NamedElement.toCaseInsensitiveNameMap(variables, NAME_LOCALE);
}
/**
@@ -266,16 +298,25 @@ public final class ChannelDecoder extend
*
* @return the localized error resource bundle.
*/
- private Errors errors() {
+ final Errors errors() {
return Errors.getResources(listeners.getLocale());
}
/**
+ * Returns the NetCDF-specific resource bundle for the locale given by {@link #getLocale()}.
+ *
+ * @return the localized error resource bundle.
+ */
+ final Resources resources() {
+ return Resources.forLocale(listeners.getLocale());
+ }
+
+ /**
* Returns an exception for a malformed header. This is used only after we have determined
* that the file should be a NetCDF one, but we found some inconsistency or unknown tags.
*/
private DataStoreException malformedHeader() {
- return new DataStoreContentException(errors().getString(Errors.Keys.CanNotParseFile_2, "NetCDF", input.filename));
+ return new DataStoreContentException(errors().getString(Errors.Keys.CanNotParseFile_2, "NetCDF", getFilename()));
}
/**
@@ -284,36 +325,27 @@ public final class ChannelDecoder extend
private void ensureNonNegative(final int nelems, final int tag) throws DataStoreException {
if (nelems < 0) {
throw new DataStoreContentException(errors().getString(Errors.Keys.NegativeArrayLength_1,
- input.filename + DefaultNameSpace.DEFAULT_SEPARATOR + tagName(tag)));
+ getFilename() + DefaultNameSpace.DEFAULT_SEPARATOR + tagName(tag)));
}
}
/**
- * Makes sure that the buffer contains at least <var>n</var> remaining elements of the given size.
- * If the buffer does not have enough bytes available, more bytes will be read from the channel.
- * If the buffer capacity is not sufficient for reading the given amount of data, an exception
- * is thrown.
+ * Aligns position in the stream after reading the given amount of bytes.
+ * This method should be invoked only for {@link DataType#BYTE} and {@link DataType#CHAR}.
*
- * <p>The NetCDF format add padding after bytes, characters and short integers in order to align
- * the data on multiple of 4 bytes. This method adds such padding to the number of bytes to read.</p>
+ * <p>The NetCDF format adds padding after bytes, characters and short integers in order to align the data
+ * on multiple of 4 bytes. This method is used for adding such padding to the number of bytes to read.</p>
*
- * @param n the number of elements to read.
- * @param dataSize the size of each element, in bytes.
- * @param name the name of the element to read, used only in case of error for formatting the message.
- * @return the number of bytes to read, rounded to the next multiple of 4.
+ * @param length number of byte reads.
+ * @throws IOException if an error occurred while skipping bytes.
*/
- private int ensureBufferContains(final int n, final int dataSize, String name) throws IOException, DataStoreException {
- // (n+3) & ~3 is a trick for rounding 'n' to the next multiple of 4.
- final long size = ((n & 0xFFFFFFFFL) * dataSize + 3) & ~3;
- if (size > input.buffer.capacity()) {
- name = input.filename + DefaultNameSpace.DEFAULT_SEPARATOR + name;
- final Errors errors = errors();
- throw new DataStoreContentException(n < 0 ?
- errors.getString(Errors.Keys.NegativeArrayLength_1, name) :
- errors.getString(Errors.Keys.ExcessiveListSize_2, name, n));
+ private void align(int length) throws IOException {
+ length &= 3;
+ if (length != 0) {
+ length = 4 - length;
+ input.ensureBufferContains(length);
+ input.buffer.position(input.buffer.position() + length);
}
- input.ensureBufferContains((int) size);
- return (int) size;
}
/**
@@ -324,17 +356,18 @@ public final class ChannelDecoder extend
}
/**
- * Reads a string from the buffer in the {@value #NAME_ENCODING}. This is suitable for the dimension,
+ * Reads a string from the channel in the {@value #NAME_ENCODING}. This is suitable for the dimension,
* variable and attribute names in the header. Note that attribute value may have a different encoding.
+ *
+ * @param length number of bytes to read. The number of bytes actually read may be greater.
*/
private String readName() throws IOException, DataStoreException {
final int length = input.readInt();
if (length < 0) {
throw malformedHeader();
}
- final int size = ensureBufferContains(length, 1, "<name>");
final String text = input.readString(length, NAME_ENCODING);
- input.buffer.position(input.buffer.position() + (size - length));
+ align(length);
return text;
}
@@ -348,56 +381,72 @@ public final class ChannelDecoder extend
*
* @return the value, or {@code null} if it was an empty string or an empty array.
*/
- private Object readValues(final String name, final int type, final int length) throws IOException, DataStoreException {
+ private Object readValues(final DataType type, final int length) throws IOException, DataStoreException {
if (length == 0) {
return null;
}
- final ByteBuffer buffer = input.buffer;
- final int size = ensureBufferContains(length, VariableInfo.sizeOf(type), name);
- final int position = buffer.position(); // Must be after 'ensureBufferContains'
- final Object result;
+ if (length < 0) {
+ throw malformedHeader();
+ }
+ if (length == 1) {
+ switch (type) {
+ case BYTE: {final byte v = input.readByte(); align(1); return v;}
+ case UBYTE: {final short v = (short) input.readUnsignedByte(); align(1); return v;}
+ case SHORT: {final short v = input.readShort(); align(2); return v;}
+ case USHORT: {final int v = input.readUnsignedShort(); align(2); return v;}
+ case INT: return input.readInt();
+ case INT64: return input.readLong();
+ case UINT: return input.readUnsignedInt();
+ case FLOAT: return input.readFloat();
+ case DOUBLE: return input.readDouble();
+ }
+ }
switch (type) {
- case VariableInfo.CHAR: {
- final String text = input.readString(length, encoding).trim();
- result = text.isEmpty() ? null : text;
- break;
+ case CHAR: {
+ final String text = input.readString(length, encoding);
+ align(length);
+ return text.isEmpty() ? null : text;
}
- case VariableInfo.BYTE: {
+ case BYTE:
+ case UBYTE: {
final byte[] array = new byte[length];
- buffer.get(array);
- result = array;
- break;
+ input.readFully(array);
+ align(length);
+ return array;
}
- case VariableInfo.SHORT: {
+ case SHORT:
+ case USHORT: {
final short[] array = new short[length];
- buffer.asShortBuffer().get(array);
- result = array;
- break;
+ input.readFully(array, 0, length);
+ align(length << 1);
+ return array;
}
- case VariableInfo.INT: {
+ case INT:
+ case UINT: {
final int[] array = new int[length];
- buffer.asIntBuffer().get(array);
- result = array;
- break;
+ input.readFully(array, 0, length);
+ return array;
}
- case VariableInfo.FLOAT: {
+ case INT64:
+ case UINT64: {
+ final long[] array = new long[length];
+ input.readFully(array, 0, length);
+ return array;
+ }
+ case FLOAT: {
final float[] array = new float[length];
- buffer.asFloatBuffer().get(array);
- result = array;
- break;
+ input.readFully(array, 0, length);
+ return array;
}
- case VariableInfo.DOUBLE: {
+ case DOUBLE: {
final double[] array = new double[length];
- buffer.asDoubleBuffer().get(array);
- result = array;
- break;
+ input.readFully(array, 0, length);
+ return array;
}
default: {
throw malformedHeader();
}
}
- buffer.position(position + size);
- return result;
}
/**
@@ -424,6 +473,7 @@ public final class ChannelDecoder extend
}
dimensions[i] = new Dimension(name, length);
}
+ dimensionMap = Dimension.toCaseInsensitiveNameMap(dimensions, NAME_LOCALE);
return dimensions;
}
@@ -443,17 +493,16 @@ public final class ChannelDecoder extend
*
* @param nelems the number of attributes to read.
*/
- private Attribute[] readAttributes(final int nelems) throws IOException, DataStoreException {
- final Attribute[] attributes = new Attribute[nelems];
- int count = 0;
- for (int i=0; i<nelems; i++) {
+ private Map<String,Object> readAttributes(int nelems) throws IOException, DataStoreException {
+ final List<Map.Entry<String,Object>> attributes = new ArrayList<>(nelems);
+ while (--nelems >= 0) {
final String name = readName();
- final Object value = readValues(name, input.readInt(), input.readInt());
+ final Object value = readValues(DataType.valueOf(input.readInt()), input.readInt());
if (value != null) {
- attributes[count++] = new Attribute(name, value);
+ attributes.add(new AbstractMap.SimpleEntry<>(name, value));
}
}
- return ArraysExt.resize(attributes, count);
+ return CollectionsExt.toCaseInsensitiveNameMap(attributes, NAME_LOCALE);
}
/**
@@ -473,14 +522,14 @@ public final class ChannelDecoder extend
* <li>Offset where data begins (use {@link #readOffset()})</li>
* </ul>
*
- * @param nelems the number of variables to read.
- * @param dimensions the dimensions previously read by {@link #readDimensions(int)}.
+ * @param nelems the number of variables to read.
+ * @param allDimensions the dimensions previously read by {@link #readDimensions(int)}.
*/
- private VariableInfo[] readVariables(final int nelems, final Dimension[] dimensions)
+ private VariableInfo[] readVariables(final int nelems, final Dimension[] allDimensions)
throws IOException, DataStoreException
{
- if (dimensions == null) {
- throw malformedHeader();
+ if (allDimensions == null) {
+ throw malformedHeader(); // May happen if readDimensions(\u2026) has not been invoked.
}
final VariableInfo[] variables = new VariableInfo[nelems];
for (int j=0; j<nelems; j++) {
@@ -489,7 +538,7 @@ public final class ChannelDecoder extend
final Dimension[] varDims = new Dimension[n];
try {
for (int i=0; i<n; i++) {
- varDims[i] = dimensions[input.readInt()];
+ varDims[i] = allDimensions[input.readInt()];
}
} catch (IndexOutOfBoundsException cause) {
final DataStoreException e = malformedHeader();
@@ -500,7 +549,7 @@ public final class ChannelDecoder extend
* Following block is almost a copy-and-paste of similar block in the contructor,
* but with less cases in the "switch" statements.
*/
- Attribute[] attributes = null;
+ Map<String,Object> attributes = null;
final long tn = input.readLong();
if (tn != 0) {
final int tag = (int) (tn >>> Integer.SIZE);
@@ -512,36 +561,12 @@ public final class ChannelDecoder extend
default: throw malformedHeader();
}
}
- variables[j] = new VariableInfo(input, name, varDims, dimensions,
- toMap(attributes, Attribute.NAME_FUNCTION), input.readInt(), input.readInt(), readOffset());
+ variables[j] = new VariableInfo(input, name, varDims, attributes,
+ DataType.valueOf(input.readInt()), input.readInt(), readOffset());
}
return variables;
}
- /**
- * Creates a (<cite>name</cite>, <cite>element</cite>) mapping for the given array of elements.
- * If the name of an element is not all lower cases, then this method also adds an entry for the
- * lower cases version of that name in order to allow case-insensitive searches.
- *
- * <p>Code searching in the returned map shall ask for the original (non lower-case) name
- * <strong>before</strong> to ask for the lower-cases version of that name.</p>
- *
- * @param <E> the type of elements.
- * @param elements the elements to store in the map, or {@code null} if none.
- * @param nameFunction the function for computing a name from an element.
- * @return a (<cite>name</cite>, <cite>element</cite>) mapping with lower cases entries where possible.
- * @throws DataStoreException if the same name is used for more than one element.
- *
- * @see #findAttribute(String)
- */
- private <E> Map<String,E> toMap(final E[] elements, final Function<E,String> nameFunction) throws DataStoreException {
- try {
- return CollectionsExt.toCaseInsensitiveNameMap(Arrays.asList(elements), nameFunction, NAME_LOCALE);
- } catch (InvalidParameterCardinalityException e) {
- throw new DataStoreContentException(errors().getString(Errors.Keys.ValueAlreadyDefined_1, e.getParameterName()));
- }
- }
-
// --------------------------------------------------------------------------------------------
@@ -549,17 +574,25 @@ public final class ChannelDecoder extend
// --------------------------------------------------------------------------------------------
/**
+ * Returns a filename for information purpose only. This is used for formatting error messages.
+ *
+ * @return a filename to report in warning or error messages.
+ */
+ @Override
+ public final String getFilename() {
+ return input.filename;
+ }
+
+ /**
* Defines the groups where to search for named attributes, in preference order.
* The {@code null} group name stands for the global attributes.
*
* <p>Current implementation does nothing, since the NetCDF binary files that {@code ChannelDecoder}
* can read do not have groups anyway. Future SIS implementations may honor the given group names if
* groups support is added.</p>
- *
- * @throws IOException {@inheritDoc}
*/
@Override
- public void setSearchPath(final String... groupNames) throws IOException {
+ public void setSearchPath(final String... groupNames) {
}
/**
@@ -568,30 +601,67 @@ public final class ChannelDecoder extend
* groups which have been found in the NetCDF file are returned by this method.
*
* @return {@inheritDoc}
- * @throws IOException {@inheritDoc}
*/
@Override
- public String[] getSearchPath() throws IOException {
+ public String[] getSearchPath() {
return new String[1];
}
/**
+ * Returns the dimension of the given name (eventually ignoring case), or {@code null} if none.
+ * This method searches in all dimensions found in the NetCDF file, regardless of variables.
+ * The search will ignore case only if no exact match is found for the given name.
+ *
+ * @param dimName the name of the dimension to search.
+ * @return dimension of the given name, or {@code null} if none.
+ */
+ final Dimension findDimension(final String dimName) {
+ Dimension dim = dimensionMap.get(dimName); // Give precedence to exact match before to ignore case.
+ if (dim == null) {
+ final String lower = dimName.toLowerCase(ChannelDecoder.NAME_LOCALE);
+ if (lower != dimName) { // Identity comparison is okay here.
+ dim = dimensionMap.get(lower);
+ }
+ }
+ return dim;
+ }
+
+ /**
+ * Returns the NetCDF variable of the given name, or {@code null} if none.
+ *
+ * @param name the name of the variable to search, or {@code null}.
+ * @return the attribute value, or {@code null} if none.
+ */
+ final VariableInfo findVariable(final String name) {
+ VariableInfo v = variableMap.get(name);
+ if (v == 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) {
+ v = variableMap.get(lower);
+ }
+ }
+ return v;
+ }
+
+ /**
* Returns the NetCDF attribute of the given name, or {@code null} if none.
* The {@code name} argument is typically (but is not restricted too) one of
* the constants defined in the {@link AttributeNames} class.
*
* @param name the name of the attribute to search, or {@code null}.
- * @return the attribute, or {@code null} if none.
+ * @return the attribute value, or {@code null} if none.
*/
- private Attribute findAttribute(final String name) {
- Attribute attribute = attributeMap.get(name);
- if (attribute == null && name != null) {
+ private Object findAttribute(final String name) {
+ Object value = attributeMap.get(name);
+ if (value == null && name != null) {
final String lower = name.toLowerCase(NAME_LOCALE);
- if (lower != name) { // Identity comparison is ok since this check is only an optimization for a common case.
- attribute = attributeMap.get(lower);
+ // Identity comparison is ok since following check is only an optimization for a common case.
+ if (lower != name) {
+ value = attributeMap.get(lower);
}
}
- return attribute;
+ return value;
}
/**
@@ -599,15 +669,11 @@ public final class ChannelDecoder extend
*
* @param name the name of the attribute to search, or {@code null}.
* @return the attribute value, or {@code null} if none or empty or if the given name was null.
- * @throws IOException {@inheritDoc}
*/
@Override
- public String stringValue(final String name) throws IOException {
- final Attribute attribute = findAttribute(name);
- if (attribute != null) {
- return attribute.value.toString();
- }
- return null;
+ public String stringValue(final String name) {
+ final Object value = findAttribute(name);
+ return (value != null) ? value.toString() : null;
}
/**
@@ -615,18 +681,15 @@ public final class ChannelDecoder extend
* If there is more than one numeric value, only the first one is returned.
*
* @return {@inheritDoc}
- * @throws IOException {@inheritDoc}
*/
@Override
- public Number numericValue(final String name) throws IOException {
- final Attribute attribute = findAttribute(name);
- if (attribute != null && attribute.value != null) {
- if (attribute.value instanceof String) {
- return parseNumber((String) attribute.value);
- }
- return (Number) Array.get(attribute.value, 0);
+ public Number numericValue(final String name) {
+ final Object value = findAttribute(name);
+ if (value instanceof String) {
+ return parseNumber((String) value);
}
- return null;
+ final Number[] v = VariableInfo.numberValues(value);
+ return (v.length != 0) ? v[0] : null;
}
/**
@@ -634,17 +697,14 @@ public final class ChannelDecoder extend
* If there is more than one numeric value, only the first one is returned.
*
* @return {@inheritDoc}
- * @throws IOException {@inheritDoc}
*/
@Override
- public Date dateValue(final String name) throws IOException {
- final Attribute attribute = findAttribute(name);
- if (attribute != null) {
- if (attribute.value instanceof CharSequence) try {
- return JDK8.parseDateTime(StandardDateFormat.dateToISO((CharSequence) attribute.value, 0, false));
- } catch (IllegalArgumentException e) {
- listeners.warning(null, e);
- }
+ public Date dateValue(final String name) {
+ final Object value = findAttribute(name);
+ if (value instanceof CharSequence) try {
+ return JDK8.parseDateTime(StandardDateFormat.dateToISO((CharSequence) value, 0, false));
+ } catch (IllegalArgumentException e) {
+ listeners.warning(null, e);
}
return null;
}
@@ -655,10 +715,9 @@ public final class ChannelDecoder extend
*
* @param values the values to convert. May contain {@code null} elements.
* @return the converted values. May contain {@code null} elements.
- * @throws IOException {@inheritDoc}
*/
@Override
- public Date[] numberToDate(final String symbol, final Number... values) throws IOException {
+ public Date[] numberToDate(final String symbol, final Number... values) {
final Date[] dates = new Date[values.length];
final String[] parts = TIME_UNIT_PATTERN.split(symbol);
if (parts.length == 2) try {
@@ -681,28 +740,41 @@ public final class ChannelDecoder extend
* This method returns a direct reference to an internal array - do not modify.
*
* @return {@inheritDoc}
- * @throws IOException {@inheritDoc}
*/
@Override
@SuppressWarnings("ReturnOfCollectionOrArrayField")
- public Variable[] getVariables() throws IOException {
+ public Variable[] getVariables() {
return variables;
}
/**
+ * If this decoder can handle the file content as features, returns handlers for them.
+ *
+ * @return {@inheritDoc}
+ * @throws IOException if an I/O operation was necessary but failed.
+ * @throws DataStoreException if a logical error occurred.
+ */
+ @Override
+ public DiscreteSampling[] getDiscreteSampling() throws IOException, DataStoreException {
+ if ("trajectory".equalsIgnoreCase(stringValue(CF.FEATURE_TYPE))) {
+ return FeaturesInfo.create(this);
+ }
+ return new FeaturesInfo[0];
+ }
+
+ /**
* Returns all grid geometries found in the NetCDF file.
* This method returns a direct reference to an internal array - do not modify.
*
* @return {@inheritDoc}
- * @throws IOException {@inheritDoc}
*/
@Override
@SuppressWarnings("ReturnOfCollectionOrArrayField")
- public GridGeometry[] getGridGeometries() throws IOException {
+ public GridGeometry[] getGridGeometries() {
if (gridGeometries == null) {
/*
- * First, find all variables which are used as coordinate system axis. The keys are the
- * grid dimensions which are the domain of the variable (i.e. the sources of the conversion
+ * First, find all variables which are used as coordinate system axis. The keys in the map are
+ * the grid dimensions which are the domain of the variable (i.e. the sources of the conversion
* from grid coordinates to CRS coordinates).
*/
final Map<Dimension, List<VariableInfo>> dimToAxes = new IdentityHashMap<>();
@@ -714,7 +786,7 @@ public final class ChannelDecoder extend
}
}
/*
- * For each variables, gets the list of all axes associated to their dimensions. The association
+ * For each variables, get the list of all axes associated to their dimensions. The association
* is given by the above 'dimToVars' map. More than one variable may have the same dimensions,
* and consequently the same axes, so we will remember the previously created instances in order
* to share them.
@@ -772,7 +844,7 @@ nextVar: for (final VariableInfo vari
@Override
public String toString() {
final StringBuilder buffer = new StringBuilder();
- buffer.append("SIS driver: \u201c").append(input.filename).append('\u201d');
+ buffer.append("SIS driver: \u201c").append(getFilename()).append('\u201d');
if (!input.channel.isOpen()) {
buffer.append(" (closed)");
}
Modified: sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/Dimension.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/Dimension.java?rev=1763840&r1=1763839&r2=1763840&view=diff
==============================================================================
--- sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/Dimension.java [UTF-8] (original)
+++ sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/Dimension.java [UTF-8] Fri Oct 7 21:35:43 2016
@@ -16,6 +16,7 @@
*/
package org.apache.sis.internal.netcdf.impl;
+import org.apache.sis.internal.netcdf.NamedElement;
import org.apache.sis.util.Debug;
@@ -27,10 +28,10 @@ import org.apache.sis.util.Debug;
* @author Johann Sorel (Geomatys)
* @author Martin Desruisseaux (Geomatys)
* @since 0.3
- * @version 0.7
+ * @version 0.8
* @module
*/
-final class Dimension {
+final class Dimension extends NamedElement {
/**
* The dimension name.
*/
@@ -53,6 +54,14 @@ final class Dimension {
}
/**
+ * Returns the name of this NetCDF dimension.
+ */
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ /**
* Returns the number of grid cell value along this dimension.
*/
final long length() {
Modified: sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/VariableInfo.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/VariableInfo.java?rev=1763840&r1=1763839&r2=1763840&view=diff
==============================================================================
--- sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/VariableInfo.java [UTF-8] (original)
+++ sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/impl/VariableInfo.java [UTF-8] Fri Oct 7 21:35:43 2016
@@ -18,9 +18,11 @@ package org.apache.sis.internal.netcdf.i
import java.util.Map;
import java.io.IOException;
+import java.lang.reflect.Array;
import ucar.nc2.constants.CF;
import ucar.nc2.constants.CDM;
import ucar.nc2.constants._Coordinate;
+import org.apache.sis.internal.netcdf.DataType;
import org.apache.sis.internal.netcdf.Variable;
import org.apache.sis.internal.storage.ChannelDataInput;
import org.apache.sis.internal.storage.HyperRectangleReader;
@@ -28,7 +30,9 @@ import org.apache.sis.internal.storage.R
import org.apache.sis.storage.DataStoreException;
import org.apache.sis.storage.DataStoreContentException;
import org.apache.sis.util.resources.Errors;
+import org.apache.sis.util.CharSequences;
import org.apache.sis.util.Numbers;
+import org.apache.sis.math.Vector;
/**
@@ -42,6 +46,11 @@ import org.apache.sis.util.Numbers;
*/
final class VariableInfo extends Variable {
/**
+ * The array to be returned by {@link #numberValues(Object)} when the given value is null.
+ */
+ private static final Number[] EMPTY = new Number[0];
+
+ /**
* The names of attributes where to look for the description to be returned by {@link #getDescription()}.
* We use the same attributes than the one documented in the {@link ucar.nc2.Variable#getDescription()} javadoc.
*/
@@ -53,52 +62,6 @@ final class VariableInfo extends Variabl
};
/**
- * The NetCDF type of data. Number of bits and endianness are same as in the Java language except {@code CHAR},
- * which is defined as an unsigned 8-bits value.
- */
- static final int BYTE=1, CHAR=2, SHORT=3, INT=4, FLOAT=5, DOUBLE=6;
-
- /**
- * Mapping from the NetCDF data type to the enumeration used by our {@link Numbers} class.
- */
- private static final byte[] NUMBER_TYPES = new byte[] {
- Numbers.BYTE,
- Numbers.BYTE, // NOT Numbers.CHARACTER
- Numbers.SHORT,
- Numbers.INTEGER,
- Numbers.FLOAT,
- Numbers.DOUBLE,
- };
-
- /**
- * The size in bytes of the above constants.
- *
- * @see #sizeOf(int)
- */
- private static final byte[] SIZES = new byte[] {
- Byte .SIZE / Byte.SIZE,
- Byte .SIZE / Byte.SIZE, // NOT Character.BYTES
- Short .SIZE / Byte.SIZE,
- Integer.SIZE / Byte.SIZE,
- Float .SIZE / Byte.SIZE,
- Double .SIZE / Byte.SIZE,
- };
-
- /**
- * The Java primitive type of the above constants.
- *
- * @see #getDataType()
- */
- private static final Class<?>[] TYPES = new Class<?>[] {
- byte .class,
- char .class,
- short .class,
- int .class,
- float .class,
- double.class
- };
-
- /**
* Helper class for reading a sub-area with a sub-sampling,
* or {@code null} if {@code dataType} is not a supported type.
*/
@@ -110,24 +73,35 @@ final class VariableInfo extends Variabl
private final String name;
/**
- * The dimensions of this variable.
+ * The dimensions of this variable, in order. When iterating over the values stored in this variable
+ * (a flattened one-dimensional sequence of values), index in the domain of {@code dimensions[0]}
+ * varies faster, followed by index in the domain of {@code dimensions[1]}, <i>etc.</i>
*/
final Dimension[] dimensions;
/**
- * All dimensions in the NetCDF files.
- */
- private final Dimension[] allDimensions;
-
- /**
* The attributes associates to the variable, or an empty map if none.
+ * Values can be:
+ *
+ * <ul>
+ * <li>a {@link String}</li>
+ * <li>A {@link Number}</li>
+ * <li>an array of primitive type</li>
+ * </ul>
+ *
+ * If the value is a {@code String}, then leading and trailing spaces and control characters
+ * should be trimmed by {@link String#trim()}.
+ *
+ * @see #stringValues(Object)
+ * @see #numberValues(Object)
+ * @see #booleanValue(Object)
*/
- private final Map<String,Attribute> attributes;
+ private final Map<String,Object> attributes;
/**
- * The type of data, as one of the {@code BYTE}, {@code SHORT} and similar constants defined in this class.
+ * The NetCDF type of data, or {@code null} if unknown.
*/
- private final int dataType;
+ private final DataType dataType;
/**
* The grid geometry associated to this variable,
@@ -136,36 +110,67 @@ final class VariableInfo extends Variabl
GridGeometryInfo gridGeometry;
/**
+ * {@code true} if this variable seems to be a coordinate system axis, as determined by comparing its name
+ * with the name of all dimensions in the NetCDF file. This information is computed at construction time
+ * because requested more than once.
+ *
+ * @see #isCoordinateSystemAxis()
+ */
+ private final boolean isCoordinateSystemAxis;
+
+ /**
* Creates a new variable.
*
* @param input the channel together with a buffer for reading the variable data.
* @param name the variable name.
* @param dimensions the dimensions of this variable.
- * @param allDimensions all dimensions in the NetCDF files.
* @param attributes the attributes associates to the variable, or an empty map if none.
- * @param dataType the type of data, as one of the {@code BYTE} and similar constants defined in this class.
+ * @param dataType the NetCDF type of data, or {@code null} if unknown.
* @param size the variable size, used for verification purpose only.
* @param offset the offset where the variable data begins in the NetCDF file.
*/
- VariableInfo(final ChannelDataInput input, final String name,
- final Dimension[] dimensions, final Dimension[] allDimensions,
- final Map<String,Attribute> attributes, int dataType, final int size, final long offset)
- throws DataStoreException
+ VariableInfo(final ChannelDataInput input,
+ final String name,
+ final Dimension[] dimensions,
+ final Map<String,Object> attributes,
+ DataType dataType,
+ final int size,
+ final long offset) throws DataStoreException
{
- this.name = name;
- this.dimensions = dimensions;
- this.allDimensions = allDimensions;
- this.attributes = attributes;
- this.dataType = dataType;
+ final Object isUnsigned = attributes.get(CDM.UNSIGNED);
+ if (isUnsigned != null) {
+ dataType = dataType.unsigned(booleanValue(isUnsigned));
+ }
+ this.name = name;
+ this.dimensions = dimensions;
+ this.attributes = attributes;
+ this.dataType = dataType;
/*
* The 'size' value is provided in the NetCDF files, but doesn't need to be stored since it
* is redundant with the dimension lengths and is not large enough for big variables anyway.
*/
- if (--dataType >= 0 && dataType < NUMBER_TYPES.length) {
- reader = new HyperRectangleReader(NUMBER_TYPES[dataType], input, offset);
+ if (dataType != null && dataType.number >= Numbers.BYTE && dataType.number <= Numbers.DOUBLE) {
+ reader = new HyperRectangleReader(dataType.number, input, offset);
} else {
reader = null;
}
+ /*
+ * If the "_CoordinateAliasForDimension" attribute is defined, then its value will be used
+ * instead of the variable name when determining if the variable is a coordinate system axis.
+ * "_CoordinateVariableAlias" seems to be a legacy attribute name for the same purpose.
+ */
+ if (dimensions.length == 1) {
+ Object value = getAttributeValue(_Coordinate.AliasForDimension, "_coordinatealiasfordimension");
+ if (value == null) {
+ value = getAttributeValue("_CoordinateVariableAlias", "_coordinatevariablealias");
+ if (value == null) {
+ value = name;
+ }
+ }
+ isCoordinateSystemAxis = dimensions[0].name.equals(value);
+ } else {
+ isCoordinateSystemAxis = false;
+ }
}
/**
@@ -184,9 +189,9 @@ final class VariableInfo extends Variabl
@Override
public String getDescription() {
for (final String attributeName : DESCRIPTION_ATTRIBUTES) {
- final Attribute attribute = attributes.get(attributeName);
- if (attribute != null && attribute.value instanceof String) {
- return (String) attribute.value;
+ final Object value = getAttributeValue(attributeName);
+ if (value instanceof String) {
+ return (String) value;
}
}
return null;
@@ -197,38 +202,18 @@ final class VariableInfo extends Variabl
*/
@Override
public String getUnitsString() {
- final Attribute attribute = attributes.get(CDM.UNITS);
- if (attribute != null && attribute.value instanceof String) {
- return (String) attribute.value;
- }
- return null;
+ final Object value = getAttributeValue(CDM.UNITS);
+ return (value instanceof String) ? (String) value : null;
}
/**
- * Returns the type of data as a Java primitive type if possible,
- * or {@code null} if the data type is unknown to this method.
+ * Returns the type of data, or {@code UNKNOWN} if the data type is unknown to this method.
+ * If this variable has a {@code "_Unsigned = true"} attribute, then the returned data type
+ * will be a unsigned variant.
*/
@Override
- public Class<?> getDataType() {
- final int i = dataType - 1;
- return (i >= 0 && i < TYPES.length) ? TYPES[i] : null;
- }
-
- /**
- * Returns the size of the given data type, or 0 if unknown.
- */
- static int sizeOf(int datatype) {
- return (--datatype >= 0 && datatype < SIZES.length) ? SIZES[datatype] : 0;
- }
-
- /**
- * Returns {@code true} if the integer values shall be considered as unsigned.
- * Current implementation searches for an {@code "_Unsigned = true"} attribute.
- */
- @Override
- public boolean isUnsigned() {
- final Attribute attribute = attributes.get(CDM.UNSIGNED);
- return (attribute != null) && attribute.booleanValue();
+ public DataType getDataType() {
+ return dataType;
}
/**
@@ -237,29 +222,15 @@ final class VariableInfo extends Variabl
*/
@Override
public boolean isCoordinateSystemAxis() {
- String name = this.name;
- final Attribute attribute = attributes.get(_CoordinateVariableAlias);
- if (attribute != null && attribute.value instanceof String) {
- name = (String) attribute.value;
- }
- for (final Dimension dimension : allDimensions) {
- if (name.equals(dimension.name)) {
- // This variable is a dimension of another variable.
- return true;
- }
- }
- return false;
+ return isCoordinateSystemAxis;
}
/**
* Returns the value of the {@code "_CoordinateAxisType"} attribute, or {@code null} if none.
*/
final String getAxisType() {
- final Attribute attribute = attributes.get(_Coordinate.AxisType);
- if (attribute != null && attribute.value instanceof String) {
- return (String) attribute.value;
- }
- return null;
+ final Object value = getAttributeValue(_Coordinate.AxisType, "_coordinateaxistype");
+ return (value instanceof String) ? (String) value : null;
}
/**
@@ -291,24 +262,100 @@ final class VariableInfo extends Variabl
}
/**
+ * Returns the value of the given attribute, or {@code null} if none.
+ * This method should be invoked only for hard-coded names that mix lower-case and upper-case letters.
+ *
+ * @param attributeName name of attribute to search, in the expected case.
+ * @param lowerCase the all lower-case variant of {@code attributeName}.
+ * @return variable attribute value of the given name, or {@code null} if none.
+ */
+ private Object getAttributeValue(final String attributeName, final String lowerCase) {
+ Object value = attributes.get(attributeName);
+ if (value == null) {
+ value = attributes.get(lowerCase);
+ }
+ return value;
+ }
+
+ /**
+ * Returns the value of the given attribute, or {@code null} if none.
+ * This method does not search the lower-case variant of the given name because the argument given to this method
+ * is usually a hard-coded value from {@link CF} or {@link CDM} conventions, which are already in lower-cases.
+ *
+ * @param attributeName name of attribute to search, in the expected case.
+ * @return variable attribute value of the given name, or {@code null} if none.
+ */
+ final Object getAttributeValue(final String attributeName) {
+ return attributes.get(attributeName);
+ }
+
+ /**
* Returns the sequence of values for the given attribute, or an empty array if none.
* The elements will be of class {@link String} if {@code numeric} is {@code false},
* or {@link Number} if {@code numeric} is {@code true}.
*/
@Override
public Object[] getAttributeValues(final String attributeName, final boolean numeric) {
- Attribute attribute = attributes.get(attributeName);
- if (attribute != null) {
- return numeric ? attribute.numberValues() : attribute.stringValues();
+ final Object value = getAttributeValue(attributeName);
+ return numeric ? numberValues(value) : stringValues(value);
+ }
+
+ /**
+ * Returns the attribute values as an array of {@link String}s, or an empty array if none.
+ * The given argument is typically a value of the {@link #attributes} map.
+ *
+ * @see #getAttributeValues(String, boolean)
+ */
+ static String[] stringValues(final Object value) {
+ if (value == null) {
+ return CharSequences.EMPTY_ARRAY;
+ }
+ if (value.getClass().isArray()) {
+ final String[] values = new String[Array.getLength(value)];
+ for (int i=0; i<values.length; i++) {
+ values[i] = Array.get(value, i).toString();
+ }
+ return values;
}
- return new Object[0];
+ return new String[] {value.toString()};
+ }
+
+ /**
+ * Returns the attribute values as an array of {@link Number}, or an empty array if none.
+ * The given argument is typically a value of the {@link #attributes} map.
+ *
+ * @see #getAttributeValues(String, boolean)
+ */
+ @SuppressWarnings("ReturnOfCollectionOrArrayField")
+ static Number[] numberValues(final Object value) {
+ if (value != null) {
+ if (value.getClass().isArray()) {
+ final Number[] values = new Number[Array.getLength(value)];
+ for (int i=0; i<values.length; i++) {
+ values[i] = (Number) Array.get(value, i);
+ }
+ return values;
+ }
+ if (value instanceof Number) {
+ return new Number[] {(Number) value};
+ }
+ }
+ return EMPTY;
+ }
+
+ /**
+ * Returns the attribute value as a boolean, or {@code false} if the attribute is not a boolean.
+ * The given argument is typically a value of the {@link #attributes} map.
+ */
+ private static boolean booleanValue(final Object value) {
+ return (value instanceof String) && Boolean.valueOf((String) value);
}
/**
* Reads all the data for this variable and returns them as an array of a Java primitive type.
*/
@Override
- public Object read() throws IOException, DataStoreException {
+ public Vector read() throws IOException, DataStoreException {
if (reader == null) {
throw new DataStoreContentException(unknownType());
}
@@ -326,7 +373,7 @@ final class VariableInfo extends Variabl
sub [i] = 1;
size[i] = dimensions[(dimension - 1) - i].length();
}
- return reader.read(new Region(size, new long[dimension], size, sub));
+ return Vector.create(reader.read(new Region(size, new long[dimension], size, sub)), dataType.isUnsigned);
}
/**
@@ -338,7 +385,7 @@ final class VariableInfo extends Variabl
* @return the data as an array of a Java primitive type.
*/
@Override
- public Object read(int[] areaLower, int[] areaUpper, int[] subsampling) throws IOException, DataStoreException {
+ public Vector read(int[] areaLower, int[] areaUpper, int[] subsampling) throws IOException, DataStoreException {
if (reader == null) {
throw new DataStoreContentException(unknownType());
}
@@ -373,7 +420,7 @@ final class VariableInfo extends Variabl
sub [i] = subsampling[j];
size [i] = dimensions[j].length();
}
- return reader.read(new Region(size, lower, upper, sub));
+ return Vector.create(reader.read(new Region(size, lower, upper, sub)), dataType.isUnsigned);
}
/**
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=1763840&r1=1763839&r2=1763840&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] Fri Oct 7 21:35:43 2016
@@ -19,9 +19,9 @@ package org.apache.sis.internal.netcdf.u
import java.util.Date;
import java.util.List;
import java.util.EnumSet;
+import java.util.Formatter;
import java.io.IOException;
import ucar.nc2.Group;
-import ucar.nc2.Dimension;
import ucar.nc2.Attribute;
import ucar.nc2.VariableIF;
import ucar.nc2.NetcdfFile;
@@ -32,12 +32,17 @@ import ucar.nc2.units.DateUnit;
import ucar.nc2.time.Calendar;
import ucar.nc2.time.CalendarDate;
import ucar.nc2.time.CalendarDateFormatter;
+import ucar.nc2.ft.FeatureDataset;
+import ucar.nc2.ft.FeatureDatasetPoint;
+import ucar.nc2.ft.FeatureDatasetFactoryManager;
+import ucar.nc2.ft.FeatureCollection;
import org.apache.sis.util.Debug;
import org.apache.sis.util.ArraysExt;
import org.apache.sis.util.logging.WarningListeners;
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.DiscreteSampling;
/**
@@ -45,7 +50,7 @@ import org.apache.sis.internal.netcdf.Gr
*
* @author Martin Desruisseaux (Geomatys)
* @since 0.3
- * @version 0.3
+ * @version 0.8
* @module
*/
public final class DecoderWrapper extends Decoder implements CancelTask {
@@ -76,6 +81,11 @@ public final class DecoderWrapper extend
private transient Variable[] variables;
/**
+ * The discrete sampling features, or {@code null} if none.
+ */
+ private transient FeatureDataset features;
+
+ /**
* The grid geometries, computed when first needed.
*
* @see #getGridGeometries()
@@ -102,12 +112,23 @@ public final class DecoderWrapper extend
* @param filename the name of the NetCDF file from which to read data.
* @throws IOException if an error occurred while opening the NetCDF file.
*/
+ @SuppressWarnings("ThisEscapedInObjectConstruction")
public DecoderWrapper(final WarningListeners<?> listeners, final String filename) throws IOException {
super(listeners);
file = NetcdfDataset.openDataset(filename, false, this);
}
/**
+ * Returns a filename for information purpose only. This is used for formatting error messages.
+ *
+ * @return a filename to report in warning or error messages.
+ */
+ @Override
+ public String getFilename() {
+ return file.getLocation();
+ }
+
+ /**
* Defines the groups where to search for named attributes, in preference order.
* The {@code null} group name stands for the global attributes.
*/
@@ -299,17 +320,40 @@ public final class DecoderWrapper extend
@SuppressWarnings({"ReturnOfCollectionOrArrayField", "null"})
public Variable[] getVariables() {
if (variables == null) {
- final List<Dimension> dimensions = file.getDimensions();
final List<? extends VariableIF> all = file.getVariables();
variables = new Variable[(all != null) ? all.size() : 0];
for (int i=0; i<variables.length; i++) {
- variables[i] = new VariableWrapper(all.get(i), dimensions);
+ variables[i] = new VariableWrapper(all.get(i));
}
}
return variables;
}
/**
+ * If this decoder can handle the file content as features, returns handlers for them.
+ *
+ * @return {@inheritDoc}
+ * @throws IOException if an I/O operation was necessary but failed.
+ */
+ @Override
+ @SuppressWarnings("null")
+ public DiscreteSampling[] getDiscreteSampling() throws IOException {
+ if (features == null && file instanceof NetcdfDataset) {
+ features = FeatureDatasetFactoryManager.wrap(null, (NetcdfDataset) file, this,
+ new Formatter(new LogAdapter(listeners), listeners.getLocale()));
+ }
+ List<FeatureCollection> fc = null;
+ if (features instanceof FeatureDatasetPoint) {
+ fc = ((FeatureDatasetPoint) features).getPointFeatureCollectionList();
+ }
+ final FeaturesWrapper[] wrappers = new FeaturesWrapper[(fc != null) ? fc.size() : 0];
+ for (int i=0; i<wrappers.length; i++) {
+ wrappers[i] = new FeaturesWrapper(fc.get(i));
+ }
+ return wrappers;
+ }
+
+ /**
* Returns all grid geometries (related to coordinate systems) found in the NetCDF file.
* This method returns a direct reference to an internal array - do not modify.
*
@@ -375,6 +419,10 @@ public final class DecoderWrapper extend
*/
@Override
public void close() throws IOException {
+ if (features != null) {
+ features.close();
+ features = null;
+ }
file.close();
}
@@ -385,6 +433,6 @@ public final class DecoderWrapper extend
@Debug
@Override
public String toString() {
- return "UCAR driver: \u201c" + file.getLocation() + '\u201d';
+ return "UCAR driver: \u201c" + getFilename() + '\u201d';
}
}
Modified: sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/VariableWrapper.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/VariableWrapper.java?rev=1763840&r1=1763839&r2=1763840&view=diff
==============================================================================
--- sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/VariableWrapper.java [UTF-8] (original)
+++ sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/internal/netcdf/ucar/VariableWrapper.java [UTF-8] Fri Oct 7 21:35:43 2016
@@ -24,6 +24,8 @@ import ucar.ma2.InvalidRangeException;
import ucar.nc2.Attribute;
import ucar.nc2.Dimension;
import ucar.nc2.VariableIF;
+import org.apache.sis.math.Vector;
+import org.apache.sis.internal.netcdf.DataType;
import org.apache.sis.internal.netcdf.Variable;
import org.apache.sis.storage.DataStoreException;
import org.apache.sis.storage.DataStoreContentException;
@@ -45,16 +47,10 @@ final class VariableWrapper extends Vari
private final VariableIF variable;
/**
- * The list of all dimensions in the NetCDF file.
- */
- private final List<? extends Dimension> all;
-
- /**
* Creates a new variable wrapping the given NetCDF interface.
*/
- VariableWrapper(final VariableIF variable, List<? extends Dimension> all) {
+ VariableWrapper(final VariableIF variable) {
this.variable = variable;
- this.all = all;
}
/**
@@ -82,43 +78,32 @@ final class VariableWrapper extends Vari
}
/**
- * Returns the variable data type, as a primitive type if possible.
- * This method may return {@code null} (UCAR code seems to allow that).
- */
- @Override
- public Class<?> getDataType() {
- return variable.getDataType().getPrimitiveClassType();
- }
-
- /**
- * Returns {@code true} if the integer values shall be considered as unsigned.
+ * Returns the variable data type.
+ * This method may return {@code UNKNOWN} if the datatype is unknown.
*/
@Override
- public boolean isUnsigned() {
- return variable.isUnsigned();
+ public DataType getDataType() {
+ final DataType type;
+ switch (variable.getDataType()) {
+ case STRING: return DataType.STRING;
+ case CHAR: return DataType.CHAR;
+ case BYTE: type = DataType.BYTE; break;
+ case SHORT: type = DataType.SHORT; break;
+ case INT: type = DataType.INT; break;
+ case LONG: type = DataType.INT64; break;
+ case FLOAT: return DataType.FLOAT;
+ case DOUBLE: return DataType.DOUBLE;
+ default: return DataType.UNKNOWN;
+ }
+ return type.unsigned(variable.isUnsigned());
}
/**
- * Returns {@code true} if this variable seems to be a coordinate system axis,
- * determined by comparing its name with the name of all dimensions in the NetCDF file.
+ * Returns {@code true} if this variable seems to be a coordinate system axis.
*/
@Override
public boolean isCoordinateSystemAxis() {
- String name = null;
- final Attribute attribute = variable.findAttributeIgnoreCase(_CoordinateVariableAlias);
- if (attribute != null) {
- name = attribute.getStringValue();
- }
- if (name == null) {
- name = getName();
- }
- for (final Dimension dimension : all) {
- if (name.equals(dimension.getShortName())) {
- // This variable is a dimension of another variable.
- return true;
- }
- }
- return false;
+ return variable.isCoordinateVariable();
}
/**
@@ -182,9 +167,9 @@ final class VariableWrapper extends Vari
* Reads all the data for this variable and returns them as an array of a Java primitive type.
*/
@Override
- public Object read() throws IOException {
+ public Vector read() throws IOException {
final Array array = variable.read();
- return array.get1DJavaArray(array.getElementType());
+ return Vector.create(array.get1DJavaArray(array.getElementType()), variable.isUnsigned());
}
/**
@@ -196,7 +181,7 @@ final class VariableWrapper extends Vari
* @return the data as an array of a Java primitive type.
*/
@Override
- public Object read(final int[] areaLower, final int[] areaUpper, final int[] subsampling)
+ public Vector read(final int[] areaLower, final int[] areaUpper, final int[] subsampling)
throws IOException, DataStoreException
{
final int[] size = new int[areaUpper.length];
@@ -209,6 +194,6 @@ final class VariableWrapper extends Vari
} catch (InvalidRangeException e) {
throw new DataStoreContentException(e);
}
- return array.get1DJavaArray(array.getElementType());
+ return Vector.create(array.get1DJavaArray(array.getElementType()), variable.isUnsigned());
}
}
Modified: sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/AttributeNames.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/AttributeNames.java?rev=1763840&r1=1763839&r2=1763840&view=diff
==============================================================================
--- sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/AttributeNames.java [UTF-8] (original)
+++ sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/AttributeNames.java [UTF-8] Fri Oct 7 21:35:43 2016
@@ -17,14 +17,16 @@
package org.apache.sis.storage.netcdf;
/*
- * All imports below except "CF" are for javadoc only. The "CF" import is used only
- * for its static final String constants, which are inlined by javac. Consequently
- * the compiled file of this class should have no dependency to the UCAR packages.
+ * All imports below except "CF" and "ACDD" are for javadoc only.
+ * The "CF" and "ACDD" imports are used only for its static final String constants,
+ * which are inlined by javac. Consequently the compiled file of this class should
+ * have no dependency to the UCAR packages.
*/
import java.io.Serializable;
import ucar.nc2.NetcdfFile;
import ucar.nc2.VariableSimpleIF;
import ucar.nc2.constants.CF;
+import ucar.nc2.constants.ACDD;
import org.opengis.metadata.Metadata;
import org.opengis.metadata.Identifier;
import org.opengis.metadata.citation.*;
@@ -128,7 +130,7 @@ import org.opengis.metadata.extent.Geogr
*
* @author Martin Desruisseaux (Geomatys)
* @since 0.3
- * @version 0.3
+ * @version 0.8
* @module
*/
public class AttributeNames {
@@ -145,7 +147,7 @@ public class AttributeNames {
* @see NetcdfFile#getTitle()
* @see <a href="http://www.unidata.ucar.edu/software/netcdf-java/formats/DataDiscoveryAttConvention.html#title_Attribute">UCAR reference</a>
*/
- public static final String TITLE = "title";
+ public static final String TITLE = ACDD.title;
/**
* The {@value} attribute name for a paragraph describing the dataset
@@ -157,7 +159,7 @@ public class AttributeNames {
*
* @see <a href="http://www.unidata.ucar.edu/software/netcdf-java/formats/DataDiscoveryAttConvention.html#summary_Attribute">UCAR reference</a>
*/
- public static final String SUMMARY = "summary";
+ public static final String SUMMARY = ACDD.summary;
/**
* The {@value} attribute name for an identifier (<em>Recommended</em>).
@@ -176,7 +178,7 @@ public class AttributeNames {
* @see NetcdfFile#getId()
* @see <a href="http://www.unidata.ucar.edu/software/netcdf-java/formats/DataDiscoveryAttConvention.html#id_Attribute">UCAR reference</a>
*/
- public static final String IDENTIFIER = "id";
+ public static final String IDENTIFIER = ACDD.id;
/**
* The {@value} attribute name for the identifier authority (<em>Recommended</em>).
@@ -195,7 +197,7 @@ public class AttributeNames {
* @see MetadataReader#getFileIdentifier()
* @see <a href="http://www.unidata.ucar.edu/software/netcdf-java/formats/DataDiscoveryAttConvention.html#id_Attribute">UCAR reference</a>
*/
- public static final String NAMING_AUTHORITY = "naming_authority";
+ public static final String NAMING_AUTHORITY = ACDD.naming_authority;
/**
* The {@value} attribute name for a long descriptive name for the variable taken from a controlled
@@ -227,7 +229,7 @@ public class AttributeNames {
* @see #VOCABULARY
* @see <a href="http://www.unidata.ucar.edu/software/netcdf-java/formats/DataDiscoveryAttConvention.html#standard_name_vocabulary_Attribute">UCAR reference</a>
*/
- public static final String STANDARD_NAME_VOCABULARY = "standard_name_vocabulary";
+ public static final String STANDARD_NAME_VOCABULARY = ACDD.standard_name_vocabulary;
/**
* The {@value} attribute name for a comma separated list of key words and phrases
@@ -242,7 +244,7 @@ public class AttributeNames {
* @see #STANDARD_NAME
* @see <a href="http://www.unidata.ucar.edu/software/netcdf-java/formats/DataDiscoveryAttConvention.html#keywords_Attribute">UCAR reference</a>
*/
- public static final String KEYWORDS = "keywords";
+ public static final String KEYWORDS = ACDD.keywords;
/**
* The {@value} attribute name for the guideline for the words/phrases in the
@@ -258,7 +260,7 @@ public class AttributeNames {
* @see #STANDARD_NAME_VOCABULARY
* @see <a href="http://www.unidata.ucar.edu/software/netcdf-java/formats/DataDiscoveryAttConvention.html#keywords_vocabulary_Attribute">UCAR reference</a>
*/
- public static final String VOCABULARY = "keywords_vocabulary";
+ public static final String VOCABULARY = ACDD.keywords_vocabulary;
/**
* The {@value} attribute name for a high-level geographic data thematic classification.
@@ -291,7 +293,7 @@ public class AttributeNames {
* @see SpatialRepresentationType
* @see <a href="http://www.unidata.ucar.edu/software/netcdf-java/formats/DataDiscoveryAttConvention.html#cdm_data_type_Attribute">UCAR reference</a>
*/
- public static final String DATA_TYPE = "cdm_data_type";
+ public static final String DATA_TYPE = ACDD.cdm_data_type;
/**
* The {@value} attribute name for providing an audit trail for modifications to the
@@ -304,7 +306,7 @@ public class AttributeNames {
*
* @see <a href="http://www.unidata.ucar.edu/software/netcdf-java/formats/DataDiscoveryAttConvention.html#history_Attribute">UCAR reference</a>
*/
- public static final String HISTORY = "history";
+ public static final String HISTORY = ACDD.history;
/**
* The {@value} attribute name for miscellaneous information about the data
@@ -316,7 +318,7 @@ public class AttributeNames {
*
* @see <a href="http://www.unidata.ucar.edu/software/netcdf-java/formats/DataDiscoveryAttConvention.html#comment_Attribute">UCAR reference</a>
*/
- public static final String COMMENT = "comment";
+ public static final String COMMENT = ACDD.comment;
/**
* The {@value} attribute name for the date on which the metadata was created
@@ -340,7 +342,7 @@ public class AttributeNames {
*
* @see <a href="http://www.unidata.ucar.edu/software/netcdf-java/formats/DataDiscoveryAttConvention.html#date_created_Attribute">UCAR reference</a>
*/
- public static final String DATE_CREATED = "date_created";
+ public static final String DATE_CREATED = ACDD.date_created;
/**
* The {@value} attribute name for the date on which this data was last modified
@@ -354,7 +356,7 @@ public class AttributeNames {
*
* @see <a href="http://www.unidata.ucar.edu/software/netcdf-java/formats/DataDiscoveryAttConvention.html#date_modified_Attribute">UCAR reference</a>
*/
- public static final String DATE_MODIFIED = "date_modified";
+ public static final String DATE_MODIFIED = ACDD.date_modified;
/**
* The {@value} attribute name for a date on which this data was formally issued
@@ -523,8 +525,8 @@ public class AttributeNames {
* @see #PUBLISHER
* @see <a href="http://www.unidata.ucar.edu/software/netcdf-java/formats/DataDiscoveryAttConvention.html#creator_name_Attribute">UCAR reference</a>
*/
- public static final Responsible CREATOR = new Responsible("creator_name",
- "institution", "creator_url", "creator_email", null, Role.ORIGINATOR);
+ public static final Responsible CREATOR = new Responsible(ACDD.creator_name,
+ "institution", ACDD.creator_url, ACDD.creator_email, null, Role.ORIGINATOR);
/**
* The set of attribute names for the contributor (<em>Suggested</em>).
@@ -556,8 +558,8 @@ public class AttributeNames {
* @see #CONTRIBUTOR
* @see <a href="http://www.unidata.ucar.edu/software/netcdf-java/formats/DataDiscoveryAttConvention.html#publisher_name_Attribute">UCAR reference</a>
*/
- public static final Responsible PUBLISHER = new Responsible("publisher_name",
- null, "publisher_url", "publisher_email", null, Role.PUBLISHER);
+ public static final Responsible PUBLISHER = new Responsible(ACDD.publisher_name,
+ null, ACDD.publisher_url, ACDD.publisher_email, null, Role.PUBLISHER);
/**
* The {@value} attribute name for the scientific project that produced the data
@@ -602,7 +604,7 @@ public class AttributeNames {
*
* @see <a href="http://www.unidata.ucar.edu/software/netcdf-java/formats/DataDiscoveryAttConvention.html#processing_level_Attribute">UCAR reference</a>
*/
- public static final String PROCESSING_LEVEL = "processing_level";
+ public static final String PROCESSING_LEVEL = ACDD.processing_level;
/**
* The {@value} attribute name for a place to acknowledge various type of support for
@@ -627,7 +629,7 @@ public class AttributeNames {
*
* @see <a href="http://www.unidata.ucar.edu/software/netcdf-java/formats/DataDiscoveryAttConvention.html#license_Attribute">UCAR reference</a>
*/
- public static final String LICENSE = "license";
+ public static final String LICENSE = ACDD.license;
/**
* The {@value} attribute name for the access constraints applied to assure the protection of
@@ -831,8 +833,7 @@ public class AttributeNames {
* @see <a href="http://www.unidata.ucar.edu/software/netcdf-java/formats/DataDiscoveryAttConvention.html#geospatial_lat_min_Attribute">UCAR reference</a>
*/
public static final Dimension LATITUDE = new Dimension(DimensionNameType.ROW,
- "geospatial_lat_min", "geospatial_lat_max", null,
- "geospatial_lat_resolution", "geospatial_lat_units", null);
+ ACDD.LAT_MIN, ACDD.LAT_MAX, null, ACDD.LAT_RESOLUTION, ACDD.LAT_UNITS, null);
/**
* The set of attribute names for the minimal and maximal longitudes of the bounding box,
@@ -856,8 +857,7 @@ public class AttributeNames {
* @see <a href="http://www.unidata.ucar.edu/software/netcdf-java/formats/DataDiscoveryAttConvention.html#geospatial_lon_min_Attribute">UCAR reference</a>
*/
public static final Dimension LONGITUDE = new Dimension(DimensionNameType.COLUMN,
- "geospatial_lon_min", "geospatial_lon_max", null,
- "geospatial_lon_resolution", "geospatial_lon_units", null);
+ ACDD.LON_MIN, ACDD.LON_MAX, null, ACDD.LON_RESOLUTION, ACDD.LON_UNITS, null);
/**
* The set of attribute names for the minimal and maximal elevations of the bounding box,
@@ -881,8 +881,7 @@ public class AttributeNames {
* @see <a href="http://www.unidata.ucar.edu/software/netcdf-java/formats/DataDiscoveryAttConvention.html#geospatial_vertical_min_Attribute">UCAR reference</a>
*/
public static final Dimension VERTICAL = new Dimension(DimensionNameType.VERTICAL,
- "geospatial_vertical_min", "geospatial_vertical_max", null,
- "geospatial_vertical_resolution", "geospatial_vertical_units", "geospatial_vertical_positive");
+ ACDD.VERT_MIN, ACDD.VERT_MAX, null, ACDD.VERT_RESOLUTION, ACDD.VERT_UNITS, ACDD.VERT_IS_POSITIVE);
/**
* The set of attribute names for the start and end times of the bounding box, resolution and
@@ -904,8 +903,7 @@ public class AttributeNames {
* @see <a href="http://www.unidata.ucar.edu/software/netcdf-java/formats/DataDiscoveryAttConvention.html#time_coverage_start_Attribute">UCAR reference</a>
*/
public static final Dimension TIME = new Dimension(DimensionNameType.TIME,
- "time_coverage_start", "time_coverage_end", "time_coverage_duration",
- "time_coverage_resolution", "time_coverage_units", null);
+ ACDD.TIME_START, ACDD.TIME_END, ACDD.TIME_DURATION, ACDD.TIME_RESOLUTION, "time_coverage_units", null);
/**
* The {@value} attribute name for the designation associated with a range element.
@@ -932,9 +930,9 @@ public class AttributeNames {
public static final String FLAG_MASKS = "flag_masks";
/**
- * The {@value} attribute name for sample values to be flagged. The {@linkplain #FLAG_MASKS
- * flag masks}, flag values and {@linkplain #FLAG_MEANINGS flag meaning} attributes, used
- * together, describe a blend of independent boolean conditions and enumerated status codes.
+ * The {@value} attribute name for sample values to be flagged. The {@linkplain #FLAG_MASKS flag masks},
+ * flag values and {@linkplain #FLAG_MEANINGS flag meaning} attributes, used together, describe a blend
+ * of independent boolean conditions and enumerated status codes.
* A flagged condition is identified by a bitwise AND of the variable value and each flag masks
* value; a result that matches the flag values value indicates a true condition.
*
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=1763840&r1=1763839&r2=1763840&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] Fri Oct 7 21:35:43 2016
@@ -453,7 +453,7 @@ final class MetadataReader {
if (resource == null) resource = createOnlineResource(url);
contact = createContact(address, resource);
}
- if (individualName != null || organisationName != null || contact != null) { // Do not test role.
+ if (individualName != null || organisationName != null || contact != null) { // Do not test role.
AbstractParty party = null;
if (individualName != null) party = new DefaultIndividual(individualName, null, null);
if (organisationName != null) party = new DefaultOrganisation(organisationName, null, (Individual) party, null);
@@ -475,9 +475,9 @@ final class MetadataReader {
private Citation createCitation(final Identifier identifier) throws IOException {
String title = stringValue(TITLE);
if (title == null) {
- title = stringValue("full_name"); // THREDDS attribute documented in TITLE javadoc.
+ title = stringValue("full_name"); // THREDDS attribute documented in TITLE javadoc.
if (title == null) {
- title = stringValue("name"); // THREDDS attribute documented in TITLE javadoc.
+ title = stringValue("name"); // THREDDS attribute documented in TITLE javadoc.
if (title == null) {
title = decoder.getTitle();
}
@@ -854,7 +854,8 @@ final class MetadataReader {
String name = variable.getName();
if (name != null && !(name = name.trim()).isEmpty()) {
if (nameFactory == null) {
- nameFactory = DefaultFactories.forBuildin(NameFactory.class); // Real dependency injection to be used in a future version.
+ nameFactory = DefaultFactories.forBuildin(NameFactory.class);
+ // Real dependency injection to be used in a future version.
}
band.setSequenceIdentifier(nameFactory.createMemberName(null, name,
nameFactory.createTypeName(null, variable.getDataTypeName())));
Modified: sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/NetcdfStore.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/NetcdfStore.java?rev=1763840&r1=1763839&r2=1763840&view=diff
==============================================================================
--- sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/NetcdfStore.java [UTF-8] (original)
+++ sis/branches/JDK7/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/NetcdfStore.java [UTF-8] Fri Oct 7 21:35:43 2016
@@ -28,6 +28,9 @@ import org.apache.sis.storage.DataStoreC
import org.apache.sis.storage.StorageConnector;
import org.apache.sis.internal.netcdf.Decoder;
import org.apache.sis.metadata.ModifiableMetadata;
+import org.apache.sis.util.CharSequences;
+import org.apache.sis.util.Version;
+import ucar.nc2.constants.CDM;
/**
@@ -97,6 +100,25 @@ public class NetcdfStore extends DataSto
}
/**
+ * Returns the version number of the Climate and Forecast (CF) conventions used in the NetCDF file.
+ * The use of CF convention is mandated by the OGC 11-165r2 standard
+ * (<cite>CF-netCDF3 Data Model Extension standard</cite>).
+ *
+ * @return CF-convention version, or {@code null} if no information about CF convention has been found.
+ * @throws DataStoreException if an error occurred while reading the data.
+ *
+ * @since 0.8
+ */
+ public synchronized Version getConventionVersion() throws DataStoreException {
+ for (final CharSequence value : CharSequences.split(decoder.stringValue(CDM.CONVENTIONS), ',')) {
+ if (CharSequences.regionMatches(value, 0, "CF-", true)) {
+ return new Version(value.subSequence(3, value.length()).toString());
+ }
+ }
+ return null;
+ }
+
+ /**
* Closes this NetCDF store and releases any underlying resources.
*
* @throws DataStoreException if an error occurred while closing the NetCDF file.
Modified: sis/branches/JDK7/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/DecoderTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/DecoderTest.java?rev=1763840&r1=1763839&r2=1763840&view=diff
==============================================================================
--- sis/branches/JDK7/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/DecoderTest.java [UTF-8] (original)
+++ sis/branches/JDK7/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/DecoderTest.java [UTF-8] Fri Oct 7 21:35:43 2016
@@ -34,7 +34,7 @@ import static org.apache.sis.storage.net
*
* @author Martin Desruisseaux (Geomatys)
* @since 0.3
- * @version 0.3
+ * @version 0.8
* @module
*/
public strictfp class DecoderTest extends TestCase {
@@ -112,4 +112,22 @@ public strictfp class DecoderTest extend
date("1993-04-10 00:00:00")
}, decoder.numberToDate("days since 1970-01-01T00:00:00Z", 8.75, -2.75, 8500));
}
+
+ /**
+ * Tests {@link Decoder#getTitle()} and {@link Decoder#getId()}.
+ *
+ * @throws IOException if an I/O error occurred while opening the file.
+ * @throws DataStoreException if a logical error occurred.
+ */
+ @Test
+ public void testGetTitleAndID() throws IOException, DataStoreException {
+ final Decoder decoder = selectDataset(NCEP);
+ /*
+ * Actually we really want a null value, even if the NCEP file contains 'title' and 'id' attributes,
+ * because the decoder methods are supposed to check only for the "_Title" and "_Id" attributes as a
+ * last resort fallback when MetadataReader failed to find the title and identifier by itself.
+ */
+ assertNull("title", decoder.getTitle());
+ assertNull("id", decoder.getId());
+ }
}
Modified: sis/branches/JDK7/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/GridGeometryTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK7/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/GridGeometryTest.java?rev=1763840&r1=1763839&r2=1763840&view=diff
==============================================================================
--- sis/branches/JDK7/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/GridGeometryTest.java [UTF-8] (original)
+++ sis/branches/JDK7/storage/sis-netcdf/src/test/java/org/apache/sis/internal/netcdf/GridGeometryTest.java [UTF-8] Fri Oct 7 21:35:43 2016
@@ -46,8 +46,8 @@ public strictfp class GridGeometryTest e
* {@code GridGeometryInfoTest} in order to ignore one-dimensional coordinate systems created
* by {@code GridGeometry} but not by the UCAR library.
*
- * @param geometries The grid geometries created by {@link Decoder}.
- * @return The grid geometries to test.
+ * @param geometries the grid geometries created by {@link Decoder}.
+ * @return the grid geometries to test.
*/
protected GridGeometry[] filter(final GridGeometry[] geometries) {
return geometries;