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 558c65499f8996695ab7d8e62a3da459432f54fb
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Sat Dec 9 16:40:30 2023 +0100

    Add a "--metadata" option to the command line for making possible to 
specify the auxiliary metadata.
---
 .../main/org/apache/sis/console/Command.java       | 11 ++--
 .../main/org/apache/sis/console/CommandRunner.java | 61 +++++++++++++++++++++-
 .../apache/sis/console/FormattedOutputCommand.java | 15 +++---
 .../main/org/apache/sis/console/InfoCommand.java   |  2 +-
 .../org/apache/sis/console/MetadataCommand.java    |  5 +-
 .../main/org/apache/sis/console/Option.java        |  5 ++
 .../main/org/apache/sis/console/Options.properties |  7 +--
 .../org/apache/sis/console/Options_fr.properties   |  7 +--
 .../main/org/apache/sis/console/SIS.java           | 26 +++++++++
 .../org/apache/sis/console/TransformCommand.java   |  4 +-
 .../org/apache/sis/console/TranslateCommand.java   |  4 +-
 .../main/org/apache/sis/setup/OptionKey.java       |  2 +-
 12 files changed, 121 insertions(+), 28 deletions(-)

diff --git 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/Command.java 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/Command.java
index e6062a0e9b..530ad9a788 100644
--- 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/Command.java
+++ 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/Command.java
@@ -41,10 +41,12 @@ import org.apache.sis.util.logging.MonolineFormatter;
  * <tr><td>{@code help}       </td><td>Show a help overview.</td></tr>
  * <tr><td>{@code about}      </td><td>Show information about Apache SIS and 
system configuration.</td></tr>
  * <tr><td>{@code mime-type}  </td><td>Show MIME type for the given 
file.</td></tr>
+ * <tr><td>{@code identifier} </td><td>Show identifiers for metadata and 
referencing systems in the given file.</td></tr>
  * <tr><td>{@code metadata}   </td><td>Show metadata information for the given 
file.</td></tr>
  * <tr><td>{@code crs}        </td><td>Show Coordinate Reference System 
information for the given file or code.</td></tr>
- * <tr><td>{@code identifier} </td><td>Show identifiers for metadata and 
referencing systems in the given file.</td></tr>
+ * <tr><td>{@code info}       </td><td>Show resource-specific information 
(e.g., grid geometry).</td></tr>
  * <tr><td>{@code transform}  </td><td>Convert or transform coordinates from 
given source CRS to target CRS.</td></tr>
+ * <tr><td>{@code translate}  </td><td>Rewrite a data file in another 
format.</td></tr>
  * </table></blockquote>
  *
  * Each command can accepts some of the following options:
@@ -53,10 +55,11 @@ import org.apache.sis.util.logging.MonolineFormatter;
  * <caption>Supported command-line options</caption>
  * <tr><td>{@code --sourceCRS} </td><td>The Coordinate Reference System of 
input data.</td></tr>
  * <tr><td>{@code --targetCRS} </td><td>The Coordinate Reference System of 
output data.</td></tr>
+ * <tr><td>{@code --metadata}  </td><td>Relative path to an auxiliary metadata 
file.</td></tr>
  * <tr><td>{@code --format}    </td><td>The output format: {@code xml}, {@code 
wkt}, {@code wkt1} or {@code text}.</td></tr>
- * <tr><td>{@code --locale}    </td><td>The locale to use for the command 
output.</td></tr>
- * <tr><td>{@code --timezone}  </td><td>The timezone for the dates to be 
formatted.</td></tr>
- * <tr><td>{@code --encoding}  </td><td>The encoding to use for the command 
outputs and some inputs.</td></tr>
+ * <tr><td>{@code --locale}    </td><td>The locale to use for the console 
output.</td></tr>
+ * <tr><td>{@code --timezone}  </td><td>The timezone for the dates printed to 
the console output.</td></tr>
+ * <tr><td>{@code --encoding}  </td><td>The encoding to use for some text 
inputs and for console output.</td></tr>
  * <tr><td>{@code --colors}    </td><td>Whether colorized output shall be 
