This is an automated email from the ASF dual-hosted git repository. desruisseaux pushed a commit to branch geoapi-4.0 in repository https://gitbox.apache.org/repos/asf/sis.git
commit ef25f4058f3c6d872847aeab3d0d0f5f657d96e1 Author: Martin Desruisseaux <martin.desruisse...@geomatys.com> AuthorDate: Fri Oct 29 15:58:26 2021 +0200 Add the possibility to listen to read or write operations. --- .../sis/internal/geotiff/SchemaModifier.java | 4 +- .../sis/internal/sql/feature/SchemaModifier.java | 4 +- .../sis/internal/storage/io/ChannelFactory.java | 39 ++++++++++++--- .../sis/internal/storage/io/InternalOptionKey.java | 56 ++++++++++++++++++++++ .../sis/internal/storage/io/package-info.java | 2 +- .../org/apache/sis/storage/StorageConnector.java | 9 ++-- 6 files changed, 100 insertions(+), 14 deletions(-) diff --git a/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/SchemaModifier.java b/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/SchemaModifier.java index 781defc..11e1650 100644 --- a/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/SchemaModifier.java +++ b/storage/sis-geotiff/src/main/java/org/apache/sis/internal/geotiff/SchemaModifier.java @@ -21,6 +21,7 @@ import org.apache.sis.coverage.SampleDimension; import org.apache.sis.measure.NumberRange; import org.apache.sis.metadata.iso.DefaultMetadata; import org.apache.sis.setup.OptionKey; +import org.apache.sis.internal.storage.io.InternalOptionKey; import org.apache.sis.storage.DataStoreException; import org.opengis.metadata.Metadata; import org.opengis.util.GenericName; @@ -123,8 +124,7 @@ public interface SchemaModifier { * @todo if we move this key in public API in the future, then it would be a * value in existing {@link org.apache.sis.storage.DataOptionKey} class. */ - OptionKey<SchemaModifier> OPTION = new OptionKey<SchemaModifier>("SCHEMA_MODIFIER", SchemaModifier.class) { - }; + OptionKey<SchemaModifier> OPTION = new InternalOptionKey<SchemaModifier>("SCHEMA_MODIFIER", SchemaModifier.class); /** * The default instance which performs no modification. diff --git a/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/SchemaModifier.java b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/SchemaModifier.java index 8a960ba..14c6917 100644 --- a/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/SchemaModifier.java +++ b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/SchemaModifier.java @@ -18,6 +18,7 @@ package org.apache.sis.internal.sql.feature; import org.apache.sis.feature.builder.FeatureTypeBuilder; import org.apache.sis.storage.DataStoreException; +import org.apache.sis.internal.storage.io.InternalOptionKey; import org.apache.sis.setup.OptionKey; // Branch-dependent imports @@ -73,6 +74,5 @@ public interface SchemaModifier { * @todo if we move this key in public API in the future, then it would be a * value in existing {@link org.apache.sis.storage.DataOptionKey} class. */ - OptionKey<SchemaModifier> OPTION = new OptionKey<SchemaModifier>("SCHEMA_MODIFIER", SchemaModifier.class) { - }; + OptionKey<SchemaModifier> OPTION = new InternalOptionKey<SchemaModifier>("SCHEMA_MODIFIER", SchemaModifier.class); } diff --git a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelFactory.java b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelFactory.java index 3bab9a5..9755cfb 100644 --- a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelFactory.java +++ b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/ChannelFactory.java @@ -23,6 +23,7 @@ import java.util.Collections; import java.util.Arrays; import java.util.logging.Level; import java.util.logging.LogRecord; +import java.util.function.UnaryOperator; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; @@ -56,7 +57,7 @@ import org.apache.sis.storage.event.StoreListeners; /** * 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 + * capable to open at least a {@link ReadableByteChannel} for that input. For some kinds of input like {@link Path} or * {@link URL}, the {@link #readable readable(…)} 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 #readable readable(…)} method invocation will succeed and all subsequent ones @@ -68,13 +69,13 @@ import org.apache.sis.storage.event.StoreListeners; * * @author Martin Desruisseaux (Geomatys) * @author Johann Sorel (Geomatys) - * @version 1.0 + * @version 1.2 * @since 0.8 * @module */ public abstract class ChannelFactory { /** - * Options to be rejected by {@link #prepare(Object, String, boolean, OpenOption...)} for safety reasons. + * Options to be rejected by {@link #prepare(Object, boolean, String, OpenOption[])} for safety reasons. */ private static final Set<StandardOpenOption> ILLEGAL_OPTIONS = EnumSet.of( StandardOpenOption.APPEND, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.DELETE_ON_CLOSE); @@ -82,7 +83,7 @@ public abstract class ChannelFactory { /** * For subclass constructors. */ - ChannelFactory() { + protected ChannelFactory() { } /** @@ -117,16 +118,42 @@ public abstract class ChannelFactory { * honor the options or not.</p> * * @param storage the stream or the file to open, or {@code null}. + * @param allowWriteOnly whether to allow wrapping {@link WritableByteChannel} and {@link OutputStream}. * @param encoding if the input is an encoded URL, the character encoding (normally {@code "UTF-8"}). * If the URL is not encoded, then {@code null}. This argument is ignored if the given * input does not need to be converted from URL to {@code File}. + * @param options the options to use for creating a new byte channel. Can be null or empty for read-only. + * @param wrapper a function for creating wrapper around the factory, or {@code null} if none. + * It can be used for installing listener or for transforming data on the fly. + * @return the channel factory for the given input, or {@code null} if the given input is of unknown type. + * @throws IOException if an error occurred while processing the given input. + */ + public static ChannelFactory prepare( + final Object storage, final boolean allowWriteOnly, + final String encoding, final OpenOption[] options, + final UnaryOperator<ChannelFactory> wrapper) throws IOException + { + ChannelFactory factory = prepare(storage, allowWriteOnly, encoding, options); + if (factory != null && wrapper != null) { + factory = wrapper.apply(factory); + } + return factory; + } + + /** + * Returns a byte channel factory without wrappers, or {@code null} if unsupported. + * This method performs the same work than {@linkplain #prepare(Object, boolean, String, + * OpenOption[], UnaryOperator, UnaryOperator) above method}, but without wrappers. + * + * @param storage the stream or the file to open, or {@code null}. * @param allowWriteOnly whether to allow wrapping {@link WritableByteChannel} and {@link OutputStream}. + * @param encoding if the input is an encoded URL, the character encoding (normally {@code "UTF-8"}). * @param options the options to use for creating a new byte channel. Can be null or empty for read-only. * @return the channel factory for the given input, or {@code null} if the given input is of unknown type. * @throws IOException if an error occurred while processing the given input. */ - public static ChannelFactory prepare(Object storage, final String encoding, - final boolean allowWriteOnly, OpenOption... options) throws IOException + private static ChannelFactory prepare(Object storage, final boolean allowWriteOnly, + final String encoding, OpenOption[] options) throws IOException { /* * Unconditionally verify the options (unless 'allowWriteOnly' is true), diff --git a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/InternalOptionKey.java b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/InternalOptionKey.java new file mode 100644 index 0000000..6789d21 --- /dev/null +++ b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/InternalOptionKey.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sis.internal.storage.io; + +import java.util.function.UnaryOperator; +import org.apache.sis.setup.OptionKey; +import org.apache.sis.storage.StorageConnector; + + +/** + * {@link StorageConnector} options not part of public API. + * Some of those options may move to public API in the future if useful. + * + * @author Martin Desruisseaux (Geomatys) + * @version 1.2 + * + * @param <T> the type of option values. + * + * @since 1.2 + * @module + */ +public final class InternalOptionKey<T> extends OptionKey<T> { + /** + * For cross-version compatibility. + */ + private static final long serialVersionUID = 1786137598411493790L; + + /** + * Wraps readable or writable channels on creation. Wrappers can be used for example + * in order to listen to read events or for transforming bytes on the fly. + */ + @SuppressWarnings("unchecked") + public static final InternalOptionKey<UnaryOperator<ChannelFactory>> CHANNEL_FACTORY_WRAPPER = + (InternalOptionKey) new InternalOptionKey<>("READ_CHANNEL_WRAPPER", UnaryOperator.class); + + /** + * Creates a new key of the given name. + */ + public InternalOptionKey(final String name, final Class<T> type) { + super(name, type); + } +} diff --git a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/package-info.java b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/package-info.java index ea669bd..e2932d1 100644 --- a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/package-info.java +++ b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/package-info.java @@ -24,7 +24,7 @@ * may change in incompatible ways in any future version without notice. * * @author Martin Desruisseaux (Geomatys) - * @version 1.1 + * @version 1.2 * @since 0.3 * @module */ diff --git a/storage/sis-storage/src/main/java/org/apache/sis/storage/StorageConnector.java b/storage/sis-storage/src/main/java/org/apache/sis/storage/StorageConnector.java index d22cd81..6e77e1f 100644 --- a/storage/sis-storage/src/main/java/org/apache/sis/storage/StorageConnector.java +++ b/storage/sis-storage/src/main/java/org/apache/sis/storage/StorageConnector.java @@ -56,6 +56,7 @@ import org.apache.sis.internal.storage.io.ChannelDataInput; import org.apache.sis.internal.storage.io.ChannelImageInputStream; import org.apache.sis.internal.storage.io.InputStreamAdapter; import org.apache.sis.internal.storage.io.RewindableLineReader; +import org.apache.sis.internal.storage.io.InternalOptionKey; import org.apache.sis.internal.util.Strings; import org.apache.sis.io.InvalidSeekException; import org.apache.sis.setup.OptionKey; @@ -86,7 +87,7 @@ import org.apache.sis.setup.OptionKey; * is serializable.</p> * * @author Martin Desruisseaux (Geomatys) - * @version 1.1 + * @version 1.2 * @since 0.3 * @module */ @@ -923,8 +924,10 @@ public class StorageConnector implements Serializable { * URL, URI, File, Path or other types that may be added in future Apache SIS versions. * If the given storage is already a ReadableByteChannel, then the factory will return it as-is. */ - final ChannelFactory factory = ChannelFactory.prepare(storage, - getOption(OptionKey.URL_ENCODING), false, getOption(OptionKey.OPEN_OPTIONS)); + final ChannelFactory factory = ChannelFactory.prepare(storage, false, + getOption(OptionKey.URL_ENCODING), + getOption(OptionKey.OPEN_OPTIONS), + getOption(InternalOptionKey.CHANNEL_FACTORY_WRAPPER)); if (factory == null) { return null; }