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 2018/12/08 18:01:53 UTC
[sis] 04/04: Implements GridCoverageResource.getSampleDimension()
in GeoTIFF reader too. It provides us more tests.
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 000b6e0c7d37aa0ac01e2de19f0ac97505d05416
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Sat Dec 8 18:55:38 2018 +0100
Implements GridCoverageResource.getSampleDimension() in GeoTIFF reader too. It provides us more tests.
---
.../java/org/apache/sis/coverage/CategoryList.java | 19 +++--
.../org/apache/sis/coverage/SampleDimension.java | 63 +++++++++++-----
.../main/java/org/apache/sis/coverage/ToNaN.java | 10 +++
.../main/java/org/apache/sis/setup/OptionKey.java | 2 +-
.../org/apache/sis/util/resources/Vocabulary.java | 5 ++
.../sis/util/resources/Vocabulary.properties | 1 +
.../sis/util/resources/Vocabulary_fr.properties | 1 +
.../apache/sis/storage/geotiff/GeoTiffStore.java | 83 ++++++++++++++++++++--
.../sis/storage/geotiff/ImageFileDirectory.java | 27 ++++++-
.../apache/sis/storage/netcdf/GridResource.java | 2 +-
.../apache/sis/internal/storage/folder/Store.java | 3 +
.../sis/internal/storage/io/ChannelFactory.java | 15 +++-
.../java/org/apache/sis/storage/Aggregate.java | 2 +-
.../org/apache/sis/storage/StorageConnector.java | 7 +-
14 files changed, 201 insertions(+), 39 deletions(-)
diff --git a/core/sis-raster/src/main/java/org/apache/sis/coverage/CategoryList.java b/core/sis-raster/src/main/java/org/apache/sis/coverage/CategoryList.java
index 8a3ec8a..d85874f 100644
--- a/core/sis-raster/src/main/java/org/apache/sis/coverage/CategoryList.java
+++ b/core/sis-raster/src/main/java/org/apache/sis/coverage/CategoryList.java
@@ -107,10 +107,11 @@ final class CategoryList extends AbstractList<Category> implements MathTransform
* The {@code CategoryList} that describes values after {@linkplain #getTransferFunction() transfer function}
* has been applied, or if this {@code CategoryList} is already converted then the original {@code CategoryList}.
* Never null, but may be {@code this} if the transfer function is the identity function.
+ * May also be {@link #EMPTY} if this category list has no quantitative category.
*
- * <p>This field establishes a bidirectional navigation between sample values and real values.
- * This is in contrast with methods named {@code converted()}, which establish a unidirectional
- * navigation from sample values to real values.</p>
+ * <p>Exempt for the {@link #EMPTY} special case, this field establishes a bidirectional navigation between
+ * sample values and real values. This is in contrast with methods named {@code converted()}, which establish
+ * a unidirectional navigation from sample values to real values.</p>
*
* @see Category#converse
* @see SampleDimension#converse
@@ -198,14 +199,20 @@ final class CategoryList extends AbstractList<Category> implements MathTransform
*/
Category extrapolation = null;
if (converse == null) {
- boolean hasConversion = false;
+ boolean hasConversion = false;
+ boolean hasQuantitative = false;
final Category[] convertedCategories = new Category[categories.length];
for (int i=0; i < convertedCategories.length; i++) {
final Category category = categories[i];
- hasConversion |= (category != category.converse);
+ hasConversion |= (category != category.converse);
+ hasQuantitative |= (category.converse.range != null);
convertedCategories[i] = category.converse;
}
- converse = hasConversion ? new CategoryList(convertedCategories, this) : this;
+ if (hasQuantitative) {
+ converse = hasConversion ? new CategoryList(convertedCategories, this) : this;
+ } else {
+ converse = EMPTY;
+ }
} else {
for (int i=categories.length; --i >= 0;) {
final Category category = categories[i];
diff --git a/core/sis-raster/src/main/java/org/apache/sis/coverage/SampleDimension.java b/core/sis-raster/src/main/java/org/apache/sis/coverage/SampleDimension.java
index d7616b4..cc8bce8 100644
--- a/core/sis-raster/src/main/java/org/apache/sis/coverage/SampleDimension.java
+++ b/core/sis-raster/src/main/java/org/apache/sis/coverage/SampleDimension.java
@@ -176,7 +176,7 @@ public class SampleDimension implements Serializable {
this.name = name;
this.background = background;
this.categories = list;
- if (list.range == null) { // !hasQuantitative() inlined since we can not yet invoke that method.
+ if (list.converse.range == null) { // !hasQuantitative() inlined since we can not yet invoke that method.
transferFunction = null;
converse = null;
} else if (list == list.converse) {
@@ -535,18 +535,30 @@ public class SampleDimension implements Serializable {
}
/**
- * Creates a range for the given number. We use the static factory methods instead than the
- * {@link NumberRange} constructor for sharing existing range instances. This is also a way
+ * Sets the name of the sample dimension as a band number.
+ * This method should be used only when no more descriptive name is available.
+ *
+ * @param band sequence identifier of the sample dimension to create.
+ * @return {@code this}, for method call chaining.
+ */
+ public Builder setName(final int band) {
+ dimensionName = Vocabulary.formatInternational(Vocabulary.Keys.Band_1, band);
+ return this;
+ }
+
+ /**
+ * Creates a range for the given minimum and maximum values. We use the static factory methods instead
+ * than the {@link NumberRange} constructor for sharing existing range instances. This is also a way
* to ensure that the number type is one of the primitive wrappers.
*/
- private static NumberRange<?> range(final Number sample) {
- switch (Numbers.getEnumConstant(sample.getClass())) {
- case Numbers.BYTE: {byte v = sample.byteValue(); return NumberRange.create(v, true, v, true);}
- case Numbers.SHORT: {short v = sample.shortValue(); return NumberRange.create(v, true, v, true);}
- case Numbers.INTEGER: {int v = sample.intValue(); return NumberRange.create(v, true, v, true);}
- case Numbers.LONG: {long v = sample.longValue(); return NumberRange.create(v, true, v, true);}
- case Numbers.FLOAT: {float v = sample.floatValue(); return NumberRange.create(v, true, v, true);}
- default: {double v = sample.doubleValue(); return NumberRange.create(v, true, v, true);}
+ private static NumberRange<?> range(final Class<?> type, final Number minimum, final Number maximum) {
+ switch (Numbers.getEnumConstant(type)) {
+ case Numbers.BYTE: return NumberRange.create(minimum.byteValue(), true, maximum.byteValue(), true);
+ case Numbers.SHORT: return NumberRange.create(minimum.shortValue(), true, maximum.shortValue(), true);
+ case Numbers.INTEGER: return NumberRange.create(minimum.intValue(), true, maximum.intValue(), true);
+ case Numbers.LONG: return NumberRange.create(minimum.longValue(), true, maximum.longValue(), true);
+ case Numbers.FLOAT: return NumberRange.create(minimum.floatValue(), true, maximum.floatValue(), true);
+ default: return NumberRange.create(minimum.doubleValue(), true, maximum.doubleValue(), true);
}
}
@@ -566,7 +578,7 @@ public class SampleDimension implements Serializable {
if (name == null) {
name = Vocabulary.formatInternational(Vocabulary.Keys.FillValue);
}
- final NumberRange<?> samples = range(sample);
+ final NumberRange<?> samples = range(sample.getClass(), sample, sample);
background = samples.getMinValue();
toNaN.background = background.doubleValue();
categories.add(new Category(name, samples, null, null, toNaN));
@@ -673,19 +685,20 @@ public class SampleDimension implements Serializable {
}
/**
- * Adds a qualitative category for samples of the given value.
+ * Adds a qualitative category for samples in the given range of values.
*
* <div class="note"><b>Implementation note:</b>
* this convenience method delegates to {@link #addQualitative(CharSequence, NumberRange)}.</div>
*
- * @param name the category name as a {@link String} or {@link InternationalString} object,
- * or {@code null} for a default "no data" name.
- * @param sample the sample value. Can not be NaN.
+ * @param name the category name as a {@link String} or {@link InternationalString} object,
+ * or {@code null} for a default "no data" name.
+ * @param minimum the minimum sample value, inclusive. Can not be NaN.
+ * @param maximum the maximum sample value, inclusive. Can not be NaN.
* @return {@code this}, for method call chaining.
- * @throws IllegalArgumentException if the given value is NaN.
+ * @throws IllegalArgumentException if a given value is NaN or if the range is empty.
*/
- public Builder addQualitative(final CharSequence name, final Number sample) {
- return addQualitative(name, range(sample));
+ public Builder addQualitative(final CharSequence name, final Number minimum, final Number maximum) {
+ return addQualitative(name, range(Numbers.widestClass(minimum, maximum), minimum, maximum));
}
/**
@@ -850,5 +863,17 @@ defName: if (name == null) {
}
return new SampleDimension(name, background, categories);
}
+
+ /**
+ * Reset this builder to the same state than after construction.
+ * The sample dimension name, background values and all categories are discarded.
+ * This method can be invoked when the same builder is reused for creating more than one sample dimension.
+ */
+ public void clear() {
+ dimensionName = null;
+ background = null;
+ categories.clear();
+ toNaN.clear();
+ }
}
}
diff --git a/core/sis-raster/src/main/java/org/apache/sis/coverage/ToNaN.java b/core/sis-raster/src/main/java/org/apache/sis/coverage/ToNaN.java
index 50d040a..4a3fc3e 100644
--- a/core/sis-raster/src/main/java/org/apache/sis/coverage/ToNaN.java
+++ b/core/sis-raster/src/main/java/org/apache/sis/coverage/ToNaN.java
@@ -49,6 +49,16 @@ final class ToNaN extends HashSet<Integer> implements DoubleToIntFunction {
}
/**
+ * Sets this function to the same state than after construction.
+ * This method is invoked when the same builder is reused for creating many sample dimensions.
+ */
+ @Override
+ public void clear() {
+ super.clear();
+ background = Double.NaN;
+ }
+
+ /**
* Returns a NaN ordinal value for the given sample value.
* The returned value can be given to {@link MathFunctions#toNanFloat(int)}.
*/
diff --git a/core/sis-utility/src/main/java/org/apache/sis/setup/OptionKey.java b/core/sis-utility/src/main/java/org/apache/sis/setup/OptionKey.java
index 63cd642..2aa8cc5 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/setup/OptionKey.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/setup/OptionKey.java
@@ -38,7 +38,7 @@ import org.apache.sis.internal.system.Modules;
* For example most data file formats read by SIS do not require the user to specify the character encoding, since the
* encoding it is often given in the file header or in the format specification. However if SIS needs to read plain
* text files <em>and</em> the default platform encoding is not suitable, then the user can specify the desired encoding
- * explicitely using the {@link #ENCODING} option.
+ * explicitly using the {@link #ENCODING} option.
*
* <p>All options are <em>hints</em> and may be silently ignored. For example most {@code DataStore}s will ignore the
* {@code ENCODING} option if irrelevant to their format, or if the encoding is specified in the data file header.</p>
diff --git a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java
index 83167db..9818540 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java
@@ -107,6 +107,11 @@ public final class Vocabulary extends IndexedResourceBundle {
public static final short AxisChanges = 8;
/**
+ * Band {0}
+ */
+ public static final short Band_1 = 161;
+
+ /**
* Barometric altitude
*/
public static final short BarometricAltitude = 9;
diff --git a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties
index 6577d2f..fe381c3 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties
+++ b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties
@@ -24,6 +24,7 @@ AngularMinutes = Minutes
AngularSeconds = Seconds
Attributes = Attributes
AxisChanges = Axis changes
+Band_1 = Band {0}
BarometricAltitude = Barometric altitude
Cardinality = Cardinality
CausedBy_1 = Caused by {0}
diff --git a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties
index 7fe1ebb..60d3d4e 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties
+++ b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties
@@ -31,6 +31,7 @@ AngularMinutes = Minutes
AngularSeconds = Secondes
Attributes = Attributs
AxisChanges = Changements d\u2019axes
+Band_1 = Bande {0}
BarometricAltitude = Altitude barom\u00e9trique
Cardinality = Cardinalit\u00e9
CausedBy_1 = Caus\u00e9e par {0}
diff --git a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GeoTiffStore.java b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GeoTiffStore.java
index cd8f677..ea256a1 100644
--- a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GeoTiffStore.java
+++ b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/GeoTiffStore.java
@@ -17,10 +17,13 @@
package org.apache.sis.storage.geotiff;
import java.util.Locale;
-import java.io.IOException;
+import java.util.Iterator;
+import java.util.Collection;
+import java.util.NoSuchElementException;
+import java.util.logging.LogRecord;
import java.net.URI;
+import java.io.IOException;
import java.nio.charset.Charset;
-import java.util.logging.LogRecord;
import java.nio.charset.StandardCharsets;
import java.nio.file.StandardOpenOption;
import org.opengis.util.NameSpace;
@@ -31,7 +34,8 @@ import org.opengis.metadata.Metadata;
import org.opengis.metadata.maintenance.ScopeCode;
import org.opengis.parameter.ParameterValueGroup;
import org.apache.sis.setup.OptionKey;
-import org.apache.sis.storage.Resource;
+import org.apache.sis.storage.Aggregate;
+import org.apache.sis.storage.GridCoverageResource;
import org.apache.sis.storage.DataStore;
import org.apache.sis.storage.StorageConnector;
import org.apache.sis.storage.DataStoreException;
@@ -41,6 +45,7 @@ import org.apache.sis.storage.DataStoreClosedException;
import org.apache.sis.storage.IllegalNameException;
import org.apache.sis.storage.event.ChangeEvent;
import org.apache.sis.storage.event.ChangeListener;
+import org.apache.sis.internal.referencing.LazySet;
import org.apache.sis.internal.storage.io.ChannelDataInput;
import org.apache.sis.internal.storage.io.IOUtilities;
import org.apache.sis.internal.storage.MetadataBuilder;
@@ -49,6 +54,7 @@ import org.apache.sis.internal.storage.URIDataStore;
import org.apache.sis.internal.util.Constants;
import org.apache.sis.internal.util.Numerics;
import org.apache.sis.metadata.sql.MetadataStoreException;
+import org.apache.sis.util.collection.BackingStoreException;
import org.apache.sis.util.resources.Errors;
@@ -62,7 +68,7 @@ import org.apache.sis.util.resources.Errors;
* @since 0.8
* @module
*/
-public class GeoTiffStore extends DataStore {
+public class GeoTiffStore extends DataStore implements Aggregate {
/**
* The encoding of strings in the metadata. The string specification said that is shall be US-ASCII,
* but Apache SIS nevertheless let the user specifies an alternative encoding if needed.
@@ -96,6 +102,13 @@ public class GeoTiffStore extends DataStore {
private Metadata metadata;
/**
+ * Description of images in this GeoTIFF files. This collection is created only when first needed.
+ *
+ * @see #components()
+ */
+ private Collection<GridCoverageResource> components;
+
+ /**
* Creates a new GeoTIFF store from the given file, URL or stream object.
* This constructor invokes {@link StorageConnector#closeAllExcept(Object)},
* keeping open only the needed resource.
@@ -139,8 +152,6 @@ public class GeoTiffStore extends DataStore {
* (for example a GeoTIFF file reading directly from a {@link java.nio.channels.ReadableByteChannel}).
*
* @return parameters used for opening this data store, or {@code null} if not available.
- *
- * @since 0.8
*/
@Override
public ParameterValueGroup getOpenParameters() {
@@ -229,6 +240,64 @@ public class GeoTiffStore extends DataStore {
}
/**
+ * Returns descriptions of all images in this GeoTIFF file.
+ * Images are not immediately loaded.
+ *
+ * <p>If an error occurs during iteration in the returned collection,
+ * an unchecked {@link BackingStoreException} will be thrown with a {@link DataStoreException} as its cause.</p>
+ *
+ * @return descriptions of all images in this GeoTIFF file.
+ * @throws DataStoreException if an error occurred while fetching the image descriptions.
+ *
+ * @since 1.0
+ */
+ @Override
+ @SuppressWarnings("ReturnOfCollectionOrArrayField")
+ public Collection<GridCoverageResource> components() throws DataStoreException {
+ if (components == null) {
+ components = new LazySet<GridCoverageResource>(new Iterator<GridCoverageResource>() {
+ /** Index of the next image to fetch, or -1 if we fetched all of them. */
+ private int index;
+
+ /** Value to be returned by {@link #next()}, or {@cod null} if not yet determined. */
+ private GridCoverageResource next;
+
+ /** Returns {@code true} if there is more resources. */
+ @Override public boolean hasNext() {
+ if (next != null) {
+ return true;
+ }
+ final int i = index;
+ if (i >= 0) {
+ index = -1; // Set now in case of failure.
+ try {
+ next = reader().getImageFileDirectory(i);
+ } catch (IOException e) {
+ throw new BackingStoreException(errorIO(e));
+ } catch (DataStoreException e) {
+ throw new BackingStoreException(e);
+ }
+ if (next != null) {
+ index = i+1;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /** Returns the next element. */
+ @Override public GridCoverageResource next() {
+ if (!hasNext()) throw new NoSuchElementException();
+ final GridCoverageResource r = next;
+ next = null;
+ return r;
+ }
+ });
+ }
+ return components; // Safe to return because unmodifiable.
+ }
+
+ /**
* Returns the image at the given index. Images numbering starts at 1.
*
* @param sequence string representation of the image index, starting at 1.
@@ -236,7 +305,7 @@ public class GeoTiffStore extends DataStore {
* @throws DataStoreException if the requested image can not be obtained.
*/
@Override
- public Resource findResource(final String sequence) throws DataStoreException {
+ public GridCoverageResource findResource(final String sequence) throws DataStoreException {
Exception cause;
int index;
try {
diff --git a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/ImageFileDirectory.java b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/ImageFileDirectory.java
index edfb70b..022d5c2 100644
--- a/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/ImageFileDirectory.java
+++ b/storage/sis-geotiff/src/main/java/org/apache/sis/storage/geotiff/ImageFileDirectory.java
@@ -29,15 +29,18 @@ import javax.measure.quantity.Length;
import org.opengis.metadata.citation.DateType;
import org.opengis.util.FactoryException;
import org.opengis.util.GenericName;
+import org.opengis.util.InternationalString;
import org.apache.sis.internal.geotiff.Resources;
import org.apache.sis.internal.storage.MetadataBuilder;
import org.apache.sis.internal.storage.AbstractGridResource;
import org.apache.sis.internal.storage.io.ChannelDataInput;
+import org.apache.sis.internal.util.UnmodifiableArrayList;
import org.apache.sis.storage.DataStoreException;
import org.apache.sis.storage.DataStoreContentException;
import org.apache.sis.coverage.grid.GridGeometry;
import org.apache.sis.coverage.grid.GridExtent;
import org.apache.sis.coverage.SampleDimension;
+import org.apache.sis.util.resources.Vocabulary;
import org.apache.sis.math.Vector;
import org.apache.sis.measure.Units;
@@ -329,10 +332,19 @@ final class ImageFileDirectory extends AbstractGridResource {
* <li>{@link GridGeometryBuilder#asciiParameters}</li>
* <li>{@link GridGeometryBuilder#modelTiePoints}</li>
* </ul>
+ *
+ * @see #getGridGeometry()
*/
private GridGeometryBuilder referencing;
/**
+ * The sample dimensions, or {@code null} if not yet created.
+ *
+ * @see #getSampleDimensions()
+ */
+ private List<SampleDimension> sampleDimensions;
+
+ /**
* Returns {@link #referencing}, created when first needed. We delay its creation since
* this object is not needed for ordinary TIFF files (i.e. without the GeoTIFF extension).
*/
@@ -1248,8 +1260,21 @@ final class ImageFileDirectory extends AbstractGridResource {
* Returns the ranges of sample values together with the conversion from samples to real values.
*/
@Override
+ @SuppressWarnings("ReturnOfCollectionOrArrayField")
public List<SampleDimension> getSampleDimensions() throws DataStoreContentException {
- throw new DataStoreContentException("Not supported yet.");
+ if (sampleDimensions == null) {
+ final SampleDimension[] dimensions = new SampleDimension[samplesPerPixel];
+ final SampleDimension.Builder builder = new SampleDimension.Builder();
+ final InternationalString name = Vocabulary.formatInternational(Vocabulary.Keys.Value);
+ for (int band = 0; band < samplesPerPixel;) {
+ builder.addQualitative(name, minValues.get(Math.min(band, minValues.size()-1)),
+ maxValues.get(Math.min(band, maxValues.size()-1)));
+ dimensions[band] = builder.setName(++band).build();
+ builder.clear();
+ }
+ sampleDimensions = UnmodifiableArrayList.wrap(dimensions);
+ }
+ return sampleDimensions; // Safe because unmodifiable.
}
/**
diff --git a/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/GridResource.java b/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/GridResource.java
index 68906c6..6b1ba58 100644
--- a/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/GridResource.java
+++ b/storage/sis-netcdf/src/main/java/org/apache/sis/storage/netcdf/GridResource.java
@@ -210,7 +210,7 @@ final class GridResource extends AbstractGridResource implements ResourceOnFileS
isFillValue = false; // Declare only one fill value.
builder.setBackground(name, n);
} else {
- builder.addQualitative(name, n);
+ builder.addQualitative(name, n, n);
}
}
}
diff --git a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/Store.java b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/Store.java
index 61e1842..1bc6157 100644
--- a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/Store.java
+++ b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/Store.java
@@ -32,6 +32,7 @@ import java.nio.file.DirectoryStream;
import java.nio.file.DirectoryIteratorException;
import java.io.IOException;
import java.io.UncheckedIOException;
+import java.nio.file.OpenOption;
import org.opengis.util.GenericName;
import org.opengis.util.NameFactory;
import org.opengis.util.NameSpace;
@@ -53,6 +54,7 @@ import org.apache.sis.internal.system.DefaultFactories;
import org.apache.sis.internal.storage.MetadataBuilder;
import org.apache.sis.internal.storage.StoreUtilities;
import org.apache.sis.internal.storage.StoreResource;
+import org.apache.sis.internal.storage.io.ChannelFactory;
import org.apache.sis.internal.storage.Resources;
import org.apache.sis.storage.event.ChangeEvent;
import org.apache.sis.storage.event.ChangeListener;
@@ -310,6 +312,7 @@ class Store extends DataStore implements StoreResource, Aggregate, DirectoryStre
connector.setOption(OptionKey.LOCALE, locale);
connector.setOption(OptionKey.TIMEZONE, timezone);
connector.setOption(OptionKey.ENCODING, encoding);
+ connector.setOption(OptionKey.OPEN_OPTIONS, new OpenOption[] {ChannelFactory.REQUIRE_REGULAR_FILE});
try {
if (componentProvider == null) {
next = DataStores.open(connector); // May throw UnsupportedStorageException.
diff --git a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelFactory.java b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelFactory.java
index 0067cc2..a01fe08 100644
--- a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelFactory.java
+++ b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelFactory.java
@@ -68,7 +68,7 @@ import org.apache.sis.storage.ForwardOnlyStorageException;
*
* @author Martin Desruisseaux (Geomatys)
* @author Johann Sorel (Geomatys)
- * @version 0.8
+ * @version 1.0
* @since 0.8
* @module
*/
@@ -80,6 +80,17 @@ public abstract class ChannelFactory {
StandardOpenOption.APPEND, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.DELETE_ON_CLOSE);
/**
+ * A customized option for instructing {@link #prepare(Object, String, boolean, OpenOption...)}
+ * to not try to open directories or non-existent files. By default, {@link Path} to non-regular
+ * files will cause an {@link IOException} to be thrown. But if this option is provided, then we
+ * will rather behave as if {@link Path} was an unknown type. This will cause store providers to
+ * not try to open that file, which gives the caller a chance to fallback on its own process.
+ */
+ public static final OpenOption REQUIRE_REGULAR_FILE = new OpenOption() {
+ @Override public String toString() {return "REQUIRE_REGULAR_FILE";}
+ };
+
+ /**
* For subclass constructors.
*/
ChannelFactory() {
@@ -240,7 +251,7 @@ public abstract class ChannelFactory {
}
if (storage instanceof Path) {
final Path path = (Path) storage;
- if (Files.isRegularFile(path)) {
+ if (!optionSet.remove(REQUIRE_REGULAR_FILE) || Files.isRegularFile(path)) {
return new ChannelFactory() {
@Override public ReadableByteChannel readable(String filename, WarningListeners<DataStore> listeners) throws IOException {
return Files.newByteChannel(path, optionSet);
diff --git a/storage/sis-storage/src/main/java/org/apache/sis/storage/Aggregate.java b/storage/sis-storage/src/main/java/org/apache/sis/storage/Aggregate.java
index b977658..ef5a8e7 100644
--- a/storage/sis-storage/src/main/java/org/apache/sis/storage/Aggregate.java
+++ b/storage/sis-storage/src/main/java/org/apache/sis/storage/Aggregate.java
@@ -85,5 +85,5 @@ public interface Aggregate extends Resource {
* @return all children resources that are components of this aggregate. Never {@code null}.
* @throws DataStoreException if an error occurred while fetching the components.
*/
- Collection<Resource> components() throws DataStoreException;
+ Collection<? extends Resource> components() throws DataStoreException;
}
diff --git a/storage/sis-storage/src/main/java/org/apache/sis/storage/StorageConnector.java b/storage/sis-storage/src/main/java/org/apache/sis/storage/StorageConnector.java
index f80143b..2360335 100644
--- a/storage/sis-storage/src/main/java/org/apache/sis/storage/StorageConnector.java
+++ b/storage/sis-storage/src/main/java/org/apache/sis/storage/StorageConnector.java
@@ -32,6 +32,7 @@ import java.nio.ByteBuffer;
import java.nio.channels.Channel;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.SeekableByteChannel;
+import java.nio.file.NoSuchFileException;
import javax.imageio.stream.ImageInputStream;
import javax.imageio.ImageIO;
import java.sql.Connection;
@@ -836,7 +837,11 @@ public class StorageConnector implements Serializable {
} catch (DataStoreException e) {
throw e;
} catch (Exception e) {
- throw new DataStoreException(Errors.format(Errors.Keys.CanNotOpen_1, getStorageName()), e);
+ short key = Errors.Keys.CanNotOpen_1;
+ if (e instanceof NoSuchFileException) {
+ key = Errors.Keys.FileNotFound_1;
+ }
+ throw new DataStoreException(Errors.format(key, getStorageName()), e);
}
return type.cast(view);
}