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 a83a431ca031215ada40d8adbbc530f87f66e1c1 Author: Martin Desruisseaux <martin.desruisse...@geomatys.com> AuthorDate: Mon Sep 6 11:58:09 2021 +0200 Modify (again) the way schema is customized, reverting back to an interface but specifying it with `OptionKey` instead than by the provider. The interface is kept in internal package for now, waiting to gain more experience with its use before to move (with changes) in public API. --- .../apache/sis/internal/sql/feature/Analyzer.java | 20 +++++--- .../sis/internal/sql/feature/SchemaModifier.java | 18 +++++++- .../sis/internal/sql/feature/TableReference.java | 28 ------------ .../java/org/apache/sis/storage/sql/SQLStore.java | 53 ++++------------------ .../sql/feature/SelectionClauseWriterTest.java | 45 +++++++----------- 5 files changed, 57 insertions(+), 107 deletions(-) diff --git a/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/Analyzer.java b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/Analyzer.java index 748ad0e..178b9f9 100644 --- a/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/Analyzer.java +++ b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/Analyzer.java @@ -134,7 +134,7 @@ final class Analyzer { private transient NameSpace namespace; /** - * User-specified modification to the features. Shall not be {@code null}. + * User-specified modification to the features, or {@code null} if none. */ private final SchemaModifier customizer; @@ -145,7 +145,7 @@ final class Analyzer { * @param connection an existing connection to the database, used only for the lifetime of this {@code Analyzer}. * @param metadata value of {@code connection.getMetaData()} (provided because already known by caller). * @param isSpatial whether the database contains "GEOMETRY_COLUMNS" and "SPATIAL_REF_SYS" tables. - * @param customizer user-specified modification to the features. Shall not be {@code null}. + * @param customizer user-specified modification to the features, or {@code null} if none. */ Analyzer(final Database<?> database, final Connection connection, final DatabaseMetaData metadata, final boolean isSpatial, final SchemaModifier customizer) @@ -299,6 +299,16 @@ final class Analyzer { } /** + * Creates the feature type from the content of the given builder. + */ + private FeatureType createFeatureType(final TableReference id, final FeatureTypeBuilder feature) + throws DataStoreException + { + feature.setName(id.getName(this)); + return (customizer != null) ? customizer.editFeatureType(id, feature) : feature.build(); + } + + /** * Initializes the value getter on the given column. * This method shall be invoked only after geometry columns have been identifier. */ @@ -602,7 +612,6 @@ final class Analyzer { */ @Override public FeatureType buildFeatureType() throws DataStoreException, SQLException { - feature.setName(id.getName(Analyzer.this)); String remarks = id.freeText; if (id instanceof Relation) { try (ResultSet reflect = metadata.getTables(id.catalog, schemaEsc, tableEsc, null)) { @@ -617,7 +626,7 @@ final class Analyzer { if (remarks != null) { feature.setDefinition(remarks); } - return customizer.editFeatureType(id, feature); + return createFeatureType(id, feature); } } @@ -717,11 +726,10 @@ final class Analyzer { */ @Override FeatureType buildFeatureType() throws DataStoreException { - feature.setName(id.getName(Analyzer.this)); if (id.freeText != null) { feature.setDefinition(id.freeText); } - return customizer.editFeatureType(id, feature); + return createFeatureType(id, feature); } } } 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 728173e..8902ce3 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.setup.OptionKey; // Branch-dependent imports import org.opengis.feature.FeatureType; @@ -26,6 +27,8 @@ import org.opengis.feature.FeatureType; /** * Modifies the feature types inferred from database analysis. * + * @todo May move to public API (in revised form) in a future version. + * * @author Martin Desruisseaux (Geomatys) * @version 1.1 * @since 1.1 @@ -37,11 +40,24 @@ public interface SchemaModifier { * The given builder is initialized with all properties inferred from the table definition. * Implementation of this method can add, remove or modify properties. * + * <p>The default implementation returns {@code feature.build()} without making any change.</p> + * * @param table the catalog (if present), schema (if present) and table name. * @param feature a feature type builder initialized with all properties inferred by the analysis of a table. * This builder can be modified in-place. * @throws DataStoreException if an error occurred while modifying the feature type. * @return the feature type to use for the specified table. */ - FeatureType editFeatureType(TableReference table, FeatureTypeBuilder feature) throws DataStoreException; + default FeatureType editFeatureType(TableReference table, FeatureTypeBuilder feature) throws DataStoreException { + return feature.build(); + } + + /** + * The option for declaring a schema modifier at {@link org.apache.sis.storage.sql.SQLStore} creation time. + * + * @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) { + }; } diff --git a/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/TableReference.java b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/TableReference.java index 3fdcb81..eef3f54 100644 --- a/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/TableReference.java +++ b/storage/sis-sqlstore/src/main/java/org/apache/sis/internal/sql/feature/TableReference.java @@ -83,34 +83,6 @@ public class TableReference { } /** - * Returns the components of the table identification. - * The returned array has a length of 1, 2 or 3 with the following content: - * - * <ul> - * <li>If a catalog name exists, then the array has a length of 3 with <var>catalog name</var>, - * <var>schema name</var> (possibly null) and <var>table name</var> elements in that order.</li> - * <li>Otherwise if a schema name exists, then the array has a length of 2 with - * <var>schema name</var> and <var>table name</var> elements in that order.</li> - * <li>Otherwise the array has a length of 1 with the <var>table name</var> element.</li> - * </ul> - * - * @return the catalog (if present), schema (if present) and table names. - */ - @SuppressWarnings("fallthrough") - public final String[] getNames() { - final String c = trimOrNull(catalog); - final String s = trimOrNull(schema); - final String[] names = new String[c != null ? 3 : s != null ? 2 : 1]; - int n = 0; - switch (names.length) { - default: names[n++] = c; // Fall through. - case 2: names[n++] = s; - case 1: names[n] = table; - } - return names; - } - - /** * Creates a name for the feature type backed by this table. * This method does not cache the value; * caller is expected to invoke only once and store the name. diff --git a/storage/sis-sqlstore/src/main/java/org/apache/sis/storage/sql/SQLStore.java b/storage/sis-sqlstore/src/main/java/org/apache/sis/storage/sql/SQLStore.java index bd7d41f..dabf372 100644 --- a/storage/sis-sqlstore/src/main/java/org/apache/sis/storage/sql/SQLStore.java +++ b/storage/sis-sqlstore/src/main/java/org/apache/sis/storage/sql/SQLStore.java @@ -34,7 +34,6 @@ import org.apache.sis.storage.StorageConnector; import org.apache.sis.storage.event.StoreEvent; import org.apache.sis.storage.event.StoreListener; import org.apache.sis.storage.event.WarningEvent; -import org.apache.sis.feature.builder.FeatureTypeBuilder; import org.apache.sis.internal.sql.feature.Database; import org.apache.sis.internal.sql.feature.Resources; import org.apache.sis.internal.sql.feature.SchemaModifier; @@ -45,9 +44,6 @@ import org.apache.sis.setup.OptionKey; import org.apache.sis.util.ArgumentChecks; import org.apache.sis.util.Exceptions; -// Branch-dependent imports -import org.opengis.feature.FeatureType; - /** * A data store capable to read and create features from a spatial database. @@ -100,6 +96,12 @@ public class SQLStore extends DataStore implements Aggregate { private Metadata metadata; /** + * The user-specified method for customizing the schema inferred by table analysis. + * This is {@code null} if there is none. + */ + private final SchemaModifier customizer; + + /** * Creates a new instance for the given storage. * The given {@code connector} shall contain a {@link DataSource}. * The given table names shall be qualified names of 1, 2 or 3 components. @@ -138,6 +140,7 @@ public class SQLStore extends DataStore implements Aggregate { } } this.tableNames = tableNames; + this.customizer = connector.getOption(SchemaModifier.OPTION); } /** @@ -169,21 +172,12 @@ public class SQLStore extends DataStore implements Aggregate { } /** - * Returns customizations on the feature type inferred from the database analysis. - * This is a workaround for giving access to protected method {@code customize(…)} - * from a different package. - */ - private SchemaModifier customizer() { - return (id,f) -> customize(id.getNames(), f); - } - - /** * Returns the database model, analyzing the database schema when first needed. */ private synchronized Database<?> model() throws DataStoreException { if (model == null) { try (Connection c = source.getConnection()) { - model = Database.create(this, source, c, geomLibrary, tableNames, customizer(), listeners); + model = Database.create(this, source, c, geomLibrary, tableNames, customizer, listeners); } catch (DataStoreException e) { throw e; } catch (Exception e) { @@ -202,7 +196,7 @@ public class SQLStore extends DataStore implements Aggregate { */ private Database<?> model(final Connection c) throws Exception { if (model == null) { - model = Database.create(this, source, c, geomLibrary, tableNames, customizer(), listeners); + model = Database.create(this, source, c, geomLibrary, tableNames, customizer, listeners); } return model; } @@ -255,35 +249,6 @@ public class SQLStore extends DataStore implements Aggregate { } /** - * Invoked after analysis of a table for allowing modifications of the inferred feature type. - * The given builder is initialized with all properties inferred from the table definition. - * Implementation of this method can add, remove or modify properties. - * - * <p>The database table for which a feature type is created is identified by the {@code table} argument. - * This argument is an array of length of 1, 2 or 3 with the following content:</p> - * - * <ul> - * <li>If a catalog name exists, then the array has a length of 3 with <var>catalog name</var>, - * <var>schema name</var> (possibly null) and <var>table name</var> elements in that order.</li> - * <li>Otherwise if a schema name exists, then the array has a length of 2 with - * <var>schema name</var> and <var>table name</var> elements in that order.</li> - * <li>Otherwise the array has a length of 1 with the <var>table name</var> element.</li> - * </ul> - * - * The default implementation returns {@code feature.build()} without making any change. - * - * @param table the catalog (if present), schema (if present) and table name. - * @param feature a feature type builder initialized with all properties inferred by the analysis of a table. - * This builder can be modified in-place. - * @return the feature type to use for the specified table. - * - * @since 1.1 - */ - protected FeatureType customize(String[] table, FeatureTypeBuilder feature) { - return feature.build(); - } - - /** * Returns the resources (features or coverages) in this SQL store. * The list contains only the tables explicitly named at construction time. * diff --git a/storage/sis-sqlstore/src/test/java/org/apache/sis/internal/sql/feature/SelectionClauseWriterTest.java b/storage/sis-sqlstore/src/test/java/org/apache/sis/internal/sql/feature/SelectionClauseWriterTest.java index 5707e9a..357984f 100644 --- a/storage/sis-sqlstore/src/test/java/org/apache/sis/internal/sql/feature/SelectionClauseWriterTest.java +++ b/storage/sis-sqlstore/src/test/java/org/apache/sis/internal/sql/feature/SelectionClauseWriterTest.java @@ -19,8 +19,6 @@ package org.apache.sis.internal.sql.feature; import org.apache.sis.geometry.GeneralEnvelope; import org.apache.sis.storage.StorageConnector; import org.apache.sis.storage.DataStore; -import org.apache.sis.storage.DataStoreException; -import org.apache.sis.storage.sql.SQLStore; import org.apache.sis.storage.sql.SQLStoreProvider; import org.apache.sis.feature.builder.AttributeTypeBuilder; import org.apache.sis.feature.builder.FeatureTypeBuilder; @@ -50,7 +48,7 @@ import org.opengis.filter.SpatialOperator; * @since 1.1 * @module */ -public final strictfp class SelectionClauseWriterTest extends TestCase { +public final strictfp class SelectionClauseWriterTest extends TestCase implements SchemaModifier { /** * The factory to use for creating the filter objects. */ @@ -69,30 +67,6 @@ public final strictfp class SelectionClauseWriterTest extends TestCase { } /** - * The data store to use for testing purpose. - * This data store modifies the schema for simulating a spatial database. - */ - private static final class Store extends SQLStore { - /** - * Creates a new data store. - */ - Store(final StorageConnector connector) throws DataStoreException { - super(null, connector, SQLStoreProvider.createTableName(null, null, Database.WILDCARD)); - } - - /** - * Invoked when the feature type interred from the test database is created. - * This method add a CRS on a property for testing purpose. - */ - @Override - protected FeatureType customize(final String[] table, final FeatureTypeBuilder builder) { - assertArrayEquals(new String[] {"APP", "TEST"}, table); - ((AttributeTypeBuilder<?>) builder.getProperty("BETA")).setCRS(HardCodedCRS.WGS84); - return super.customize(table, builder); - } - } - - /** * Tests on Derby. * * @throws Exception if an error occurred while testing the database. @@ -103,7 +77,9 @@ public final strictfp class SelectionClauseWriterTest extends TestCase { db.executeSQL(SelectionClauseWriterTest.class, "CREATE TABLE TEST (ALPHA INTEGER, BETA INTEGER, GAMMA INTEGER, PI FLOAT);"); - try (DataStore store = new Store(new StorageConnector(db.source))) { + final StorageConnector connector = new StorageConnector(db.source); + connector.setOption(SchemaModifier.OPTION, this); + try (DataStore store = new SQLStoreProvider().open(connector)) { table = (Table) store.findResource("TEST"); testSimpleFilter(); testGeometricFilter(); @@ -137,6 +113,19 @@ public final strictfp class SelectionClauseWriterTest extends TestCase { } /** + * Invoked when the feature type interred from the test database is created. + * This method add a CRS on a property for testing purpose. + */ + @Override + public FeatureType editFeatureType(final TableReference table, final FeatureTypeBuilder feature) { + assertEquals("", table.catalog); + assertEquals("APP", table.schema); + assertEquals("TEST", table.table); + ((AttributeTypeBuilder<?>) feature.getProperty("BETA")).setCRS(HardCodedCRS.WGS84); + return feature.build(); + } + + /** * Verifies that a spatial operator transforms literal value before-hand if possible. */ private void testGeometricFilterWithTransform() {