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 882b2546639adb474a17795b70d9cea4bdf0f517 Author: Martin Desruisseaux <martin.desruisse...@geomatys.com> AuthorDate: Fri Sep 29 15:55:31 2023 +0200 Modification of the fix about the property name of a link. The check for instance of `ValueReference` should be merged with the existing check done 20 lines below. --- .../main/org/apache/sis/storage/FeatureQuery.java | 42 ++++++++------- .../org/apache/sis/storage/FeatureQueryTest.java | 60 +++++++++++++++------- 2 files changed, 61 insertions(+), 41 deletions(-) diff --git a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/FeatureQuery.java b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/FeatureQuery.java index d0d8f9c218..172b87705a 100644 --- a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/FeatureQuery.java +++ b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/FeatureQuery.java @@ -738,7 +738,7 @@ public class FeatureQuery extends Query implements Cloneable, Serializable { final NamedExpression item = projection[column]; /* * For each property, get the expected type (mandatory) and its name (optional). - * A default name will be computed if no alias were explicitly given by user. + * A default name will be computed if no alias was explicitly given by the user. */ final Expression<? super Feature,?> expression = item.expression; final FeatureExpression<?,?> fex = FeatureExpression.castOrCopy(expression); @@ -748,14 +748,6 @@ public class FeatureQuery extends Query implements Cloneable, Serializable { expression.getFunctionName().toInternationalString(), column)); } GenericName name = item.alias; - if (name == null && expression instanceof ValueReference<?,?>) { - /* - * If we do not have an alias, use the original property name. - * This name may be different from the resultType name because of links or functions. - */ - name = valueType.getProperty(((ValueReference<?, ?>) expression).getXPath()).getName(); - } - if (name == null) { /* * Build a list of aliases declared by the user, for making sure that we do not collide with them. @@ -777,14 +769,20 @@ public class FeatureQuery extends Query implements Cloneable, Serializable { */ CharSequence text = null; if (expression instanceof ValueReference<?,?>) { - final GenericName current = resultType.getName(); - if (current != null && names.add(current.toString())) { - continue; - } String xpath = ((ValueReference<?,?>) expression).getXPath().trim(); - xpath = xpath.substring(xpath.lastIndexOf(XPath.SEPARATOR) + 1); // Works also if '/' is not found. - if (!(xpath.isEmpty() || names.contains(xpath))) { - text = xpath; + /* + * Before to take the tip, take the existing `GenericName` instance from the property. + * It should be equivalent, except that it may be a `ScopedName` instead of `LocalName`. + * We do not take `resultType.getName()` because the latter is different if the property + * is itself a link to another property (in which case `resultType` is the final target). + */ + name = valueType.getProperty(xpath).getName(); + if (name == null || !names.add(name.toString())) { + name = null; + xpath = xpath.substring(xpath.lastIndexOf(XPath.SEPARATOR) + 1); // Works also if '/' is not found. + if (!(xpath.isEmpty() || names.contains(xpath))) { + text = xpath; + } } } /* @@ -793,15 +791,15 @@ public class FeatureQuery extends Query implements Cloneable, Serializable { * (for easier programmatic use) because `GenericName` implementation is designed for * providing localized names only if explicitly requested. */ - if (text == null) do { - text = Vocabulary.formatInternational(Vocabulary.Keys.Unnamed_1, ++unnamedNumber); - } while (!names.add(text.toString())); - name = Names.createLocalName(null, null, text); + if (name == null) { + if (text == null) do { + text = Vocabulary.formatInternational(Vocabulary.Keys.Unnamed_1, ++unnamedNumber); + } while (!names.add(text.toString())); + name = Names.createLocalName(null, null, text); + } } /* * If the attribute that we just added should be virtual, replace the attribute by an operation. - * We need to keep the property name computed by `fex.expectedType(…)` for the operation result, - * because that name is the name of the link to create if the operation is `ValueReference`. */ if (item.type == ProjectionType.COMPUTING && resultType instanceof AttributeTypeBuilder<?>) { final var ab = (AttributeTypeBuilder<?>) resultType; diff --git a/endorsed/src/org.apache.sis.storage/test/org/apache/sis/storage/FeatureQueryTest.java b/endorsed/src/org.apache.sis.storage/test/org/apache/sis/storage/FeatureQueryTest.java index 4dea8a07ca..a165f2bc7d 100644 --- a/endorsed/src/org.apache.sis.storage/test/org/apache/sis/storage/FeatureQueryTest.java +++ b/endorsed/src/org.apache.sis.storage/test/org/apache/sis/storage/FeatureQueryTest.java @@ -63,12 +63,12 @@ public final class FeatureQueryTest extends TestCase { /** * An arbitrary number of features, all of the same type. */ - private final Feature[] features; + private Feature[] features; /** * The {@link #features} array wrapped in a in-memory feature set. */ - private final FeatureSet featureSet; + private FeatureSet featureSet; /** * The query to be executed. @@ -76,9 +76,31 @@ public final class FeatureQueryTest extends TestCase { private final FeatureQuery query; /** - * Creates a new test with a feature type composed of two attributes and one association. + * Creates a new test case. */ public FeatureQueryTest() { + query = new FeatureQuery(); + } + + /** + * Creates a simple feature with a property flagged as an identifier. + */ + private void createFeatureWithIdentifier() { + final FeatureTypeBuilder ftb = new FeatureTypeBuilder().setName("Test"); + ftb.addAttribute(String.class).setName("id").addRole(AttributeRole.IDENTIFIER_COMPONENT); + final FeatureType type = ftb.build(); + features = new Feature[] { + type.newInstance() + }; + features[0].setPropertyValue("id", "id-0"); + featureSet = new MemoryFeatureSet(null, type, Arrays.asList(features)); + } + + /** + * Creates a set of features common to most tests. + * The feature type is composed of two attributes and one association. + */ + private void createFeaturesWithAssociation() { FeatureTypeBuilder ftb; // A dependency of the test feature type. @@ -100,7 +122,6 @@ public final class FeatureQueryTest extends TestCase { feature(type, null, 4, 1, 0) }; featureSet = new MemoryFeatureSet(null, type, Arrays.asList(features)); - query = new FeatureQuery(); } /** @@ -158,6 +179,7 @@ public final class FeatureQueryTest extends TestCase { */ @Test public void testLimit() throws DataStoreException { + createFeaturesWithAssociation(); query.setLimit(2); verifyQueryResult(0, 1); } @@ -169,6 +191,7 @@ public final class FeatureQueryTest extends TestCase { */ @Test public void testOffset() throws DataStoreException { + createFeaturesWithAssociation(); query.setOffset(2); verifyQueryResult(2, 3, 4); } @@ -180,6 +203,7 @@ public final class FeatureQueryTest extends TestCase { */ @Test public void testSortBy() throws DataStoreException { + createFeaturesWithAssociation(); final FilterFactory<Feature,?,?> ff = DefaultFilterFactory.forFeatures(); query.setSortBy(ff.sort(ff.property("value1", Integer.class), SortOrder.ASCENDING), ff.sort(ff.property("value2", Integer.class), SortOrder.DESCENDING)); @@ -193,6 +217,7 @@ public final class FeatureQueryTest extends TestCase { */ @Test public void testSelection() throws DataStoreException { + createFeaturesWithAssociation(); final FilterFactory<Feature,?,?> ff = DefaultFilterFactory.forFeatures(); query.setSelection(ff.equal(ff.property("value1", Integer.class), ff.literal(2), true, MatchAction.ALL)); @@ -207,6 +232,7 @@ public final class FeatureQueryTest extends TestCase { */ @Test public void testSelectionThroughAssociation() throws DataStoreException { + createFeaturesWithAssociation(); final FilterFactory<Feature,?,?> ff = DefaultFilterFactory.forFeatures(); query.setSelection(ff.equal(ff.property("dependency/value3"), ff.literal(18))); verifyQueryResult(3); @@ -219,6 +245,7 @@ public final class FeatureQueryTest extends TestCase { */ @Test public void testProjection() throws DataStoreException { + createFeaturesWithAssociation(); final FilterFactory<Feature,?,?> ff = DefaultFilterFactory.forFeatures(); query.setProjection(new FeatureQuery.NamedExpression(ff.property("value1", Integer.class), (String) null), new FeatureQuery.NamedExpression(ff.property("value1", Integer.class), "renamed1"), @@ -252,6 +279,7 @@ public final class FeatureQueryTest extends TestCase { */ @Test public void testProjectionByNames() throws DataStoreException { + createFeaturesWithAssociation(); query.setProjection("value2"); final Feature instance = executeAndGetFirst(); final PropertyType p = TestUtilities.getSingleton(instance.getType().getProperties(true)); @@ -266,6 +294,7 @@ public final class FeatureQueryTest extends TestCase { */ @Test public void testDefaultColumnName() throws DataStoreException { + createFeaturesWithAssociation(); final FilterFactory<Feature,?,?> ff = DefaultFilterFactory.forFeatures(); query.setLimit(1); query.setProjection( @@ -291,6 +320,7 @@ public final class FeatureQueryTest extends TestCase { */ @Test public void testProjectionOfAbstractType() throws DataStoreException { + createFeaturesWithAssociation(); final FilterFactory<Feature,?,?> ff = DefaultFilterFactory.forFeatures(); query.setProjection(new FeatureQuery.NamedExpression(ff.property("value1"), (String) null), new FeatureQuery.NamedExpression(ff.property("/*/unknown"), "unexpected")); @@ -320,6 +350,7 @@ public final class FeatureQueryTest extends TestCase { */ @Test public void testProjectionThroughAssociation() throws DataStoreException { + createFeaturesWithAssociation(); final FilterFactory<Feature,?,?> ff = DefaultFilterFactory.forFeatures(); query.setProjection(new FeatureQuery.NamedExpression(ff.property("value1"), (String) null), new FeatureQuery.NamedExpression(ff.property("dependency/value3"), "value3")); @@ -331,27 +362,16 @@ public final class FeatureQueryTest extends TestCase { /** * Tests {@link FeatureQuery#setProjection(FeatureQuery.NamedExpression...)} on a field - * which is a link, ensure the link name is preserved. + * which is a link, ensuring that the link name is preserved. * * @throws DataStoreException if an error occurred while executing the query. */ @Test public void testProjectionOfLink() throws DataStoreException { - - final FeatureTypeBuilder ftb = new FeatureTypeBuilder(); - ftb.setName("test"); - ftb.addAttribute(String.class).setName("id").addRole(AttributeRole.IDENTIFIER_COMPONENT); - FeatureType ft = ftb.build(); - - Feature feature = ft.newInstance(); - feature.setPropertyValue("id", "id-0"); - - final FeatureQuery query = new FeatureQuery(); + createFeatureWithIdentifier(); query.setProjection(AttributeConvention.IDENTIFIER); - - final FeatureSet fs = new MemoryFeatureSet(null, ft, List.of(feature)); - Feature r = fs.subset(query).features(true).iterator().next(); - assertEquals("id-0", r.getPropertyValue(AttributeConvention.IDENTIFIER)); + final Feature instance = executeAndGetFirst(); + assertEquals("id-0", instance.getPropertyValue(AttributeConvention.IDENTIFIER)); } /** @@ -368,6 +388,7 @@ public final class FeatureQueryTest extends TestCase { */ @Test public void testVirtualProjection() throws DataStoreException { + createFeaturesWithAssociation(); final FilterFactory<Feature,?,?> ff = DefaultFilterFactory.forFeatures(); query.setProjection( new FeatureQuery.NamedExpression(ff.property("value1", Integer.class), (String) null), @@ -411,6 +432,7 @@ public final class FeatureQueryTest extends TestCase { */ @Test public void testIncorrectVirtualProjection() throws DataStoreException { + createFeaturesWithAssociation(); final FilterFactory<Feature,?,?> ff = DefaultFilterFactory.forFeatures(); query.setProjection(new FeatureQuery.NamedExpression(ff.property("value1", Integer.class), (String) null), virtualProjection(ff.property("valueMissing", Integer.class), "renamed1"));