enabled.</td></tr>
  * <tr><td>{@code --brief}     </td><td>Whether the output should contain only 
brief information.</td></tr>
  * <tr><td>{@code --verbose}   </td><td>Whether the output should contain more 
detailed information.</td></tr>
diff --git 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/CommandRunner.java
 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/CommandRunner.java
index 581834dfa6..8de71c01f9 100644
--- 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/CommandRunner.java
+++ 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/CommandRunner.java
@@ -29,12 +29,16 @@ import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.io.OutputStreamWriter;
 import java.nio.charset.Charset;
+import java.nio.file.Path;
+import java.nio.file.InvalidPathException;
 import org.apache.sis.util.Locales;
 import org.apache.sis.util.Exceptions;
 import org.apache.sis.util.Workaround;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.internal.X364;
 import org.apache.sis.pending.jdk.JDK17;
+import org.apache.sis.storage.DataOptionKey;
+import org.apache.sis.storage.StorageConnector;
 
 
 /**
@@ -302,8 +306,29 @@ abstract class CommandRunner {
         if (value instanceof CharSequence) {
             return value.toString();
         }
-        final String name = key.label();
-        throw new 
InvalidOptionException(Errors.format(Errors.Keys.IllegalOptionValue_2, name, 
value), name);
+        throw invalidOption(key, value, null);
+    }
+
+    /**
+     * Returns the value of the specified option as a path.
+     *
+     * @param  key  the option for which to get a value.
+     * @return the requested option, or {@code null} if not present.
+     * @throws InvalidOptionException if the value is not convertible to a 
path.
+     */
+    final Path getOptionAsPath(final Option key) throws InvalidOptionException 
{
+        final Object value = options.get(key);
+        if (value == null) return null;
+        if (value instanceof Path) {
+            return (Path) value;
+        }
+        Throwable cause = null;
+        if (value instanceof CharSequence) try {
+            return Path.of(value.toString());
+        } catch (InvalidPathException e) {
+            cause = e;
+        }
+        throw invalidOption(key, value, cause);
     }
 
     /**
@@ -376,6 +401,38 @@ abstract class CommandRunner {
         return files.isEmpty() && System.console() == null;
     }
 
+    /**
+     * Returns a storage connector for the specified input.
+     * This method should be invoked only for the input, not for the output, 
because it uses input-specific options.
+     * Conversely, this storage connector does <strong>not</strong> include 
the options intended for console output
+     * such as {@linkplain #locale}, {@link #timezone} and {@linkplain 
#encoding}.
+     *
+     * @param  input  the storage input.
+     * @return storage connector for the specified input.
+     * @throws InvalidOptionException if an option has an invalid value.
+     */
+    final StorageConnector inputConnector(final Object input) throws 
InvalidOptionException {
+        final var connector = new StorageConnector(input);
+        final Path p = getOptionAsPath(Option.METADATA);
+        if (p != null) {
+            connector.setOption(DataOptionKey.METADATA_PATH, p);
+        }
+        return connector;
+    }
+
+    /**
+     * Returns the exception to throw for an invalid option.
+     *
+     * @param  key    the requested option.
+     * @param  value  the value associated to the specified option.
+     * @param  cause  cause of the invalidity, or {@code null} if none.
+     * @return the exception to throw.
+     */
+    private static InvalidOptionException invalidOption(Option key, Object 
value, Throwable cause) {
+        final String name = key.label();
+        return new 
InvalidOptionException(Errors.format(Errors.Keys.IllegalOptionValue_2, name, 
value), cause, name);
+    }
+
     /**
      * Prints the <q>Cannot open …</q> error message followed by the message 
in the given exception.
      *
diff --git 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/FormattedOutputCommand.java
 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/FormattedOutputCommand.java
index f268dbbff2..e65fd49cce 100644
--- 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/FormattedOutputCommand.java
+++ 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/FormattedOutputCommand.java
@@ -178,14 +178,14 @@ abstract class FormattedOutputCommand extends 
CommandRunner {
      * The input format is detected automatically (this is 
<strong>not</strong> {@link #outputFormat}).
      *
      * @return a {@link Metadata} or {@link CoordinateReferenceSystem} 
instance, or {@code null} if none.
+     * @throws InvalidOptionException if an option has an invalid value.
      * @throws DataStoreException if an error occurred while reading the file.
      * @throws FactoryException if an error occurred while looking for a CRS 
identifier.
      */
