Modified: sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelFactory.java URL: http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelFactory.java?rev=1807624&r1=1807623&r2=1807624&view=diff ============================================================================== --- sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelFactory.java [UTF-8] (original) +++ sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelFactory.java [UTF-8] Thu Sep 7 15:47:24 2017 @@ -45,6 +45,8 @@ import org.apache.sis.util.logging.Loggi import org.apache.sis.util.resources.Errors; import org.apache.sis.internal.system.Modules; import org.apache.sis.internal.storage.Resources; +import org.apache.sis.storage.DataStoreException; +import org.apache.sis.storage.ForwardOnlyStorageException; /** @@ -244,6 +246,18 @@ public abstract class ChannelFactory { } /** + * Returns whether the streams or channels created by this factory is coupled with the {@code storage} argument + * given to the {@link #prepare prepare(…)} method. This is {@code true} if the storage is an {@link InputStream}, + * {@link OutputStream} or {@link Channel}, and {@code false} if the storage is a {@link Path}, {@link File}, + * {@link URL}, {@link URI} or equivalent. + * + * @return whether using the streams or channels will affect the original {@code storage} object. + */ + public boolean isCoupled() { + return false; + } + + /** * Returns {@code true} if this factory is capable to create another reader. This method returns {@code false} * if this factory is capable to create only one channel and {@link #reader(String)} has already been invoked. * @@ -259,9 +273,10 @@ public abstract class ChannelFactory { * * @param filename data store name to report in case of failure. * @return the input stream. + * @throws DataStoreException if the channel is read-once. * @throws IOException if the input stream or its underlying byte channel can not be created. */ - public InputStream inputStream(final String filename) throws IOException { + public InputStream inputStream(final String filename) throws DataStoreException, IOException { return Channels.newInputStream(reader(filename)); } @@ -271,9 +286,10 @@ public abstract class ChannelFactory { * * @param filename data store name to report in case of failure. * @return the output stream. + * @throws DataStoreException if the channel is write-once. * @throws IOException if the output stream or its underlying byte channel can not be created. */ - public OutputStream outputStream(final String filename) throws IOException { + public OutputStream outputStream(final String filename) throws DataStoreException, IOException { return Channels.newOutputStream(writer(filename)); } @@ -284,9 +300,10 @@ public abstract class ChannelFactory { * * @param filename data store name to report in case of failure. * @return the channel for the given input. + * @throws DataStoreException if the channel is read-once. * @throws IOException if an error occurred while opening the channel. */ - public abstract ReadableByteChannel reader(String filename) throws IOException; + public abstract ReadableByteChannel reader(String filename) throws DataStoreException, IOException; /** * Returns a byte channel from the output given to the {@link #prepare prepare(…)} method. @@ -295,9 +312,10 @@ public abstract class ChannelFactory { * * @param filename data store name to report in case of failure. * @return the channel for the given output. + * @throws DataStoreException if the channel is write-once. * @throws IOException if an error occurred while opening the channel. */ - public abstract WritableByteChannel writer(String filename) throws IOException; + public abstract WritableByteChannel writer(String filename) throws DataStoreException, IOException; /** * A factory that returns an existing channel <cite>as-is</cite>. @@ -317,6 +335,14 @@ public abstract class ChannelFactory { } /** + * Returns {@code true} since use of channels or streams will affect the original storage object. + */ + @Override + public boolean isCoupled() { + return true; + } + + /** * Returns whether {@link #reader(String)} or {@link #writer(String)} can be invoked. */ @Override @@ -329,13 +355,19 @@ public abstract class ChannelFactory { * throws an exception on all subsequent invocations. */ @Override - public ReadableByteChannel reader(final String filename) throws IOException { + public ReadableByteChannel reader(final String filename) throws DataStoreException, IOException { final Channel in = channel; if (in instanceof ReadableByteChannel) { channel = null; return (ReadableByteChannel) in; } - throw new IOException(Resources.format(Resources.Keys.StreamIsReadOnce_1, filename)); + String message = Resources.format(in != null ? Resources.Keys.StreamIsNotReadable_1 + : Resources.Keys.StreamIsReadOnce_1, filename); + if (in != null) { + throw new IOException(message); // Stream is not readable. + } else { + throw new ForwardOnlyStorageException(message); // Stream has already been read. + } } /** @@ -343,13 +375,19 @@ public abstract class ChannelFactory { * throws an exception on all subsequent invocations. */ @Override - public WritableByteChannel writer(final String filename) throws IOException { - final Channel in = channel; - if (in instanceof WritableByteChannel) { + public WritableByteChannel writer(final String filename) throws DataStoreException, IOException { + final Channel out = channel; + if (out instanceof WritableByteChannel) { channel = null; - return (WritableByteChannel) in; + return (WritableByteChannel) out; + } + String message = Resources.format(out != null ? Resources.Keys.StreamIsNotWritable_1 + : Resources.Keys.StreamIsWriteOnce_1, filename); + if (out != null) { + throw new IOException(message); // Stream is not writable. + } else { + throw new ForwardOnlyStorageException(message); // Stream has already been written. } - throw new IOException(Resources.format(Resources.Keys.StreamIsWriteOnce_1, filename)); } } @@ -357,7 +395,7 @@ public abstract class ChannelFactory { * A factory used as a fallback when we failed to convert a {@link File} to a {@link Path}. * This is used only if the conversion attempt threw an {@link InvalidPathException}. Such * failure is unlikely to happen, but if it happens anyway we try to open the channel in a - * way less surprising for the user (closer to the object he has specified). + * less surprising way for the user (i.e. closer to the object (s)he has specified). */ private static final class Fallback extends ChannelFactory { /**
Modified: sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelImageInputStream.java URL: http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelImageInputStream.java?rev=1807624&r1=1807623&r2=1807624&view=diff ============================================================================== --- sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelImageInputStream.java [UTF-8] (original) +++ sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelImageInputStream.java [UTF-8] Thu Sep 7 15:47:24 2017 @@ -344,6 +344,14 @@ loop: while ((c = read()) >= 0) { /** * Closes the {@linkplain #channel}. + * If the channel is backed by an {@link java.io.InputStream}, that stream will be closed too. + * + * <div class="section">Departure from Image I/O standard implementation</div> + * Java Image I/O wrappers around input/output streams do not close the underlying stream (see for example + * {@link javax.imageio.stream.FileCacheImageInputStream#close()} specification). But Apache SIS needs the + * underlying stream to be closed because we do not keep reference to the original input stream. Note that + * channels created by {@link java.nio.channels.Channels#newChannel(java.io.InputStream)} close the stream, + * which is the desired behavior for this method. * * @throws IOException if an error occurred while closing the channel. */ Modified: sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/IOUtilities.java URL: http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/IOUtilities.java?rev=1807624&r1=1807623&r2=1807624&view=diff ============================================================================== --- sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/IOUtilities.java [UTF-8] (original) +++ sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/IOUtilities.java [UTF-8] Thu Sep 7 15:47:24 2017 @@ -20,6 +20,7 @@ import java.util.Locale; import java.io.File; import java.io.FileInputStream; import java.io.LineNumberReader; +import java.io.Reader; import java.io.InputStream; import java.io.OutputStream; import java.io.IOException; @@ -236,7 +237,7 @@ public final class IOUtilities extends S return null; } /* - * Convert the URL to an URI, taking in account the encoding if any. + * Convert the URL to a URI, taking in account the encoding if any. * * Note: URL.toURI() is implemented as new URI(URL.toString()) where toString() * delegates to toExternalForm(), and all those methods are final. So we really @@ -528,6 +529,30 @@ public final class IOUtilities extends S } /** + * Reads the next character as an Unicode code point. Unless end-of-file has been reached, the returned value is + * between {@value java.lang.Character#MIN_CODE_POINT} and {@value java.lang.Character#MAX_CODE_POINT} inclusive. + * + * @param in the reader from which to read code point. + * @return the next code point, or -1 on end of file. + * @throws IOException if an error occurred while reading characters. + * + * @since 0.8 + */ + public static int readCodePoint(final Reader in) throws IOException { + int c = in.read(); + while (c >= Character.MIN_HIGH_SURROGATE && c <= Character.MAX_HIGH_SURROGATE) { + final int low = in.read(); + if (low >= Character.MIN_LOW_SURROGATE && low <= Character.MAX_LOW_SURROGATE) { + c = Character.toCodePoint((char) c, (char) low); + break; + } else { + c = low; // Discard orphan high surrogate and take the next character. + } + } + return c; + } + + /** * Returns the error message for a file that can not be parsed. * The error message will contain the line number if available. * Modified: sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/InputStreamAdapter.java URL: http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/InputStreamAdapter.java?rev=1807624&r1=1807623&r2=1807624&view=diff ============================================================================== --- sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/InputStreamAdapter.java [UTF-8] (original) +++ sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/InputStreamAdapter.java [UTF-8] Thu Sep 7 15:47:24 2017 @@ -20,11 +20,14 @@ import java.io.InputStream; import java.io.IOException; import javax.imageio.stream.ImageInputStream; +// Branch-dependent imports +import java.io.UncheckedIOException; + /** * Wraps an {@link ImageInputStream} as a standard {@link InputStream}. * - * @author Martin Desruisseaux (IRD) + * @author Martin Desruisseaux (IRD, Geomatys) * @version 0.8 * * @see OutputStreamAdapter @@ -41,13 +44,27 @@ public final class InputStreamAdapter ex public final ImageInputStream input; /** + * Position of the last mark created by {@link #mark(int)}, or the file beginning if there is no mark. + */ + private long markPosition; + + /** + * Count of marks created by {@link #mark()}, not counting the mark created by {@link #mark(int)}. + * We have to keep this count ourselves because {@link ImageInputStream#reset()} does nothing if + * there is no mark, and provides no API for letting us know if {@code reset()} worked. + */ + private int nestedMarks; + + /** * Constructs a new input stream. * - * @param input the stream to wrap. + * @param input the stream to wrap. + * @throws IOException if an error occurred while creating the adapter. */ - public InputStreamAdapter(final ImageInputStream input) { + public InputStreamAdapter(final ImageInputStream input) throws IOException { assert !(input instanceof InputStream); this.input = input; + markPosition = input.getStreamPosition(); } /** @@ -105,31 +122,60 @@ public final class InputStreamAdapter ex } /** - * Marks the current position in this input stream. + * Discards all previous marks and marks the current position in this input stream. + * This method is part of {@link InputStream} API, where only one mark can be set and multiple + * calls to {@code reset()} move to the same position until {@code mark(int)} is invoked again. * * @param readlimit ignored. + * @throws UncheckedIOException if the mark can not be set. */ @Override public void mark(final int readlimit) { - input.mark(); + try { + markPosition = input.getStreamPosition(); + input.flushBefore(markPosition); + nestedMarks = 0; + } catch (IOException e) { + throw new UncheckedIOException(e); // InputStream.mark() does not allow us to throw IOException. + } } /** * Marks the current position in this input stream. + * This method is part of {@link Markable} API, where marks can be nested. + * It is okay to invoke this method after {@link #mark(int)} (but not before). */ @Override public void mark() { input.mark(); + nestedMarks++; } /** * Repositions this stream to the position at the time the {@code mark} method was last called. + * This method has to comply with both {@link InputStream#reset()} and {@link Markable#reset()} + * contracts. It does that by choosing the first option in following list: + * + * <ul> + * <li>If there is nested {@link #mark()} calls, then this {@code reset()} method sets the stream + * position to the most recent unmatched call to {@code mark()}.</li> + * <li>Otherwise if the {@link #mark(int)} method has been invoked, then this method sets the stream + * position to the mark created by the most recent call to {@code mark(int)}. The {@code reset()} + * method can be invoked many time; it will always set the position to the same mark + * (this behavior is required by {@link InputStream} contract).</li> + * <li>Otherwise this method sets the stream position to the position it had when this + * {@code InputStreamAdapter} has been created.</li> + * </ul> * * @throws IOException if an I/O error occurs. */ @Override public void reset() throws IOException { - input.reset(); + if (--nestedMarks >= 0) { + input.reset(); + } else { + input.seek(markPosition); + } } /** Modified: sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/Markable.java URL: http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/Markable.java?rev=1807624&r1=1807623&r2=1807624&view=diff ============================================================================== --- sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/Markable.java [UTF-8] (original) +++ sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/Markable.java [UTF-8] Thu Sep 7 15:47:24 2017 @@ -17,7 +17,6 @@ package org.apache.sis.internal.storage.io; import java.io.IOException; -import java.nio.InvalidMarkException; /** @@ -56,6 +55,8 @@ public interface Markable { * Calls to {@code mark()} and {@code reset()} can be nested arbitrarily. * * @throws IOException if this stream can not mark the current position. + * + * @see javax.imageio.stream.ImageInputStream#mark() */ void mark() throws IOException; @@ -64,8 +65,14 @@ public interface Markable { * An {@code IOException} may be be thrown if the previous marked position lies in the * discarded portion of the stream. * - * @throws InvalidMarkException if there is no mark. + * <p>If there is no mark, then the behavior is undefined. + * {@link java.io.InputStream#reset()} specifies that we shall move to the file beginning. + * {@link javax.imageio.stream.ImageInputStream#reset()} specifies that we shall do nothing. + * {@link ChannelDataInput#reset()} throws {@link java.nio.InvalidMarkException}.</p> + * * @throws IOException if a mark was defined but this stream can not move to that position. + * + * @see javax.imageio.stream.ImageInputStream#reset() */ void reset() throws IOException; } Modified: sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/FirstKeywordPeek.java URL: http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/FirstKeywordPeek.java?rev=1807624&r1=1807623&r2=1807624&view=diff ============================================================================== --- sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/FirstKeywordPeek.java [UTF-8] (original) +++ sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/FirstKeywordPeek.java [UTF-8] Thu Sep 7 15:47:24 2017 @@ -19,6 +19,7 @@ package org.apache.sis.internal.storage. import java.io.Reader; import java.io.IOException; import java.nio.ByteBuffer; +import org.apache.sis.internal.storage.io.IOUtilities; import org.apache.sis.storage.DataStoreException; import org.apache.sis.storage.StorageConnector; import org.apache.sis.storage.ProbeResult; @@ -41,6 +42,7 @@ public abstract class FirstKeywordPeek { /** * The read-ahead limit when reading a text from a {@link Reader}. + * Should be no more than {@code StorageConnector.DEFAULT_BUFFER_SIZE / 2}. */ static final int READ_AHEAD_LIMIT = 2048; @@ -78,7 +80,7 @@ public abstract class FirstKeywordPeek { return -1; } int c; - while ((c = reader.read()) >= 0) { + while ((c = IOUtilities.readCodePoint(reader)) >= 0) { if (!Character.isWhitespace(c)) break; } return c; @@ -94,7 +96,7 @@ public abstract class FirstKeywordPeek { if (!buffer.hasRemaining()) break; c = (char) buffer.get(); } else { - c = reader.read(); + c = IOUtilities.readCodePoint(reader); if (c < 0) break; } } while (!Characters.isLineOrParagraphSeparator(c)); @@ -159,7 +161,7 @@ public abstract class FirstKeywordPeek { } keyword[pos++] = (char) c; } - c = (buffer == null) ? reader.read() : buffer.hasRemaining() ? (char) buffer.get() : -1; + c = (buffer == null) ? IOUtilities.readCodePoint(reader) : buffer.hasRemaining() ? (char) buffer.get() : -1; } while ((s = isKeywordChar(c)) >= ACCEPT); /* * At this point we finished to read and store the keyword. @@ -187,7 +189,7 @@ public abstract class FirstKeywordPeek { } /** - * Returns {@code true} if the given first non-white character after the keywordis one of the expected characters. + * Returns {@code true} if the given first non-white character after the keyword is one of the expected characters. * * @param c the first non-white character after the keyword, or -1 if we reached the end of stream. * @return {@code true} if the given character is one of the expected post-keyword characters. Modified: sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/Store.java URL: http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/Store.java?rev=1807624&r1=1807623&r2=1807624&view=diff ============================================================================== --- sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/Store.java [UTF-8] (original) +++ sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/Store.java [UTF-8] Thu Sep 7 15:47:24 2017 @@ -33,14 +33,14 @@ import org.apache.sis.internal.storage.R import org.apache.sis.internal.system.Loggers; import org.apache.sis.io.wkt.WKTFormat; import org.apache.sis.io.wkt.Warnings; -import org.apache.sis.storage.Resource; -import org.apache.sis.storage.DataStore; import org.apache.sis.storage.StorageConnector; import org.apache.sis.storage.DataStoreException; import org.apache.sis.storage.DataStoreContentException; +import org.apache.sis.storage.UnsupportedStorageException; import org.apache.sis.internal.referencing.DefinitionVerifier; import org.apache.sis.internal.storage.MetadataBuilder; -import org.apache.sis.util.resources.Errors; +import org.apache.sis.internal.storage.URIDataStore; +import org.apache.sis.setup.OptionKey; import org.apache.sis.util.CharSequences; @@ -52,7 +52,7 @@ import org.apache.sis.util.CharSequences * @since 0.7 * @module */ -final class Store extends DataStore { +final class Store extends URIDataStore { /** * Arbitrary size limit. Files that big are likely to be something else than WKT, * so this limit allows earlier error reporting than loading huge amount of data @@ -89,7 +89,8 @@ final class Store extends DataStore { source = connector.getStorageAs(Reader.class); connector.closeAllExcept(source); if (source == null) { - throw new DataStoreException(Errors.format(Errors.Keys.CanNotOpen_1, super.getDisplayName())); + throw new UnsupportedStorageException(super.getLocale(), StoreProvider.NAME, + connector.getStorage(), connector.getOption(OptionKey.OPEN_OPTIONS)); } } @@ -194,15 +195,6 @@ final class Store extends DataStore { } /** - * There is currently no resource associated to Well Known Text format since we parse only CRS. - * Future versions may return resources if we parse also geometries. - */ - @Override - public Resource getRootResource() throws DataStoreException { - return null; - } - - /** * Closes this data store and releases any underlying resources. * * @throws DataStoreException if an error occurred while closing this data store. Modified: sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/StoreProvider.java URL: http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/StoreProvider.java?rev=1807624&r1=1807623&r2=1807624&view=diff ============================================================================== --- sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/StoreProvider.java [UTF-8] (original) +++ sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/StoreProvider.java [UTF-8] Thu Sep 7 15:47:24 2017 @@ -20,12 +20,12 @@ import java.util.Collections; import java.util.HashSet; import java.util.Set; import org.apache.sis.storage.DataStore; -import org.apache.sis.storage.DataStoreProvider; import org.apache.sis.storage.DataStoreException; import org.apache.sis.storage.StorageConnector; import org.apache.sis.storage.ProbeResult; import org.apache.sis.internal.storage.Capability; import org.apache.sis.internal.storage.Capabilities; +import org.apache.sis.internal.storage.URIDataStore; import org.apache.sis.internal.metadata.WKTKeywords; import org.apache.sis.util.Version; @@ -39,7 +39,12 @@ import org.apache.sis.util.Version; * @module */ @Capabilities(Capability.READ) -public final class StoreProvider extends DataStoreProvider { +public final class StoreProvider extends URIDataStore.Provider { + /** + * The format name. + */ + static final String NAME = "WKT"; + /** * The {@value} MIME type. */ @@ -152,7 +157,7 @@ public final class StoreProvider extends */ @Override public String getShortName() { - return "WKT"; + return NAME; } /** Modified: sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/AbstractProvider.java URL: http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/AbstractProvider.java?rev=1807624&r1=1807623&r2=1807624&view=diff ============================================================================== --- sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/AbstractProvider.java [UTF-8] (original) +++ sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/AbstractProvider.java [UTF-8] Thu Sep 7 15:47:24 2017 @@ -25,6 +25,7 @@ import org.apache.sis.storage.DataStore; import org.apache.sis.storage.DataStoreException; import org.apache.sis.storage.StorageConnector; import org.apache.sis.storage.ProbeResult; +import org.apache.sis.internal.storage.io.IOUtilities; import org.apache.sis.internal.storage.DocumentedStoreProvider; @@ -138,7 +139,7 @@ public abstract class AbstractProvider e final ProbeResult result = new MimeTypeDetector(types) { private int remaining = READ_AHEAD_LIMIT; @Override int read() throws IOException { - return (--remaining >= 0) ? reader.read() : -1; + return (--remaining >= 0) ? IOUtilities.readCodePoint(reader) : -1; } }.probeContent(); reader.reset(); Modified: sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/MimeTypeDetector.java URL: http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/MimeTypeDetector.java?rev=1807624&r1=1807623&r2=1807624&view=diff ============================================================================== --- sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/MimeTypeDetector.java [UTF-8] (original) +++ sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/MimeTypeDetector.java [UTF-8] Thu Sep 7 15:47:24 2017 @@ -98,7 +98,7 @@ abstract class MimeTypeDetector { /** * Reads a single byte or character, or -1 if we reached the end of the stream portion that we are allowed * to read. We are typically not allowed to read the full stream because only a limited amount of bytes is - * cached. + * cached. This method may return a Unicode code point (i.e. the returned value may not fit in {@code char}). * * @return the character, or -1 on EOF. * @throws IOException if an error occurred while reading the byte or character. Modified: sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/Store.java URL: http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/Store.java?rev=1807624&r1=1807623&r2=1807624&view=diff ============================================================================== --- sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/Store.java [UTF-8] (original) +++ sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/Store.java [UTF-8] Thu Sep 7 15:47:24 2017 @@ -30,16 +30,17 @@ import org.opengis.util.FactoryException import org.opengis.referencing.ReferenceSystem; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.apache.sis.xml.XML; -import org.apache.sis.storage.Resource; -import org.apache.sis.storage.DataStore; import org.apache.sis.storage.StorageConnector; import org.apache.sis.storage.DataStoreException; +import org.apache.sis.storage.UnsupportedStorageException; import org.apache.sis.metadata.iso.DefaultMetadata; import org.apache.sis.util.logging.WarningListener; import org.apache.sis.util.resources.Errors; import org.apache.sis.internal.system.Loggers; +import org.apache.sis.internal.storage.URIDataStore; import org.apache.sis.internal.storage.MetadataBuilder; import org.apache.sis.internal.referencing.DefinitionVerifier; +import org.apache.sis.setup.OptionKey; /** @@ -59,7 +60,7 @@ import org.apache.sis.internal.referenci * @since 0.4 * @module */ -final class Store extends DataStore { +final class Store extends URIDataStore { /** * The input stream or reader, set by the constructor and cleared when no longer needed. */ @@ -97,7 +98,8 @@ final class Store extends DataStore { final Closeable c = input(source); connector.closeAllExcept(c); if (c == null) { - throw new DataStoreException(Errors.format(Errors.Keys.CanNotOpen_1, super.getDisplayName())); + throw new UnsupportedStorageException(super.getLocale(), StoreProvider.NAME, + connector.getStorage(), connector.getOption(OptionKey.OPEN_OPTIONS)); } } @@ -209,15 +211,6 @@ final class Store extends DataStore { } /** - * Current implementation does not provide any resource since it is only about metadata. - * Futures versions may return resources if Apache SIS provides a wider GML support. - */ - @Override - public Resource getRootResource() throws DataStoreException { - return null; - } - - /** * Closes this data store and releases any underlying resources. * * @throws DataStoreException if an error occurred while closing this data store. Modified: sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/StoreProvider.java URL: http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/StoreProvider.java?rev=1807624&r1=1807623&r2=1807624&view=diff ============================================================================== --- sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/StoreProvider.java [UTF-8] (original) +++ sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/xml/StoreProvider.java [UTF-8] Thu Sep 7 15:47:24 2017 @@ -35,6 +35,11 @@ import org.apache.sis.internal.storage.C @Capabilities(Capability.READ) public final class StoreProvider extends AbstractProvider { /** + * The format name. + */ + static final String NAME = "XML"; + + /** * Creates a new provider. */ public StoreProvider() { @@ -52,7 +57,7 @@ public final class StoreProvider extends */ @Override public String getShortName() { - return "XML"; + return NAME; } /** Modified: sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/Aggregate.java URL: http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/Aggregate.java?rev=1807624&r1=1807623&r2=1807624&view=diff ============================================================================== --- sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/Aggregate.java [UTF-8] (original) +++ sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/Aggregate.java [UTF-8] Thu Sep 7 15:47:24 2017 @@ -17,6 +17,9 @@ package org.apache.sis.storage; import java.util.Collection; +import org.opengis.metadata.Metadata; +import org.opengis.referencing.crs.CoordinateReferenceSystem; +import org.apache.sis.internal.storage.Resources; /** @@ -42,7 +45,7 @@ import java.util.Collection; * * The same resource may be part of more than one aggregate. For example the same resource could be part of * a <cite>production series</cite> and a <cite>transfer aggregate</cite>. In Apache SIS implementation, - * those two kinds of aggregate will usually be created by different {@link DataStore} instances. + * those two kinds of aggregate will usually be implemented by different {@link DataStore} instances. * * <div class="section">Metadata</div> * Aggregates should have {@link #getMetadata() metadata} / @@ -82,4 +85,47 @@ public interface Aggregate extends Resou * @throws DataStoreException if an error occurred while fetching the components. */ Collection<Resource> components() throws DataStoreException; + + /** + * Adds a new {@code Resource} in this {@code Aggregate}. + * The given {@link Resource} will be copied, and the <cite>effectively added</cite> resource returned. + * The effectively added resource may differ from the given resource in many aspects. + * The possible changes may include the followings but not only: + * <ul> + * <li>types and properties names</li> + * <li>{@link CoordinateReferenceSystem}</li> + * <li>{@link Metadata}</li> + * </ul> + * + * <div class="note"><b>Warning:</b> + * copying informations between stores may produce differences in many aspects. + * The range of changes depends both on the original {@link Resource} structure + * and the target {@code Resource} structure. If the differences are too large, + * then this {@code Aggregate} may throw an exception. + * </div> + * + * <p>The default implementation throws {@link ReadOnlyStorageException}.</p> + * + * @param resource the resource to copy in this {@code Aggregate}. + * @return the effectively added resource. May be {@code resource} itself if it has been added verbatim. + * @throws ReadOnlyStorageException if this instance does not support write operations. + * @throws DataStoreException if the given resource can not be stored in this {@code Aggregate} for another reason. + */ + default Resource add(Resource resource) throws ReadOnlyStorageException, DataStoreException { + throw new ReadOnlyStorageException(this, Resources.Keys.StoreIsReadOnly); + } + + /** + * Removes a {@code Resource} from this {@code Aggregate}. + * This operation is destructive: the {@link Resource} and it's related data will be removed. + * + * <p>The default implementation throws {@link ReadOnlyStorageException}.</p> + * + * @param resource child resource to remove, should not be null. + * @throws ReadOnlyStorageException if this instance does not support write operations. + * @throws DataStoreException if the given resource could not be removed for another reason. + */ + default void remove(Resource resource) throws ReadOnlyStorageException, DataStoreException { + throw new ReadOnlyStorageException(this, Resources.Keys.StoreIsReadOnly); + } } Modified: sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/DataSet.java URL: http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/DataSet.java?rev=1807624&r1=1807623&r2=1807624&view=diff ============================================================================== --- sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/DataSet.java [UTF-8] (original) +++ sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/DataSet.java [UTF-8] Thu Sep 7 15:47:24 2017 @@ -16,6 +16,8 @@ */ package org.apache.sis.storage; +import org.opengis.geometry.Envelope; + /** * Collection of features that share a common set of attributes or properties. @@ -45,4 +47,28 @@ package org.apache.sis.storage; * @module */ public interface DataSet extends Resource { + /** + * Returns the spatio-temporal extent of this resource in its most natural coordinate reference system. + * The following relationship to {@linkplain #getMetadata()} should hold: + * + * <ul> + * <li>The envelope should be contained in the union of all geographic, vertical or temporal extents + * described by {@code metadata} / + * {@link org.apache.sis.metadata.iso.DefaultMetadata#getIdentificationInfo() identificationInfo} / + * {@link org.apache.sis.metadata.iso.identification.AbstractIdentification#getExtents() extent}.</li> + * <li>The coordinate reference system should be one of the instances returned by + * {@link org.apache.sis.metadata.iso.DefaultMetadata#getReferenceSystemInfo() referenceSystemInfo}.</li> + * </ul> + * + * The envelope should use the coordinate reference system (CRS) + * that most closely matches the geometry of the resource storage. It is often a + * {@linkplain org.apache.sis.referencing.crs.DefaultProjectedCRS projected CRS}, but other types like + * {@linkplain org.apache.sis.referencing.crs.DefaultEngineeringCRS engineering CRS} are also allowed. + * If this resource uses many different CRS with none of them covering all data, then the envelope should use a + * global system (typically a {@linkplain org.apache.sis.referencing.crs.DefaultGeocentricCRS geographic CRS}). + * + * @return the spatio-temporal resource extent. Should not be {@code null}. + * @throws DataStoreException if an error occurred while reading or computing the envelope. + */ + Envelope getEnvelope() throws DataStoreException; } Modified: sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStore.java URL: http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStore.java?rev=1807624&r1=1807623&r2=1807624&view=diff ============================================================================== --- sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStore.java [UTF-8] (original) +++ sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStore.java [UTF-8] Thu Sep 7 15:47:24 2017 @@ -22,6 +22,7 @@ import java.util.IdentityHashMap; import java.util.NoSuchElementException; import org.opengis.metadata.Metadata; import org.opengis.metadata.identification.Identification; +import org.opengis.parameter.ParameterValueGroup; import org.apache.sis.util.Localized; import org.apache.sis.util.ArgumentChecks; import org.apache.sis.util.logging.WarningListener; @@ -32,6 +33,13 @@ import org.apache.sis.internal.util.Cita /** * Manages a series of features, coverages or sensor data. + * Different {@code DataStore} subclasses exist for different formats (netCDF, GeoTIFF, <i>etc.</i>). + * The supported format can be identifier by the {@linkplain #getProvider() provider}. + * + * <p>Each data store is itself a {@link Resource}. The data store subclasses should implement + * a more specialized {@code Resource} interface depending on the format characteristics. + * For example a {@code DataStore} for ShapeFiles will implement the {@link FeatureSet} interface, + * while a {@code DataStore} for netCDF files will implement the {@link Aggregate} interface.</p> * * <div class="section">Thread safety policy</div> * This {@code DataStore} base class is thread-safe. However subclasses do not need to be thread-safe. @@ -46,13 +54,15 @@ import org.apache.sis.internal.util.Cita * @since 0.3 * @module */ -public abstract class DataStore implements Localized, AutoCloseable { +public abstract class DataStore implements Resource, Localized, AutoCloseable { /** * The factory that created this {@code DataStore} instance, or {@code null} if unspecified. * This information can be useful for fetching information common to all {@code DataStore} * instances of the same class. * * @since 0.8 + * + * @see #getProvider() */ protected final DataStoreProvider provider; @@ -112,6 +122,21 @@ public abstract class DataStore implemen } /** + * Returns the factory that created this {@code DataStore} instance. + * The provider gives additional information on this {@code DataStore} such as a format description + * and a list of parameters that can be used for opening data stores of the same class. + * + * @return the factory that created this {@code DataStore} instance, or {@code null} if unspecified. + * + * @see #provider + * + * @since 0.8 + */ + public DataStoreProvider getProvider() { + return provider; + } + + /** * Returns a short name or label for this data store. * The returned name can be used in user interfaces or in error messages. * It may be a title in natural language, but should be relatively short. @@ -169,6 +194,29 @@ public abstract class DataStore implemen } /** + * Returns the parameters used to open this data store. + * The collection of legal parameters is implementation-dependent + * ({@linkplain org.apache.sis.parameter.DefaultParameterValue#getDescriptor() their description} + * is given by {@link DataStoreProvider#getOpenParameters()}), + * but should contain at least a parameter named {@value DataStoreProvider#LOCATION} + * with a {@link java.net.URI}, {@link java.nio.file.Path} or {@link javax.sql.DataSource} value. + * + * <p>In the event a data store must be closed and reopened later, those parameters can be stored in a file or + * database and used for {@linkplain DataStoreProvider#open(ParameterValueGroup) creating a new store} later.</p> + * + * <p>In some cases, for stores reading in-memory data or other inputs that can not fit with + * {@code ParameterDescriptorGroup} requirements (for example an {@link java.io.InputStream} + * connected to unknown or no {@link java.net.URL}), this method may return null.</p> + * + * @return parameters used for opening this {@code DataStore}, or {@code null} if not available. + * + * @see DataStoreProvider#getOpenParameters() + * + * @since 0.8 + */ + public abstract ParameterValueGroup getOpenParameters(); + + /** * Returns information about the data store as a whole. The returned metadata object can contain * information such as the spatiotemporal extent of all contained {@linkplain Resource resources}, * contact information about the creator or distributor, data quality, update frequency, usage constraints, @@ -179,22 +227,10 @@ public abstract class DataStore implemen * * @see Resource#getMetadata() */ + @Override public abstract Metadata getMetadata() throws DataStoreException; /** - * Returns the starting point from which all resources in this data store can be accessed. - * A resource can be for example a air temperature map or the set of all bridges in a city. - * If this data store contains only one resource, then that resource is returned directly. - * Otherwise if this data store contains more than one resource, then this method returns - * an {@link Aggregate} from which other resources can be accessed. - * - * @return the starting point of all resources in this data store, - * or {@code null} if this data store does not contain any resources. - * @throws DataStoreException if an error occurred while reading the data. - */ - public abstract Resource getRootResource() throws DataStoreException; - - /** * Searches for a resource identified by the given identifier. * The given identifier should match the following metadata element of a resource: * @@ -207,8 +243,10 @@ public abstract class DataStore implemen * is {@code "foo:bar"}, then this method may accept {@code "bar"} as a synonymous of {@code "foo:bar"} * provided that it does not introduce ambiguity. * - * <p>The default implementation verifies the {@linkplain #getRootResource() root resource}, then iterates over - * components of {@link Aggregate}s. If a match is found without ambiguity, the associated resource is returned. + * <p>The default implementation verifies if above criterion matches to this {@code DataStore} + * (which is itself a resource), then iterates recursively over {@link Aggregate} components + * if this data store is an aggregate. + * If a match is found without ambiguity, the associated resource is returned. * Otherwise an exception is thrown. Subclasses are encouraged to override this method with a more efficient * implementation.</p> * @@ -219,7 +257,7 @@ public abstract class DataStore implemen */ public Resource findResource(final String identifier) throws DataStoreException { ArgumentChecks.ensureNonEmpty("identifier", identifier); - final Resource resource = findResource(identifier, getRootResource(), new IdentityHashMap<>()); + final Resource resource = findResource(identifier, this, new IdentityHashMap<>()); if (resource != null) { return resource; } Modified: sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreProvider.java URL: http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreProvider.java?rev=1807624&r1=1807623&r2=1807624&view=diff ============================================================================== --- sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreProvider.java [UTF-8] (original) +++ sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreProvider.java [UTF-8] Thu Sep 7 15:47:24 2017 @@ -16,11 +16,16 @@ */ package org.apache.sis.storage; +import org.opengis.parameter.ParameterValueGroup; +import org.opengis.parameter.ParameterDescriptorGroup; +import org.opengis.parameter.ParameterNotFoundException; import org.opengis.metadata.distribution.Format; import org.apache.sis.internal.simple.SimpleFormat; +import org.apache.sis.internal.storage.Resources; import org.apache.sis.metadata.iso.citation.DefaultCitation; import org.apache.sis.metadata.iso.distribution.DefaultFormat; import org.apache.sis.measure.Range; +import org.apache.sis.util.ArgumentChecks; import org.apache.sis.util.Version; @@ -52,12 +57,29 @@ import org.apache.sis.util.Version; * However the {@code DataStore} instances created by the providers do not need to be thread-safe. * * @author Martin Desruisseaux (Geomatys) + * @author Johann Sorel (Geomatys) * @version 0.8 * @since 0.3 * @module */ public abstract class DataStoreProvider { /** + * Name of the parameter that specifies the data store location. + * A parameter named {@value} should be included in the group of parameters returned by {@link #getOpenParameters()}. + * The parameter value is often a {@link java.net.URI} or a {@link java.nio.file.Path}, but other types are allowed. + * + * <p>Implementors are encouraged to define a parameter with this name + * to ensure a common and consistent definition among providers. + * The parameter should be defined as mandatory and declared with a well-known Java class such as + * {@link java.net.URI}, {@link java.nio.file.Path}, JDBC {@linkplain javax.sql.DataSource}, <i>etc</i>. + * The type should have a compact textual representation, for serialization in XML or configuration files. + * Consequently {@link java.io.InputStream} and {@link java.nio.channels.Channel} should be avoided.</p> + * + * @see #getOpenParameters() + */ + public static final String LOCATION = "location"; + + /** * Creates a new provider. */ protected DataStoreProvider() { @@ -130,6 +152,37 @@ public abstract class DataStoreProvider } /** + * Returns a description of all parameters accepted by this provider for opening a data store. + * Those parameters provide an alternative to {@link StorageConnector} for opening a {@link DataStore} + * from a path or URL, together with additional information like character encoding. + * + * <p>Implementors are responsible for declaring all parameters and whether they are mandatory or optional. + * It is recommended to define at least a parameter named {@value #LOCATION}. + * That parameter will be recognized by the default {@code DataStoreProvider} methods and used whenever a + * {@link StorageConnector} is required.</p> + * + * <div class="note"><b>Alternative:</b> + * the main differences between the use of {@code StorageConnector} and parameters are: + * <ul class="verbose"> + * <li>{@code StorageConnector} is designed for use with file or stream of unknown format; + * the format is automatically detected. By contrast, the use of parameters require to + * determine the format first (i.e. select a {@code DataStoreProvider}).</li> + * <li>Parameters can be used to dynamically generate user configuration interfaces + * and provide fine grain control over the store general behavior such as caching, + * time-outs, encoding, <i>etc</i>.</li> + * <li>Parameters can more easily be serialized in XML or configuration files.</li> + * </ul></div> + * + * @return description of the parameters required for opening a {@link DataStore}. + * + * @see #open(ParameterValueGroup) + * @see DataStore#getOpenParameters() + * + * @since 0.8 + */ + public abstract ParameterDescriptorGroup getOpenParameters(); + + /** * Indicates if the given storage appears to be supported by the {@code DataStore}s created by this provider. * The most typical return values are: * @@ -187,6 +240,10 @@ public abstract class DataStoreProvider /** * Returns a data store implementation associated with this provider. + * This method is typically invoked when the format is not known in advance + * (the {@link #probeContent(StorageConnector)} method can be tested on many providers) + * or when the input is not a type accepted by {@link #open(ParameterValueGroup)} + * (for example an {@link java.io.InputStream}). * * <div class="section">Implementation note</div> * Implementors shall invoke {@link StorageConnector#closeAllExcept(Object)} after {@code DataStore} @@ -199,4 +256,50 @@ public abstract class DataStoreProvider * @see DataStores#open(Object) */ public abstract DataStore open(StorageConnector connector) throws DataStoreException; + + /** + * Returns a data store implementation associated with this provider for the given parameters. + * The {@code DataStoreProvider} instance needs to be known before parameters are initialized, + * since the parameters are implementation-dependent. Example: + * + * {@preformat java + * DataStoreProvider provider = ...; + * ParameterValueGroup pg = provider.getOpenParameters().createValue(); + * pg.parameter(DataStoreProvider.LOCATION, myURL); + * // Set any other parameters if desired. + * try (DataStore ds = provider.open(pg)) { + * // Use the data store. + * } + * } + * + * <div class="section">Implementation note</div> + * The default implementation gets the value of a parameter named {@value #LOCATION}. + * That value (typically a path or URL) is given to {@link StorageConnector} constructor, + * which is then passed to {@link #open(StorageConnector)}. + * + * @param parameters opening parameters as defined by {@link #getOpenParameters()}. + * @return a data store implementation associated with this provider for the given parameters. + * @throws DataStoreException if an error occurred while creating the data store instance. + * + * @see #getOpenParameters() + * + * @since 0.8 + */ + public DataStore open(final ParameterValueGroup parameters) throws DataStoreException { + ArgumentChecks.ensureNonNull("parameter", parameters); + ParameterNotFoundException cause = null; + Object location; + try { + location = parameters.parameter(LOCATION).getValue(); + } catch (ParameterNotFoundException e) { + location = null; + cause = e; + } + if (location == null) { + throw new IllegalOpenParameterException(Resources.format(Resources.Keys.UndefinedParameter_2, + getShortName(), LOCATION), cause); + } + final StorageConnector connector = new StorageConnector(location); + return open(connector); + } } Modified: sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/FeatureSet.java URL: http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/FeatureSet.java?rev=1807624&r1=1807623&r2=1807624&view=diff ============================================================================== --- sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/FeatureSet.java [UTF-8] (original) +++ sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/FeatureSet.java [UTF-8] Thu Sep 7 15:47:24 2017 @@ -16,7 +16,12 @@ */ package org.apache.sis.storage; +import java.util.Iterator; +import org.apache.sis.internal.storage.Resources; + // Branch-dependent imports +import java.util.function.Predicate; +import java.util.function.UnaryOperator; import java.util.stream.Stream; import org.opengis.feature.Feature; import org.opengis.feature.FeatureType; @@ -101,4 +106,63 @@ public interface FeatureSet extends Data * @throws DataStoreException if an error occurred while creating the stream. */ Stream<Feature> features(boolean parallel) throws DataStoreException; + + /** + * Inserts new features in this {@code FeatureSet}. + * Any feature already present in the {@link FeatureSet} will remain unmodified. + * + * <div class="note"><b>API note:</b> + * this method expects an {@link Iterator} rather then a {@link java.util.stream.Stream} for easing + * inter-operability with various API. Implementing a custom {@link Iterator} requires less effort + * than implementing a {@link Stream}. On the other side if the user has a {@link Stream}, + * obtaining an {@link Iterator} can be done by a call to {@link Stream#iterator()}.</div> + * + * <p>The default implementation throws {@link ReadOnlyStorageException}.</p> + * + * @param features features to insert in this {@code FeatureSet}. + * @throws ReadOnlyStorageException if this instance does not support write operations. + * @throws DataStoreException if another error occurred while storing new features. + */ + default void add(Iterator<? extends Feature> features) throws ReadOnlyStorageException, DataStoreException { + throw new ReadOnlyStorageException(this, Resources.Keys.StoreIsReadOnly); + } + + /** + * Removes all features from this {@code FeatureSet} which matches the given predicate. + * + * <p>The default implementation throws {@link ReadOnlyStorageException}.</p> + * + * @param filter a predicate which returns true for resources to be removed. + * @return {@code true} if any elements were removed. + * @throws ReadOnlyStorageException if this instance does not support write operations. + * @throws DataStoreException if another error occurred while removing features. + */ + default boolean removeIf(Predicate<? super Feature> filter) throws ReadOnlyStorageException, DataStoreException { + throw new ReadOnlyStorageException(this, Resources.Keys.StoreIsReadOnly); + } + + /** + * Updates all features from this {@code FeatureSet} which matches the given predicate. + * For each {@link Feature} instance matching the given {@link Predicate}, + * the <code>{@linkplain UnaryOperator#apply UnaryOperator.apply(Feature)}</code> method will be invoked. + * {@code UnaryOperator}s are free to modify the given {@code Feature} <i>in-place</i> or to return a + * different feature instance. Two behaviors are possible: + * <ul> + * <li>If the operator returns a non-null {@link Feature}, then the modified feature is stored + * in replacement of the previous feature (not necessarily at the same location).</li> + * <li>If the operator returns {@code null}, then the feature will be removed from the {@code FeatureSet}.</li> + * </ul> + * + * <p>The default implementation throws {@link ReadOnlyStorageException}.</p> + * + * @param filter a predicate which returns true for resources to be updated. + * @param updater operation called for each matching {@link Feature}. + * @throws ReadOnlyStorageException if this instance does not support write operations. + * @throws DataStoreException if another error occurred while replacing features. + */ + default void replaceIf(Predicate<? super Feature> filter, UnaryOperator<Feature> updater) + throws ReadOnlyStorageException, DataStoreException + { + throw new ReadOnlyStorageException(this, Resources.Keys.StoreIsReadOnly); + } } Modified: sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/ForwardOnlyStorageException.java URL: http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/ForwardOnlyStorageException.java?rev=1807624&r1=1807623&r2=1807624&view=diff ============================================================================== --- sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/ForwardOnlyStorageException.java [UTF-8] (original) +++ sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/ForwardOnlyStorageException.java [UTF-8] Thu Sep 7 15:47:24 2017 @@ -35,6 +35,8 @@ import org.apache.sis.internal.storage.i * @version 0.8 * @since 0.8 * @module + * + * @see ReadOnlyStorageException */ public class ForwardOnlyStorageException extends DataStoreException { /** @@ -58,7 +60,17 @@ public class ForwardOnlyStorageException } /** - * Creates a localized exception with a default message saying that the stream is read-once. + * Creates an exception with the specified details message and cause. + * + * @param message the detail message in the default locale. + * @param cause the cause for this exception. + */ + public ForwardOnlyStorageException(final String message, final Throwable cause) { + super(message, cause); + } + + /** + * Creates a localized exception with a default message saying that the stream is read-once or write-once. * * @param locale the locale of the message to be returned by {@link #getLocalizedMessage()}, or {@code null}. * @param filename name of the file or data store where the error occurred. Modified: sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/Resource.java URL: http://svn.apache.org/viewvc/sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/Resource.java?rev=1807624&r1=1807623&r2=1807624&view=diff ============================================================================== --- sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/Resource.java [UTF-8] (original) +++ sis/branches/JDK9/storage/sis-storage/src/main/java/org/apache/sis/storage/Resource.java [UTF-8] Thu Sep 7 15:47:24 2017 @@ -16,7 +16,6 @@ */ package org.apache.sis.storage; -import org.opengis.geometry.Envelope; import org.opengis.metadata.Metadata; @@ -28,8 +27,8 @@ import org.opengis.metadata.Metadata; * organization, in which case only metadata are provided. If the resource is digital, then {@code Resource}s * should be instances of sub-types like {@link Aggregate} or {@link FeatureSet}. * - * <p>The resources contained in a data store can be obtained by a call to {@link DataStore#getRootResource()}. - * If the data store contains resources for many feature types or coverages, then the root resource will be an + * <p>{@code DataStore}s are themselves closeable resources. + * If the data store contains resources for many feature types or coverages, then the data store will be an * instance of {@link Aggregate}. The {@linkplain Aggregate#components() components} of an aggregate can be * themselves other aggregates, thus forming a tree.</p> * @@ -37,14 +36,13 @@ import org.opengis.metadata.Metadata; * this type is closely related to the {@code DS_Resource} type defined by ISO 19115. * The Apache SIS type differs from the ISO type by being more closely related to data extraction, * as can been seen from the checked {@link DataStoreException} thrown by most methods. - * Convenience methods for frequently requested information – for example {@link #getEnvelope()} – were added. + * Convenience methods for frequently requested information – for example {@link DataSet#getEnvelope()} – were added. * The sub-types performing the actual data extraction – for example {@link FeatureSet} – are specific to Apache SIS. * </div> * * @author Johann Sorel (Geomatys) * @version 0.8 * - * @see DataStore#getRootResource() * @see Aggregate#components() * * @since 0.8 @@ -53,8 +51,6 @@ import org.opengis.metadata.Metadata; public interface Resource { /** * Returns information about this resource. - * If this resource is the {@linkplain DataStore#getRootResource() data store root resource}, - * then this method may return the same metadata instance than {@link DataStore#getMetadata()}. * If this resource is an {@link Aggregate}, then the metadata may enumerate characteristics * (spatio-temporal extents, feature types, range dimensions, <i>etc.</i>) of all * {@linkplain Aggregate#components() components} in the aggregate, or summarize them (for example by omitting @@ -107,29 +103,4 @@ public interface Resource { * @see DataStore#getMetadata() */ Metadata getMetadata() throws DataStoreException; - - /** - * Returns the spatio-temporal extent of this resource in its most natural coordinate reference system. - * The following relationship to {@linkplain #getMetadata()} should hold: - * - * <ul> - * <li>The envelope should be contained in the union of all geographic, vertical or temporal extents - * described by {@code metadata} / - * {@link org.apache.sis.metadata.iso.DefaultMetadata#getIdentificationInfo() identificationInfo} / - * {@link org.apache.sis.metadata.iso.identification.AbstractIdentification#getExtents() extent}.</li> - * <li>The coordinate reference system should be one of the instances returned by - * {@link org.apache.sis.metadata.iso.DefaultMetadata#getReferenceSystemInfo() referenceSystemInfo}.</li> - * </ul> - * - * The envelope should use the coordinate reference system (CRS) - * that most closely matches the geometry of the resource storage. It is often a - * {@linkplain org.apache.sis.referencing.crs.DefaultProjectedCRS projected CRS}, but other types like - * {@linkplain org.apache.sis.referencing.crs.DefaultEngineeringCRS engineering CRS} are also allowed. - * If this resource uses many different CRS with none of them covering all data, then the envelope should use a - * global system (typically a {@linkplain org.apache.sis.referencing.crs.DefaultGeocentricCRS geographic CRS}). - * - * @return the spatio-temporal resource extent. Should not be {@code null}. - * @throws DataStoreException if an error occurred while reading or computing the envelope. - */ - Envelope getEnvelope() throws DataStoreException; }
