Modified: sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources_fr.properties URL: http://svn.apache.org/viewvc/sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources_fr.properties?rev=1809404&r1=1809403&r2=1809404&view=diff ============================================================================== --- sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources_fr.properties [ISO-8859-1] (original) +++ sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/Resources_fr.properties [ISO-8859-1] Sat Sep 23 11:47:20 2017 @@ -25,6 +25,7 @@ # U+00A0 NO-BREAK SPACE before : # AmbiguousName_4 = Le nom \u00ab\u202f{3}\u202f\u00bb est ambigu\u00eb car il peut \u00eatre interpr\u00e9t\u00e9 aussi bien comme \u00ab\u202f{1}\u202f\u00bb ou \u00ab\u202f{2}\u202f\u00bb dans le contexte des donn\u00e9es de \u00ab\u202f{0}\u202f\u00bb. +CanNotReadDirectory_1 = Ne peut pas lire le r\u00e9pertoire \u00ab\u202f{0}\u202f\u00bb. CanNotReadFile_2 = Ne peut pas lire \u00ab\u202f{1}\u202f\u00bb comme un fichier au format {0}. CanNotReadFile_3 = Ne peut pas lire la ligne {2} de \u00ab\u202f{1}\u202f\u00bb comme une partie d\u2019un fichier au format {0}. CanNotReadFile_4 = Ne peut pas lire la ligne {2} (apr\u00e8s la colonne {3}) de \u00ab\u202f{1}\u202f\u00bb comme une partie d\u2019un fichier au format {0}. @@ -32,6 +33,11 @@ ClosedReader_1 = Ce l ClosedWriter_1 = Cet encodeur {0} est ferm\u00e9. ConcurrentRead_1 = Une ou plusieurs op\u00e9rations de lecture sont en cours sur les donn\u00e9es de \u00ab\u202f{0}\u202f\u00bb. ConcurrentWrite_1 = Une op\u00e9ration d\u2019\u00e9criture est en cours sur les donn\u00e9es de \u00ab\u202f{0}\u202f\u00bb. +DataStoreEncoding = Encodage des caract\u00e8res utilis\u00e9 par la source de donn\u00e9es. +DataStoreLocale = Conventions d\u2019\u00e9criture des dates et des nombres. +DataStoreLocation = Chemin (fichier ou URL) vers la source de donn\u00e9es. +DataStoreTimeZone = Fuseau horaire des dates dans les donn\u00e9es. +DirectoryContent_1 = Contenu du r\u00e9pertoire \u00ab\u202f{0}\u202f\u00bb. ExcessiveStringSize_3 = La cha\u00eene de caract\u00e8res dans le fichier \u00ab\u202f{0}\u202f\u00bb est trop longue. La cha\u00eene fait {2} caract\u00e8res alors que la limite est {1}. FeatureAlreadyPresent_2 = Une entit\u00e9 nomm\u00e9e \u00ab\u202f{1}\u202f\u00bb est d\u00e9j\u00e0 pr\u00e9sente dans les donn\u00e9es de \u00ab\u202f{0}\u202f\u00bb. FeatureNotFound_2 = L\u2019entit\u00e9 \u00ab\u202f{1}\u202f\u00bb n\u2019est pas \u00e9t\u00e9 trouv\u00e9e dans les donn\u00e9es de \u00ab\u202f{0}\u202f\u00bb. @@ -44,6 +50,7 @@ ProcessingExecutedOn_1 = Trai ResourceIdentifierCollision_2 = Plusieurs ressources utilisent l\u2019identifiant \u00ab\u202f{1}\u202f\u00bb dans les donn\u00e9es de \u00ab\u202f{0}\u202f\u00bb. ResourceNotFound_2 = Aucune ressource n\u2019a \u00e9t\u00e9 trouv\u00e9e pour l\u2019identifiant \u00ab\u202f{1}\u202f\u00bb dans les donn\u00e9es de \u00ab\u202f{0}\u202f\u00bb. ShallBeDeclaredBefore_2 = L\u2019\u00e9l\u00e9ment \u00ab\u202f{1}\u202f\u00bb doit \u00eatre d\u00e9clar\u00e9 avant \u00ab\u202f{0}\u202f\u00bb. +SharedDirectory_1 = Le r\u00e9pertoire \u00ab\u202f{0}\u202f\u00bb est utilis\u00e9 plus d\u2019une fois \u00e0 cause des liens symboliques. StoreIsReadOnly = Les op\u00e9rations d\u2019\u00e9criture ne sont pas support\u00e9es. StreamIsForwardOnly_1 = Ne peut pas reculer dans le flux de donn\u00e9es \u00ab\u202f{0}\u202f\u00bb. StreamIsNotReadable_1 = Les donn\u00e9es de \u00ab\u202f{0}\u202f\u00bb ne sont pas accessibles en lecture. @@ -52,3 +59,4 @@ StreamIsReadOnce_1 = Les StreamIsWriteOnce_1 = Ne peut pas revenir sur les donn\u00e9es d\u00e9j\u00e0 \u00e9crites dans \u00ab\u202f{0}\u202f\u00bb. UndefinedParameter_2 = Ne peut pas ouvrir une source de donn\u00e9es {0} sans le param\u00e8tre \u00ab\u202f{1}\u202f\u00bb. UnknownFormatFor_1 = Le format de \u00ab\u202f{0}\u202f\u00bb n\u2019est pas reconnu. +UsedOnlyIfNotEncoded = Utilis\u00e9 seulement si cette information n\u2019est pas encod\u00e9e avec les donn\u00e9es.
Modified: sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/StoreTypeDetector.java URL: http://svn.apache.org/viewvc/sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/StoreTypeDetector.java?rev=1809404&r1=1809403&r2=1809404&view=diff ============================================================================== --- sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/StoreTypeDetector.java [UTF-8] (original) +++ sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/StoreTypeDetector.java [UTF-8] Sat Sep 23 11:47:20 2017 @@ -33,7 +33,7 @@ import org.apache.sis.storage.DataStoreE * @since 0.4 * @module */ -public class StoreTypeDetector extends FileTypeDetector { +public final class StoreTypeDetector extends FileTypeDetector { /** * Constructor for {@link java.util.ServiceLoader}. */ Modified: sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/URIDataStore.java URL: http://svn.apache.org/viewvc/sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/URIDataStore.java?rev=1809404&r1=1809403&r2=1809404&view=diff ============================================================================== --- sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/URIDataStore.java [UTF-8] (original) +++ sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/URIDataStore.java [UTF-8] Sat Sep 23 11:47:20 2017 @@ -25,6 +25,7 @@ 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.internal.storage.io.IOUtilities; /** @@ -98,7 +99,8 @@ public abstract class URIDataStore exten /** * Description of the location parameter. */ - protected static final ParameterDescriptor<URI> LOCATION_PARAM = new ParameterBuilder() + public static final ParameterDescriptor<URI> LOCATION_PARAM = new ParameterBuilder() + .setDescription(Resources.formatInternational(Resources.Keys.DataStoreLocation)) .addName(LOCATION) .setRequired(true) .create(URI.class, null); @@ -120,7 +122,7 @@ public abstract class URIDataStore exten * This method creates the descriptor only when first needed. Subclasses can override the * {@link #build(ParameterBuilder)} method if they need to modify the descriptor to create. * - * @return description of the parameters required for opening a {@link DataStore}. + * @return description of the parameters required or accepted for opening a {@link DataStore}. */ @Override public final ParameterDescriptorGroup getOpenParameters() { @@ -160,4 +162,23 @@ public abstract class URIDataStore exten return new ParameterBuilder().addName(name).createGroup(LOCATION_PARAM); } } + + /** + * Adds the filename (without extension) as the citation title if there is no title, or as the identifier otherwise. + * This method should be invoked last, after {@code DataStore} implementation did its best effort for adding a title. + * The intend is actually to provide an identifier, but since the title is mandatory in ISO 19115 metadata, providing + * only an identifier without title would be invalid. + * + * @param builder where to add the title or identifier. + */ + protected final void addTitleOrIdentifier(final MetadataBuilder builder) { + if (location != null) { + /* + * The getDisplayName() contract does not allow us to use it as an identifier. However current implementation + * in super.getDisplayName() returns the filename provided that the input was a URI, URL, File or Path. Since + * all those types are convertibles to URI, we can use (location != null) as a criterion. + */ + builder.addTitleOrIdentifier(IOUtilities.filenameWithoutExtension(super.getDisplayName()), MetadataBuilder.Scope.ALL); + } + } } Modified: sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/Store.java URL: http://svn.apache.org/viewvc/sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/Store.java?rev=1809404&r1=1809403&r2=1809404&view=diff ============================================================================== --- sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/Store.java [UTF-8] (original) +++ sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/csv/Store.java [UTF-8] Sat Sep 23 11:47:20 2017 @@ -83,7 +83,7 @@ import org.apache.sis.feature.AbstractId * @since 0.7 * @module */ -public final class Store extends URIDataStore implements FeatureSet { +final class Store extends URIDataStore implements FeatureSet { /** * The character at the beginning of lines to ignore in the header. * Note that this is not part of OGC Moving Feature Specification. @@ -615,6 +615,7 @@ public final class Store extends URIData listeners.warning(null, e); } builder.addFeatureType(featureType, null); + addTitleOrIdentifier(builder); metadata = builder.build(true); } return metadata; Modified: sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/FolderStoreProvider.java URL: http://svn.apache.org/viewvc/sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/FolderStoreProvider.java?rev=1809404&r1=1809390&r2=1809404&view=diff ============================================================================== --- sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/FolderStoreProvider.java [UTF-8] (original) +++ sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/FolderStoreProvider.java [UTF-8] Sat Sep 23 11:47:20 2017 @@ -43,6 +43,9 @@ import org.apache.sis.internal.storage.R import org.apache.sis.internal.storage.URIDataStore; import org.apache.sis.setup.OptionKey; +// Branch-dependent imports +import org.apache.sis.referencing.AbstractIdentifiedObject; + /** * The provider of {@link Store} instances. This provider is intentionally <strong>not</strong> registered @@ -87,7 +90,7 @@ public final class FolderStoreProvider e LOCALE = builder.addName("locale" ).setDescription(Resources.formatInternational(Resources.Keys.DataStoreLocale )).setRemarks(remark).create(Locale.class, null); TIMEZONE = builder.addName("timezone").setDescription(Resources.formatInternational(Resources.Keys.DataStoreTimeZone)).setRemarks(remark).create(TimeZone.class, null); ENCODING = builder.addName("encoding").setDescription(Resources.formatInternational(Resources.Keys.DataStoreEncoding)).setRemarks(remark).create(Charset.class, null); - location = builder.addName( LOCATION ).setDescription(URIDataStore.Provider.LOCATION_PARAM.getDescription()) .setRequired(true) .create(Path.class, null); + location = builder.addName( LOCATION ).setDescription(((AbstractIdentifiedObject) URIDataStore.Provider.LOCATION_PARAM).getDescription()).setRequired(true) .create(Path.class, null); PARAMETERS = builder.addName( NAME ).createGroup(location, LOCALE, TIMEZONE, ENCODING); } Modified: sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/Store.java URL: http://svn.apache.org/viewvc/sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/Store.java?rev=1809404&r1=1809390&r2=1809404&view=diff ============================================================================== --- sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/Store.java [UTF-8] (original) +++ sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/folder/Store.java [UTF-8] Sat Sep 23 11:47:20 2017 @@ -188,7 +188,7 @@ final class Store extends DataStore impl if (metadata == null) { final MetadataBuilder mb = new MetadataBuilder(); final String name = getDisplayName(); - mb.addResourceScope(ScopeCode.COLLECTION, Resources.formatInternational(Resources.Keys.DirectoryContent_1, name)); + mb.addResourceScope(ScopeCode.valueOf("COLLECTION"), Resources.formatInternational(Resources.Keys.DirectoryContent_1, name)); mb.addLanguage(locale, MetadataBuilder.Scope.RESOURCE); mb.addEncoding(encoding, MetadataBuilder.Scope.RESOURCE); mb.addTitleOrIdentifier(name, MetadataBuilder.Scope.ALL); Modified: sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelData.java URL: http://svn.apache.org/viewvc/sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelData.java?rev=1809404&r1=1809403&r2=1809404&view=diff ============================================================================== --- sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelData.java [UTF-8] (original) +++ sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelData.java [UTF-8] Sat Sep 23 11:47:20 2017 @@ -49,7 +49,8 @@ public abstract class ChannelData implem private static final int BIT_OFFSET_SIZE = 3; /** - * A file identifier used only for formatting error message. + * A short identifier (typically a filename without path) used for formatting error message. + * This is often the value given by {@link org.apache.sis.storage.StorageConnector#getStorageName()}. */ public final String filename; @@ -114,7 +115,7 @@ public abstract class ChannelData implem * Creates a new instance for the given channel and using the given buffer. * The channel is not stored by this class - it shall be stored by the subclass. * - * @param filename a file identifier used only for formatting error message. + * @param filename a short identifier (typically a filename without path) used for formatting error message. * @param channel the channel from where data are read or where to wrote. * @param buffer the buffer where to store the data. * @throws IOException if an error occurred while reading the channel. Modified: sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelDataInput.java URL: http://svn.apache.org/viewvc/sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelDataInput.java?rev=1809404&r1=1809403&r2=1809404&view=diff ============================================================================== --- sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelDataInput.java [UTF-8] (original) +++ sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelDataInput.java [UTF-8] Sat Sep 23 11:47:20 2017 @@ -26,6 +26,7 @@ import java.nio.IntBuffer; import java.nio.LongBuffer; import java.nio.FloatBuffer; import java.nio.DoubleBuffer; +import java.nio.charset.Charset; import java.nio.channels.ReadableByteChannel; import java.nio.channels.SeekableByteChannel; import org.apache.sis.internal.storage.Resources; @@ -79,7 +80,7 @@ public class ChannelDataInput extends Ch * If the buffer already contains some data, then the {@code filled} argument shall be {@code true}. * Otherwise (e.g. if it is a newly created buffer), then {@code filled} shall be {@code false}. * - * @param filename a file identifier used only for formatting error message. + * @param filename a short identifier (typically a filename without path) used for formatting error message. * @param channel the channel from where data are read. * @param buffer the buffer where to copy the data. * @param filled {@code true} if the buffer already contains data, or {@code false} if it needs @@ -825,7 +826,7 @@ public class ChannelDataInput extends Ch * @return the string decoded from the {@code length} next bytes. * @throws IOException if an error occurred while reading the bytes, or if the given encoding is invalid. */ - public final String readString(final int length, final String encoding) throws IOException { + public final String readString(final int length, final Charset encoding) throws IOException { if (buffer.hasArray() && length <= buffer.capacity()) { ensureBufferContains(length); final int position = buffer.position(); // Must be after 'ensureBufferContains(int)'. Modified: sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelFactory.java URL: http://svn.apache.org/viewvc/sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelFactory.java?rev=1809404&r1=1809403&r2=1809404&view=diff ============================================================================== --- sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelFactory.java [UTF-8] (original) +++ sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelFactory.java [UTF-8] Sat Sep 23 11:47:20 2017 @@ -21,6 +21,8 @@ import java.util.EnumSet; import java.util.HashSet; import java.util.Collections; import java.util.Arrays; +import java.util.logging.Level; +import java.util.logging.LogRecord; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; @@ -43,8 +45,10 @@ import java.nio.channels.ReadableByteCha import java.nio.channels.WritableByteChannel; import org.apache.sis.util.logging.Logging; import org.apache.sis.util.resources.Errors; +import org.apache.sis.util.logging.WarningListeners; import org.apache.sis.internal.system.Modules; import org.apache.sis.internal.storage.Resources; +import org.apache.sis.storage.DataStore; import org.apache.sis.storage.DataStoreException; import org.apache.sis.storage.ForwardOnlyStorageException; @@ -53,13 +57,14 @@ import org.apache.sis.storage.ForwardOnl * Opens a readable channel for a given input object (URL, input stream, <i>etc</i>). * The {@link #prepare prepare(…)} method analyzes the given input {@link Object} and tries to return a factory instance * capable to open at least one {@link ReadableByteChannel} for that input. For some kinds of input like {@link Path} or - * {@link URL}, the {@link #reader(String)} method can be invoked an arbitrary amount of times for creating as many channels - * as needed. But for other kinds of input like {@link InputStream}, only one channel can be returned. In such case, - * only the first {@link #reader(String)} method invocation will succeed and all subsequent ones will throw an exception. + * {@link URL}, the {@link #reader reader(…)} method can be invoked an arbitrary amount of times for creating as many + * channels as needed. But for other kinds of input like {@link InputStream}, only one channel can be returned. + * In such case, only the first {@link #reader reader(…)} method invocation will succeed and all subsequent ones + * will throw an exception. * * <div class="section">Multi-threading</div> * This class is not thread-safe, except for the static {@link #prepare prepare(…)} method. - * Callers are responsible for synchronizing their call to any member methods ({@link #reader(String)}, <i>etc</i>). + * Callers are responsible for synchronizing their call to any member methods ({@link #reader reader(…)}, <i>etc</i>). * * @author Martin Desruisseaux (Geomatys) * @author Johann Sorel (Geomatys) @@ -211,7 +216,9 @@ public abstract class ChannelFactory { * Unlikely to happen. But if it happens anyway, try to open the channel in a * way less surprising for the user (closer to the object he has specified). */ - return new Fallback(file, e); + if (file.isFile()) { + return new Fallback(file, e); + } } } } @@ -223,24 +230,26 @@ public abstract class ChannelFactory { if (storage instanceof URL) { final URL file = (URL) storage; return new ChannelFactory() { - @Override public ReadableByteChannel reader(String filename) throws IOException { + @Override public ReadableByteChannel reader(String filename, WarningListeners<DataStore> listeners) throws IOException { return Channels.newChannel(file.openStream()); } - @Override public WritableByteChannel writer(String filename) throws IOException { + @Override public WritableByteChannel writer(String filename, WarningListeners<DataStore> listeners) throws IOException { return Channels.newChannel(file.openConnection().getOutputStream()); } }; } if (storage instanceof Path) { final Path path = (Path) storage; - return new ChannelFactory() { - @Override public ReadableByteChannel reader(String filename) throws IOException { - return Files.newByteChannel(path, optionSet); - } - @Override public WritableByteChannel writer(String filename) throws IOException { - return Files.newByteChannel(path, optionSet); - } - }; + if (Files.isRegularFile(path)) { + return new ChannelFactory() { + @Override public ReadableByteChannel reader(String filename, WarningListeners<DataStore> listeners) throws IOException { + return Files.newByteChannel(path, optionSet); + } + @Override public WritableByteChannel writer(String filename, WarningListeners<DataStore> listeners) throws IOException { + return Files.newByteChannel(path, optionSet); + } + }; + } } return null; } @@ -259,9 +268,9 @@ public abstract class ChannelFactory { /** * 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. + * if this factory is capable to create only one channel and {@link #reader reader(…)} has already been invoked. * - * @return whether {@link #reader(String)} can be invoked. + * @return whether {@link #reader reader(…)} or {@link #writer writer(…)} can be invoked. */ public boolean canOpen() { return true; @@ -272,12 +281,15 @@ public abstract class ChannelFactory { * it is caller's responsibility to wrap the stream in a {@link java.io.BufferedInputStream} if desired. * * @param filename data store name to report in case of failure. + * @param listeners set of registered {@code WarningListener}s for the data store, or {@code null} if none. * @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 DataStoreException, IOException { - return Channels.newInputStream(reader(filename)); + public InputStream inputStream(String filename, WarningListeners<DataStore> listeners) + throws DataStoreException, IOException + { + return Channels.newInputStream(reader(filename, listeners)); } /** @@ -285,12 +297,15 @@ public abstract class ChannelFactory { * it is caller's responsibility to wrap the stream in a {@link java.io.BufferedOutputStream} if desired. * * @param filename data store name to report in case of failure. + * @param listeners set of registered {@code WarningListener}s for the data store, or {@code null} if none. * @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 DataStoreException, IOException { - return Channels.newOutputStream(writer(filename)); + public OutputStream outputStream(String filename, WarningListeners<DataStore> listeners) + throws DataStoreException, IOException + { + return Channels.newOutputStream(writer(filename, listeners)); } /** @@ -299,11 +314,13 @@ public abstract class ChannelFactory { * this method throws an exception. * * @param filename data store name to report in case of failure. + * @param listeners set of registered {@code WarningListener}s for the data store, or {@code null} if none. * @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 DataStoreException, IOException; + public abstract ReadableByteChannel reader(String filename, WarningListeners<DataStore> listeners) + throws DataStoreException, IOException; /** * Returns a byte channel from the output given to the {@link #prepare prepare(…)} method. @@ -311,11 +328,13 @@ public abstract class ChannelFactory { * this method throws an exception. * * @param filename data store name to report in case of failure. + * @param listeners set of registered {@code WarningListener}s for the data store, or {@code null} if none. * @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 DataStoreException, IOException; + public abstract WritableByteChannel writer(String filename, WarningListeners<DataStore> listeners) + throws DataStoreException, IOException; /** * A factory that returns an existing channel <cite>as-is</cite>. @@ -343,7 +362,7 @@ public abstract class ChannelFactory { } /** - * Returns whether {@link #reader(String)} or {@link #writer(String)} can be invoked. + * Returns whether {@link #reader reader(…)} or {@link #writer writer(…)} can be invoked. */ @Override public boolean canOpen() { @@ -355,7 +374,9 @@ public abstract class ChannelFactory { * throws an exception on all subsequent invocations. */ @Override - public ReadableByteChannel reader(final String filename) throws DataStoreException, IOException { + public ReadableByteChannel reader(final String filename, final WarningListeners<DataStore> listeners) + throws DataStoreException, IOException + { final Channel in = channel; if (in instanceof ReadableByteChannel) { channel = null; @@ -375,7 +396,9 @@ public abstract class ChannelFactory { * throws an exception on all subsequent invocations. */ @Override - public WritableByteChannel writer(final String filename) throws DataStoreException, IOException { + public WritableByteChannel writer(final String filename, final WarningListeners<DataStore> listeners) + throws DataStoreException, IOException + { final Channel out = channel; if (out instanceof WritableByteChannel) { channel = null; @@ -425,7 +448,9 @@ public abstract class ChannelFactory { * {@link File} to a {@link Path}. On all subsequent invocations, the file is opened silently.</p> */ @Override - public FileInputStream inputStream(String filename) throws IOException { + public FileInputStream inputStream(final String filename, final WarningListeners<DataStore> listeners) + throws IOException + { final FileInputStream in; try { in = new FileInputStream(file); @@ -441,7 +466,7 @@ public abstract class ChannelFactory { * But the exception was nevertheless unexpected, so log its stack trace in order * to allow the developer to check if there is something wrong. */ - warning(); + warning("inputStream", listeners); return in; } @@ -453,7 +478,9 @@ public abstract class ChannelFactory { * {@link File} to a {@link Path}. On all subsequent invocations, the file is opened silently.</p> */ @Override - public FileOutputStream outputStream(String filename) throws IOException { + public FileOutputStream outputStream(final String filename, final WarningListeners<DataStore> listeners) + throws IOException + { final FileOutputStream out; try { out = new FileOutputStream(file); @@ -464,7 +491,7 @@ public abstract class ChannelFactory { } throw ioe; } - warning(); + warning("outputStream", listeners); return out; } @@ -473,10 +500,19 @@ public abstract class ChannelFactory { * Since the exception was nevertheless unexpected, log its stack trace in order to allow the developer * to check if there is something wrong. */ - private void warning() { + private void warning(final String method, final WarningListeners<DataStore> listeners) { if (cause != null) { - Logging.unexpectedException(Logging.getLogger(Modules.STORAGE), ChannelFactory.class, "prepare", cause); + final LogRecord record = new LogRecord(Level.WARNING, cause.toString()); + record.setLoggerName(Modules.STORAGE); + record.setSourceMethodName(method); + record.setSourceClassName(ChannelFactory.class.getName()); + record.setThrown(cause); cause = null; + if (listeners != null) { + listeners.warning(record); + } else { + Logging.getLogger(Modules.STORAGE).log(record); + } } } @@ -484,16 +520,16 @@ public abstract class ChannelFactory { * Opens a new channel for the file given at construction time. */ @Override - public ReadableByteChannel reader(String filename) throws IOException { - return inputStream(filename).getChannel(); + public ReadableByteChannel reader(String filename, WarningListeners<DataStore> listeners) throws IOException { + return inputStream(filename, listeners).getChannel(); } /** * Opens a new channel for the file given at construction time. */ @Override - public WritableByteChannel writer(String filename) throws IOException { - return outputStream(filename).getChannel(); + public WritableByteChannel writer(String filename, WarningListeners<DataStore> listeners) throws IOException { + return outputStream(filename, listeners).getChannel(); } } Modified: sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/IOUtilities.java URL: http://svn.apache.org/viewvc/sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/IOUtilities.java?rev=1809404&r1=1809403&r2=1809404&view=diff ============================================================================== --- sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/IOUtilities.java [UTF-8] (original) +++ sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/IOUtilities.java [UTF-8] Sat Sep 23 11:47:20 2017 @@ -164,6 +164,29 @@ public final class IOUtilities extends S } /** + * Returns the given path without the directories and without the extension. + * For example if the given path is {@code "/Users/name/Map.png"}, then this + * method returns {@code "Map"}. + * + * @param path the path from which to get the filename without extension, or {@code null}. + * @return the filename without extension, or {@code null} if none. + */ + public static String filenameWithoutExtension(String path) { + if (path != null) { + int s = path.lastIndexOf(File.separatorChar); + if (s < 0 && File.separatorChar != '/') { + s = path.lastIndexOf('/'); + } + int e = path.lastIndexOf('.'); + if (e <= ++s) { + e = path.length(); + } + path = path.substring(s, e); + } + return path; + } + + /** * Encodes the characters that are not legal for the {@link URI#URI(String)} constructor. * Note that in addition to unreserved characters ("{@code _-!.~'()*}"), the reserved * characters ("{@code ?/[]@}") and the punctuation characters ("{@code ,;:$&+=}") Modified: sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/Store.java URL: http://svn.apache.org/viewvc/sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/Store.java?rev=1809404&r1=1809403&r2=1809404&view=diff ============================================================================== --- sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/Store.java [UTF-8] (original) +++ sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/wkt/Store.java [UTF-8] Sat Sep 23 11:47:20 2017 @@ -189,6 +189,7 @@ final class Store extends URIDataStore { builder.addReferenceSystem((ReferenceSystem) object); } } + addTitleOrIdentifier(builder); metadata = builder.build(true); } return metadata; Modified: sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/storage/Aggregate.java URL: http://svn.apache.org/viewvc/sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/storage/Aggregate.java?rev=1809404&r1=1809403&r2=1809404&view=diff ============================================================================== --- sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/storage/Aggregate.java [UTF-8] (original) +++ sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/storage/Aggregate.java [UTF-8] Sat Sep 23 11:47:20 2017 @@ -63,7 +63,7 @@ import org.opengis.referencing.crs.Coord */ public interface Aggregate extends Resource { /** - * Returns the children resources of this aggregate. The returned collection contains at least all + * Returns the children resources of this aggregate. The returned collection contains * the resources listed by their name in the following {@linkplain #getMetadata() metadata} elements. * The returned collection may contain more resources if the metadata are incomplete, * and the resources do not need to be in the same order: @@ -80,6 +80,11 @@ public interface Aggregate extends Resou * {@link org.apache.sis.metadata.iso.identification.AbstractIdentification#getCitation() citation} / * {@link org.apache.sis.metadata.iso.citation.DefaultCitation#getTitle() title}</blockquote> * + * <div class="section">Lazy resource instantiation</div> + * If the collection instantiates components only when first needed, and if a checked exception occurs + * during invocation of a {@link Collection} or {@link java.util.Iterator} method, then the collection + * or the iterator should wrap the exception in a {@link org.apache.sis.util.collection.BackingStoreException}. + * * @return all children resources that are components of this aggregate. Never {@code null}. * @throws DataStoreException if an error occurred while fetching the components. */ Modified: sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStore.java URL: http://svn.apache.org/viewvc/sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStore.java?rev=1809404&r1=1809403&r2=1809404&view=diff ============================================================================== --- sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStore.java [UTF-8] (original) +++ sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStore.java [UTF-8] Sat Sep 23 11:47:20 2017 @@ -122,6 +122,32 @@ public abstract class DataStore implemen } /** + * Creates a new instance as a child of another data store instance. + * The new instance inherits the parent {@linkplain #getProvider() provider}. + * The parent and the child share the same listeners: adding or removing a listener to a parent + * adds or removes the same listeners to all children, and conversely. + * + * @param parent the parent data store, or {@code null} if none. + * @param connector information about the storage (URL, stream, reader instance, <i>etc</i>). + * @throws DataStoreException if an error occurred while creating the data store for the given storage. + * + * @since 0.8 + */ + protected DataStore(final DataStore parent, final StorageConnector connector) throws DataStoreException { + ArgumentChecks.ensureNonNull("connector", connector); + if (parent != null) { + provider = parent.provider; + locale = parent.locale; + listeners = parent.listeners; + } else { + provider = null; + locale = Locale.getDefault(Locale.Category.DISPLAY); + listeners = new WarningListeners<>(this); + } + name = connector.getStorageName(); + } + + /** * 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. Modified: sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreProvider.java URL: http://svn.apache.org/viewvc/sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreProvider.java?rev=1809404&r1=1809403&r2=1809404&view=diff ============================================================================== --- sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreProvider.java [UTF-8] (original) +++ sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreProvider.java [UTF-8] Sat Sep 23 11:47:20 2017 @@ -173,7 +173,7 @@ public abstract class DataStoreProvider * <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}. + * @return description of the parameters required or accepted for opening a {@link DataStore}. * * @see #open(ParameterValueGroup) * @see DataStore#getOpenParameters() @@ -299,7 +299,6 @@ public abstract class DataStoreProvider throw new IllegalOpenParameterException(Resources.format(Resources.Keys.UndefinedParameter_2, getShortName(), LOCATION), cause); } - final StorageConnector connector = new StorageConnector(location); - return open(connector); + return open(new StorageConnector(location)); } } Modified: sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreRegistry.java URL: http://svn.apache.org/viewvc/sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreRegistry.java?rev=1809404&r1=1809403&r2=1809404&view=diff ============================================================================== --- sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreRegistry.java [UTF-8] (original) +++ sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/storage/DataStoreRegistry.java [UTF-8] Sat Sep 23 11:47:20 2017 @@ -245,7 +245,9 @@ search: while (!deferred.isEmpty } } if (open && selected == null) { - throw new UnsupportedStorageException(null, Resources.Keys.UnknownFormatFor_1, connector.getStorageName()); + @SuppressWarnings("null") + final String name = connector.getStorageName(); + throw new UnsupportedStorageException(null, Resources.Keys.UnknownFormatFor_1, name); } return selected; } Modified: sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/storage/StorageConnector.java URL: http://svn.apache.org/viewvc/sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/storage/StorageConnector.java?rev=1809404&r1=1809403&r2=1809404&view=diff ============================================================================== --- sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/storage/StorageConnector.java [UTF-8] (original) +++ sis/trunk/storage/sis-storage/src/main/java/org/apache/sis/storage/StorageConnector.java [UTF-8] Sat Sep 23 11:47:20 2017 @@ -597,8 +597,10 @@ public class StorageConnector implements } /** - * Returns a short name of the input/output object. The default implementation performs - * the following choices based on the type of the {@linkplain #getStorage() storage} object: + * Returns a short name of the input/output object. For example if the storage is a file, + * this method returns the filename without the path (but including the file extension). + * The default implementation performs the following choices based on the type of the + * {@linkplain #getStorage() storage} object: * * <ul> * <li>For {@link java.nio.file.Path}, {@link java.io.File}, {@link java.net.URI} or {@link java.net.URL} @@ -921,7 +923,7 @@ public class StorageConnector implements * (potentially an InputStream). We need to remember this chain in 'Coupled' objects. */ final String name = getStorageName(); - final ReadableByteChannel channel = factory.reader(name); + final ReadableByteChannel channel = factory.reader(name, null); addView(ReadableByteChannel.class, channel, null, factory.isCoupled() ? CASCADE_ON_RESET : 0); ByteBuffer buffer = getOption(OptionKey.BYTE_BUFFER); // User-supplied buffer. if (buffer == null) { @@ -1374,8 +1376,9 @@ public class StorageConnector implements op.setValue(TableColumn.NAME, "options"); op.setValue(TableColumn.VALUE, options); } - if (views != null) { - views.get(null).append(root.newChild(), views); + final Coupled c = getView(null); + if (c != null) { + c.append(root.newChild(), views); } return table.toString(); } Modified: sis/trunk/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/io/ChannelDataInputTest.java URL: http://svn.apache.org/viewvc/sis/trunk/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/io/ChannelDataInputTest.java?rev=1809404&r1=1809403&r2=1809404&view=diff ============================================================================== --- sis/trunk/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/io/ChannelDataInputTest.java [UTF-8] (original) +++ sis/trunk/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/io/ChannelDataInputTest.java [UTF-8] Sat Sep 23 11:47:20 2017 @@ -21,6 +21,7 @@ import java.io.DataInputStream; import java.io.ByteArrayInputStream; import java.io.IOException; import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; import org.junit.Test; import static org.junit.Assert.*; @@ -134,7 +135,7 @@ public final strictfp class ChannelDataI } /** - * Tests the {@link ChannelDataInput#readString(int, String)} method. + * Tests the {@link ChannelDataInput#readString(int, Charset)} method. * * @throws IOException should never happen since we read and write in memory only. */ @@ -146,7 +147,7 @@ public final strictfp class ChannelDataI final ChannelDataInput input = new ChannelDataInput("testReadString", new DripByteChannel(array, random, 1, 32), ByteBuffer.allocate(array.length + 4), false); - assertEquals(expected, input.readString(array.length, "UTF-8")); + assertEquals(expected, input.readString(array.length, StandardCharsets.UTF_8)); assertFalse(input.buffer.hasRemaining()); } Modified: sis/trunk/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/io/IOUtilitiesTest.java URL: http://svn.apache.org/viewvc/sis/trunk/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/io/IOUtilitiesTest.java?rev=1809404&r1=1809403&r2=1809404&view=diff ============================================================================== --- sis/trunk/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/io/IOUtilitiesTest.java [UTF-8] (original) +++ sis/trunk/storage/sis-storage/src/test/java/org/apache/sis/internal/storage/io/IOUtilitiesTest.java [UTF-8] Sat Sep 23 11:47:20 2017 @@ -35,7 +35,7 @@ import static org.junit.Assert.*; * * @author Martin Desruisseaux (Geomatys) * @author Johann Sorel (Geomatys) - * @version 0.4 + * @version 0.8 * @since 0.3 * @module */ @@ -112,6 +112,17 @@ public final strictfp class IOUtilitiesT } /** + * Tests {@link IOUtilities#filenameWithoutExtension(String)}. + */ + @Test + public void testFilenameWithoutExtension() { + assertEquals("Map", IOUtilities.filenameWithoutExtension("/Users/name/Map.png")); + assertEquals("Map", IOUtilities.filenameWithoutExtension("/Users/name/Map")); + assertEquals("Map", IOUtilities.filenameWithoutExtension("Map.png")); + assertEquals(".hidden", IOUtilities.filenameWithoutExtension("/Users/name/.hidden")); + } + + /** * Tests {@link IOUtilities#encodeURI(String)}. */ @Test Modified: sis/trunk/storage/sis-storage/src/test/java/org/apache/sis/test/suite/StorageTestSuite.java URL: http://svn.apache.org/viewvc/sis/trunk/storage/sis-storage/src/test/java/org/apache/sis/test/suite/StorageTestSuite.java?rev=1809404&r1=1809403&r2=1809404&view=diff ============================================================================== --- sis/trunk/storage/sis-storage/src/test/java/org/apache/sis/test/suite/StorageTestSuite.java [UTF-8] (original) +++ sis/trunk/storage/sis-storage/src/test/java/org/apache/sis/test/suite/StorageTestSuite.java [UTF-8] Sat Sep 23 11:47:20 2017 @@ -48,6 +48,7 @@ import org.junit.BeforeClass; org.apache.sis.internal.storage.wkt.StoreTest.class, org.apache.sis.internal.storage.csv.StoreProviderTest.class, org.apache.sis.internal.storage.csv.StoreTest.class, + org.apache.sis.internal.storage.folder.StoreTest.class, org.apache.sis.storage.DataStoresTest.class }) public final strictfp class StorageTestSuite extends TestSuite { Modified: sis/trunk/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/gpx/Store.java URL: http://svn.apache.org/viewvc/sis/trunk/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/gpx/Store.java?rev=1809404&r1=1809403&r2=1809404&view=diff ============================================================================== --- sis/trunk/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/gpx/Store.java [UTF-8] (original) +++ sis/trunk/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/gpx/Store.java [UTF-8] Sat Sep 23 11:47:20 2017 @@ -29,7 +29,7 @@ import org.apache.sis.storage.DataStoreC import org.apache.sis.storage.ConcurrentReadException; import org.apache.sis.storage.IllegalNameException; import org.apache.sis.internal.system.DefaultFactories; -import org.apache.sis.internal.storage.AbstractDataSet; +import org.apache.sis.internal.storage.AbstractResource; import org.apache.sis.internal.storage.xml.stream.StaxDataStore; import org.apache.sis.util.collection.BackingStoreException; import org.apache.sis.util.ArgumentChecks; @@ -177,7 +177,7 @@ public final class Store extends StaxDat */ @Override public Envelope getEnvelope() throws DataStoreException { - return AbstractDataSet.envelope(getMetadata()); + return AbstractResource.envelope(getMetadata()); } /** Modified: sis/trunk/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/xml/stream/StaxDataStore.java URL: http://svn.apache.org/viewvc/sis/trunk/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/xml/stream/StaxDataStore.java?rev=1809404&r1=1809403&r2=1809404&view=diff ============================================================================== --- sis/trunk/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/xml/stream/StaxDataStore.java [UTF-8] (original) +++ sis/trunk/storage/sis-xmlstore/src/main/java/org/apache/sis/internal/storage/xml/stream/StaxDataStore.java [UTF-8] Sat Sep 23 11:47:20 2017 @@ -125,9 +125,9 @@ public abstract class StaxDataStore exte * <p>We keep this reference as long as possible in order to use {@link #mark()} and {@link #reset()} * instead than creating new streams for re-reading the data. If we can not reset the stream but can * create a new one, then this field will become a reference to the new stream. This change should be - * done only in last resort, when there is no way to reuse the existing stream. This is because the - * streams created by {@link ChannelFactory#inputStream(String)} are not of the same kind than the - * streams created by {@link StorageConnector}.</p> + * done only in last resort, when there is no way to reuse the existing stream. This is because the + * streams created by {@link ChannelFactory#inputStream(String, WarningListeners)} are not of the same + * kind than the streams created by {@link StorageConnector}.</p> * * @see #close() */ @@ -473,7 +473,7 @@ public abstract class StaxDataStore exte if (channelFactory == null) { throw new ForwardOnlyStorageException(getLocale(), name, StandardOpenOption.READ); } - inputOrFile = input = channelFactory.inputStream(name); + inputOrFile = input = channelFactory.inputStream(name, listeners); type = InputType.STREAM; if (stream == null) { stream = input;