-    final Object readMetadataOrCRS() throws DataStoreException, 
FactoryException {
+    final Object readMetadataOrCRS() throws InvalidOptionException, 
DataStoreException, FactoryException {
+        final Object input;
         if (useStandardInput()) {
-            try (DataStore store = DataStores.open(System.in)) {
-                return store.getMetadata();
-            }
+            input = System.in;
         } else if (hasUnexpectedFileCount(1, 1)) {
             hasUnexpectedFileCount = true;
             return null;
@@ -197,9 +197,10 @@ abstract class FormattedOutputCommand extends 
CommandRunner {
                     return CRS.forCode(c);
                 }
             }
-            try (DataStore store = DataStores.open(file)) {
-                return store.getMetadata();
-            }
+            input = file;
+        }
+        try (DataStore store = DataStores.open(inputConnector(input))) {
+            return store.getMetadata();
         }
     }
 
diff --git 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/InfoCommand.java
 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/InfoCommand.java
index b09ab42c2f..286a7e8bc2 100644
--- 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/InfoCommand.java
+++ 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/InfoCommand.java
@@ -48,7 +48,7 @@ final class InfoCommand extends FormattedOutputCommand {
      * Returns valid options for the {@code "metadata"} command.
      */
     static EnumSet<Option> options() {
-        return EnumSet.of(Option.LOCALE, Option.TIMEZONE, Option.COLORS, 
Option.VERBOSE, Option.HELP, Option.DEBUG);
+        return EnumSet.of(Option.METADATA, Option.LOCALE, Option.TIMEZONE, 
Option.COLORS, Option.VERBOSE, Option.HELP, Option.DEBUG);
     }
 
     /**
diff --git 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/MetadataCommand.java
 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/MetadataCommand.java
index b12a6b4010..365cf47610 100644
--- 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/MetadataCommand.java
+++ 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/MetadataCommand.java
@@ -32,7 +32,8 @@ import org.apache.sis.util.collection.TreeTable;
  * Some available options are:
  *
  * <ul>
- *   <li>{@code --format}: the output format (text, XML or GPX).</li>
+ *   <li>{@code --metadata}: relative path to auxiliary metadata to combine 
with the main metadata.</li>
+ *   <li>{@code --format}:   the output format (text, XML or GPX).</li>
  * </ul>
  *
  * @author  Martin Desruisseaux (Geomatys)
@@ -42,7 +43,7 @@ final class MetadataCommand extends FormattedOutputCommand {
      * Returns valid options for the {@code "metadata"} command.
      */
     static EnumSet<Option> options() {
-        return EnumSet.of(Option.FORMAT, Option.LOCALE, Option.TIMEZONE,
+        return EnumSet.of(Option.METADATA, Option.FORMAT, Option.LOCALE, 
Option.TIMEZONE,
                 Option.ENCODING, Option.COLORS, Option.VERBOSE, Option.HELP, 
Option.DEBUG);
     }
 
diff --git 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/Option.java 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/Option.java
index 23e6f2f1f3..8fdead8c96 100644
--- 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/Option.java
+++ 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/Option.java
@@ -37,6 +37,11 @@ enum Option {
      */
     TARGET_CRS(true),
 
+    /**
+     * Relative path to an auxiliary metadata file.
+     */
+    METADATA(true),
+
     /**
      * The file to write.
      */
diff --git 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/Options.properties
 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/Options.properties
index e8c984cb5d..ae275090d8 100644
--- 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/Options.properties
+++ 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/Options.properties
@@ -2,11 +2,12 @@
 # and to You under the Apache License, Version 2.0.
 sourceCRS=The Coordinate Reference System of input data.
 targetCRS=The Coordinate Reference System of output data.
+metadata=Relative path to an auxiliary metadata file.
 output=The output file.
 format=The output format. Examples: xml, wkt, wkt1 or text.
-locale=The locale to use for the command output.
-timezone=The timezone for the dates to be formatted.
-encoding=The encoding to use for the command outputs and some inputs.
+locale=The locale to use for the console output.
+timezone=The timezone for the dates printed to the console output.
+encoding=The encoding to use for some text inputs and for console output.
 colors=Whether colorized output shall be enabled.
 brief=Reduce the output to only brief information.
 verbose=Request the output to contain more detailed information.
diff --git 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/Options_fr.properties
 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/Options_fr.properties
index 1828e7be7d..6e7bf7227f 100644
--- 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/Options_fr.properties
+++ 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/Options_fr.properties
@@ -2,11 +2,12 @@
 # and to You under the Apache License, Version 2.0.
 sourceCRS=Le syst\u00e8me de r\u00e9f\u00e9rence des coordonn\u00e9es source.
 targetCRS=Le syst\u00e8me de r\u00e9f\u00e9rence des coordonn\u00e9es 
destination.
+metadata=Chemin relatif vers un fichier auxiliaire de m\u00e9ta-donn\u00e9es.
 output=Le fichier de sortie.
 format=Le format de sortie. Exemples: xml, wkt, wkt1 ou text.
-locale=Les param\u00e8tres r\u00e9gionaux \u00e0 utiliser pour la sortie de la 
commande.
-timezone=Le fuseau horaire des dates \u00e0 \u00e9crire.
-encoding=L\u2019encodage des caract\u00e8res \u00e0 utiliser pour la sortie de 
la commande et certaines entr\u00e9es.
+locale=Les param\u00e8tres r\u00e9gionaux \u00e0 utiliser pour la sortie vers 
la console.
+timezone=Le fuseau horaire des dates \u00e0 \u00e9crire dans la sortie console.
+encoding=L\u2019encodage des caract\u00e8res \u00e0 utiliser pour certaines 
entr\u00e9es textuels et la sortie vers la console.
 colors=Indique si l\u2019affichage peut \u00eatre en couleurs.
 brief=Indique que la sortie de la commande ne doit contenir que de br\u00e8ves 
informations.
 verbose=Indique que la sortie de la commande doit contenir des informations 
plus d\u00e9taill\u00e9es.
diff --git 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/SIS.java 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/SIS.java
index 2f78e06b7e..b6f6d15934 100644
--- a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/SIS.java
+++ b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/SIS.java
@@ -432,6 +432,19 @@ public final class SIS extends Static {
         /** Creates the instance for metadata or CRS. */
         Metadata(String command) {super(command);}
 
+        /**
+         * Sets the path to auxiliary metadata, relative to the main file.
+         * The {@code '*'} character stands for the name of the main file.
+         * For example if the main file is {@code "city-center.tiff"},
+         * then {@code "*.xml"} stands for {@code "city-center.xml"}.
+         *
+         * @param  value  relative path to auxiliary metadata.
+         * @return a new builder or {@code this}, for method call chaining.
+         */
+        public Metadata metadata(String value) {
+            return set(Option.METADATA, value);
+        }
+
         /**
          * Sets the output format.
          *
@@ -699,6 +712,19 @@ public final class SIS extends Static {
         /** Creates the unique instance. */
         Translate() {super("translate");}
 
+        /**
+         * Sets the path to auxiliary metadata, relative to the main file.
+         * The {@code '*'} character stands for the name of the main file.
+         * For example if the main file is {@code "city-center.tiff"},
+         * then {@code "*.xml"} stands for {@code "city-center.xml"}.
+         *
+         * @param  value  relative path to auxiliary metadata.
+         * @return a new builder or {@code this}, for method call chaining.
+         */
+        public Translate metadata(String value) {
+            return set(Option.METADATA, value);
+        }
+
         /**
          * Sets the destination file.
          *
diff --git 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/TransformCommand.java
 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/TransformCommand.java
index ea45f0ee88..698ee27bfa 100644
--- 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/TransformCommand.java
+++ 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/TransformCommand.java
@@ -77,7 +77,6 @@ import 
org.apache.sis.metadata.iso.extent.DefaultGeographicBoundingBox;
 import org.apache.sis.util.resources.Vocabulary;
 import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.logging.Logging;
-import org.apache.sis.setup.OptionKey;
 
 // Specific to the geoapi-3.1 and geoapi-4.0 branches:
 import org.opengis.referencing.ObjectDomain;
@@ -219,8 +218,7 @@ final class TransformCommand extends FormattedOutputCommand 
{
                 }
             } else {
                 for (final Object file : files) {
-                    final var c = new StorageConnector(file);
-                    c.setOption(OptionKey.ENCODING, encoding);
+                    final StorageConnector c = inputConnector(file);
                     try (BufferedReader in = 
IOUtilities.toBuffered(c.commit(Reader.class, "transform"))) {
                         points = readCoordinates(in, file);
                     }
diff --git 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/TranslateCommand.java
 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/TranslateCommand.java
index 38864f912e..142fe59bbe 100644
--- 
a/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/TranslateCommand.java
+++ 
b/endorsed/src/org.apache.sis.console/main/org/apache/sis/console/TranslateCommand.java
@@ -49,7 +49,7 @@ final class TranslateCommand extends CommandRunner {
      * @throws InvalidOptionException if an illegal option has been provided, 
or the option has an illegal value.
      */
     TranslateCommand(final int commandIndex, final Object[] arguments) throws 
InvalidOptionException {
-        super(commandIndex, arguments, EnumSet.of(Option.OUTPUT, 
Option.FORMAT, Option.HELP, Option.DEBUG));
+        super(commandIndex, arguments, EnumSet.of(Option.METADATA, 
Option.OUTPUT, Option.FORMAT, Option.HELP, Option.DEBUG));
     }
 
     /**
@@ -72,7 +72,7 @@ final class TranslateCommand extends CommandRunner {
         });
         try (DataStore target = DataStores.openWritable(connector, format)) {
             for (final Object file : files) {
-                try (DataStore source = DataStores.open(file)) {
+                try (DataStore source = DataStores.open(inputConnector(file))) 
{
                     if (target instanceof WritableAggregate) {
                         ((WritableAggregate) target).add(source);
                     } else if (target instanceof WritableGridCoverageResource) 
{
diff --git 
a/endorsed/src/org.apache.sis.util/main/org/apache/sis/setup/OptionKey.java 
b/endorsed/src/org.apache.sis.util/main/org/apache/sis/setup/OptionKey.java
index 19b1f40705..03377c606d 100644
--- a/endorsed/src/org.apache.sis.util/main/org/apache/sis/setup/OptionKey.java
+++ b/endorsed/src/org.apache.sis.util/main/org/apache/sis/setup/OptionKey.java
@@ -108,7 +108,7 @@ public class OptionKey<T> implements Serializable {
 
     /**
      * The character encoding of document content.
-     * This option can be used when the file to read does not describe itself 
its encoding.
+     * This option can be used when the file to read or write does not 
describe itself its encoding.
      * For example, this option can be used when reading plain text files, but 
is ignored when
      * reading XML files having a {@code <?xml version="1.0" encoding="…"?>} 
declaration.
      *

Reply via email to