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 428ee02c5e942943502fce6cb935ca737761bbc1
Author: Martin Desruisseaux <martin.desruisse...@geomatys.com>
AuthorDate: Mon May 5 17:07:38 2025 +0200

    Add a `PropertyTypeBuilder.replaceBy(…)` method.
---
 .../sis/feature/builder/AttributeTypeBuilder.java  | 14 +++++-----
 .../sis/feature/builder/FeatureTypeBuilder.java    | 10 +++++--
 .../sis/feature/builder/PropertyTypeBuilder.java   | 32 ++++++++++++++++++++--
 .../org/apache/sis/feature/internal/Resources.java |  5 ++++
 .../sis/feature/internal/Resources.properties      |  1 +
 .../sis/feature/internal/Resources_fr.properties   |  1 +
 .../feature/builder/FeatureTypeBuilderTest.java    | 29 ++++++++++++++++++++
 7 files changed, 79 insertions(+), 13 deletions(-)

diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/builder/AttributeTypeBuilder.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/builder/AttributeTypeBuilder.java
index 9a572459ef..ece724e014 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/builder/AttributeTypeBuilder.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/builder/AttributeTypeBuilder.java
@@ -64,7 +64,7 @@ import org.opengis.feature.AttributeType;
  *
  * @author  Johann Sorel (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.8
+ * @version 1.5
  *
  * @param <V>  the class of attribute values.
  *
@@ -294,11 +294,11 @@ public final class AttributeTypeBuilder<V> extends 
PropertyTypeBuilder {
         if (type == valueClass) {
             return (AttributeTypeBuilder<N>) this;
         }
-        final AttributeTypeBuilder<N> newb = new AttributeTypeBuilder<>(this, 
type);
+        final var newb = new AttributeTypeBuilder<N>(this, type);
         for (final CharacteristicTypeBuilder<?> c : characteristics) {
             c.owner(newb);
         }
-        owner.replace(this, newb);
+        owner.replace(this, newb, true);
         dispose();
         return newb;
     }
@@ -343,7 +343,7 @@ public final class AttributeTypeBuilder<V> extends 
PropertyTypeBuilder {
         final V[] values = (V[]) Array.newInstance(valueClass, c.size());
         int index = 0;
         for (final Object value : c) {
-            values[index++] = (V) value;        // ArrayStoreException if 
'value' is not the expected type.
+            values[index++] = (V) value;        // ArrayStoreException if 
`value` is not the expected type.
         }
         return values;
     }
@@ -589,14 +589,14 @@ public final class AttributeTypeBuilder<V> extends 
PropertyTypeBuilder {
                             next = AttributeRole.IDENTIFIER_COMPONENT;
                             break;
                         }
-                        index++;        // Fall through for testing the case 
for next 'index' value.
+                        index++;        // Fall through for testing the case 
for next `index` value.
                     }
                     case 1: {
                         if (owner().defaultGeometry == 
AttributeTypeBuilder.this) {
                             next = AttributeRole.DEFAULT_GEOMETRY;
                             break;
                         }
-                        index++;        // Fall through for testing the case 
for next 'index' value.
+                        index++;        // Fall through for testing the case 
for next `index` value.
                     }
                     default: {
                         return false;
@@ -718,7 +718,7 @@ public final class AttributeTypeBuilder<V> extends 
PropertyTypeBuilder {
     public void remove() {
         if (isIdentifier) {
             isIdentifier = false;
-            owner().identifierCount--;      // Owner should never be null 
since we set 'isIdentifier' to false.
+            owner().identifierCount--;      // Owner should never be null 
since we set `isIdentifier` to false.
         }
         super.remove();
     }
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/builder/FeatureTypeBuilder.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/builder/FeatureTypeBuilder.java
index 22287ece14..a11ddf1654 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/builder/FeatureTypeBuilder.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/builder/FeatureTypeBuilder.java
@@ -1001,11 +1001,15 @@ public class FeatureTypeBuilder extends TypeBuilder {
     /**
      * Replaces the given builder instance by a new instance, or delete the 
old instance.
      * This builder should contain exactly one instance of the given {@code 
old} builder.
+     * The {@code metadata} argument should generally be {@code true}, except 
when this
+     * method is invoked for removing a property only temporarily, e.g. before 
to move
+     * it to another location.
      *
      * @param old          the instance to replace.
      * @param replacement  the replacement, or {@code null} for deleting the 
old instance.
+     * @param metadata     whether to update metadata such as {@link 
#defaultGeometry}.
      */
-    final void replace(final PropertyTypeBuilder old, final 
PropertyTypeBuilder replacement) {
+    final void replace(final PropertyTypeBuilder old, final 
PropertyTypeBuilder replacement, final boolean metadata) {
         final int index = properties.lastIndexOf(old);
         if (index < 0 || (replacement != null ? properties.set(index, 
replacement) : properties.remove(index)) != old) {
             /*
@@ -1014,8 +1018,8 @@ public class FeatureTypeBuilder extends TypeBuilder {
              */
             throw new CorruptedObjectException();
         }
-        if (old == defaultGeometry) {
-            defaultGeometry = (AttributeTypeBuilder<?>) replacement;
+        if (metadata && old == defaultGeometry) {
+            defaultGeometry = (replacement instanceof AttributeTypeBuilder<?>) 
? (AttributeTypeBuilder<?>) replacement : null;
         }
         clearCache();
     }
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/builder/PropertyTypeBuilder.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/builder/PropertyTypeBuilder.java
index 0b4c088e8d..2c9405313e 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/builder/PropertyTypeBuilder.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/builder/PropertyTypeBuilder.java
@@ -18,6 +18,7 @@ package org.apache.sis.feature.builder;
 
 import org.opengis.util.GenericName;
 import org.apache.sis.util.resources.Errors;
+import org.apache.sis.feature.internal.Resources;
 
 // Specific to the geoapi-3.1 and geoapi-4.0 branches:
 import org.opengis.feature.AttributeType;
@@ -41,7 +42,7 @@ import org.opengis.feature.FeatureAssociationRole;
  *
  * @author  Johann Sorel (Geomatys)
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.0
+ * @version 1.5
  * @since   0.8
  */
 public abstract class PropertyTypeBuilder extends TypeBuilder {
@@ -80,7 +81,7 @@ public abstract class PropertyTypeBuilder extends TypeBuilder 
{
         owner         = builder.owner;
         minimumOccurs = builder.minimumOccurs;
         maximumOccurs = builder.maximumOccurs;
-        // Do not copy the 'property' reference since the 'valueClass' is 
different.
+        // Do not copy the `property` reference since the `valueClass` is 
different.
     }
 
     /**
@@ -312,8 +313,33 @@ public abstract class PropertyTypeBuilder extends 
TypeBuilder {
     @Override
     public void remove() {
         if (owner != null) {
-            owner.replace(this, null);
+            owner.replace(this, null, true);
             dispose();
         }
     }
+
+    /**
+     * Removes this property and moves the given replacement to the location 
previously occupied by this property.
+     * The replacement must have been built by the same {@link 
FeatureTypeBuilder} than this property.
+     * If the replacement is {@code null}, then this method is equivalent to 
{@link #remove()}.
+     *
+     * @param  replacement  the property to move to the location of this 
property, or {@code null} if none.
+     * @throws IllegalArgumentException if the replacement has not been built 
by the same {@link FeatureTypeBuilder}
+     *         than this property.
+     *
+     * @since 1.5
+     */
+    public void replaceBy(final PropertyTypeBuilder replacement) {
+        @SuppressWarnings("LocalVariableHidesMemberVariable")
+        final FeatureTypeBuilder owner = owner();
+        if (replacement != null) {
+            if (replacement.owner() != owner) {
+                throw new 
IllegalArgumentException(resources().getString(Resources.Keys.MismatchedParentFeature));
+            }
+            owner.replace(replacement, null, false);
+        }
+        owner.replace(this, replacement, true);
+        dispose();
+        remove();   // For subclasses that override this method.
+    }
 }
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/internal/Resources.java
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/internal/Resources.java
index c3ad59f00a..b887eda202 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/internal/Resources.java
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/internal/Resources.java
@@ -315,6 +315,11 @@ public class Resources extends IndexedResourceBundle {
          */
         public static final short MismatchedImageSize_3 = 44;
 
+        /**
+         * The two properties are not members of the same feature.
+         */
+        public static final short MismatchedParentFeature = 92;
+
         /**
          * Mismatched type for “{0}” property.
          */
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/internal/Resources.properties
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/internal/Resources.properties
index 1aeaa4f3bf..2dd17c3f46 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/internal/Resources.properties
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/internal/Resources.properties
@@ -70,6 +70,7 @@ MismatchedDataType                = The bands store sample 
values using differen
 MismatchedGeometryLibrary_2       = Expected a geometry from {0} library but 
got {1}.
 MismatchedImageLocation           = The two images have different size or 
pixel coordinates.
 MismatchedImageSize_3             = Image {0,choice,0#width|1#height} 
({1,number} pixels) does not match the grid extent size ({2,number} cells).
+MismatchedParentFeature           = The two properties are not members of the 
same feature.
 MismatchedPropertyType_1          = Mismatched type for \u201c{0}\u201d 
property.
 MismatchedSampleModel             = The two images use different sample models.
 MismatchedTileGrid                = The two images have different tile grid.
diff --git 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/internal/Resources_fr.properties
 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/internal/Resources_fr.properties
index b2b4f45237..13996cde79 100644
--- 
a/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/internal/Resources_fr.properties
+++ 
b/endorsed/src/org.apache.sis.feature/main/org/apache/sis/feature/internal/Resources_fr.properties
@@ -75,6 +75,7 @@ MismatchedDataType                = Les bandes stockent leurs 
valeurs en utilisa
 MismatchedGeometryLibrary_2       = Une g\u00e9om\u00e9tries {0} \u00e9tait 
attendue mais l\u2019objet re\u00e7u est de {1}.
 MismatchedImageLocation           = Les deux images ont une taille ou des 
coordonn\u00e9es pixels diff\u00e9rentes.
 MismatchedImageSize_3             = La {0,choice,0#largeur|1#hauteur} de 
l\u2019image ({1,number} pixels) ne correspond pas \u00e0 l\u2019\u00e9tendue 
de la grille ({2,number} cellules).
+MismatchedParentFeature           = Les deux propri\u00e9t\u00e9s ne sont pas 
membres de la m\u00eame entit\u00e9.
 MismatchedPropertyType_1          = Le type de la propri\u00e9t\u00e9 
\u00ab\u202f{0}\u202f\u00bb ne correspond pas.
 MismatchedSampleModel             = Les deux images disposent les pixels 
diff\u00e9remment.
 MismatchedTileGrid                = Les deux images utilisent des grilles de 
tuiles diff\u00e9rentes.
diff --git 
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/feature/builder/FeatureTypeBuilderTest.java
 
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/feature/builder/FeatureTypeBuilderTest.java
index 3e4f9bb389..42cd33194a 100644
--- 
a/endorsed/src/org.apache.sis.feature/test/org/apache/sis/feature/builder/FeatureTypeBuilderTest.java
+++ 
b/endorsed/src/org.apache.sis.feature/test/org/apache/sis/feature/builder/FeatureTypeBuilderTest.java
@@ -259,6 +259,35 @@ public final class FeatureTypeBuilderTest extends TestCase 
{
         assertEquals(Integer.class, a2.getValueClass());
     }
 
+    /**
+     * Tests {@link PropertyTypeBuilder#replaceBy(PropertyTypeBuilder)}.
+     */
+    @Test
+    public void testReplace() {
+        final var builder = new FeatureTypeBuilder();
+        assertSame(builder, builder.setName("City"));
+        final AttributeTypeBuilder<?> toReplace, replacement;
+        /* unmodified */ builder.addAttribute(String.class).setName("name");
+        toReplace      = builder.addAttribute(String.class).setName("someKey");
+        /* unmodified */ 
builder.addAttribute(Integer.class).setName("population");
+        replacement    = builder.addAttribute(Integer.class).setName("someId");
+
+        var it = builder.build().getProperties(true).iterator();
+        assertPropertyEquals("name",       String.class,  it.next());
+        assertPropertyEquals("someKey",    String.class,  it.next());
+        assertPropertyEquals("population", Integer.class, it.next());
+        assertPropertyEquals("someId",     Integer.class, it.next());
+        assertFalse(it.hasNext());
+
+        toReplace.replaceBy(replacement);
+        assertMessageContains(assertThrows(IllegalStateException.class, () -> 
toReplace.setName("Foo")));
+        it = builder.build().getProperties(true).iterator();
+        assertPropertyEquals("name",       String.class,  it.next());
+        assertPropertyEquals("someId",     Integer.class, it.next());
+        assertPropertyEquals("population", Integer.class, it.next());
+        assertFalse(it.hasNext());
+    }
+
     /**
      * Tests creation of a builder from an existing feature type.
      * This method also acts as a test of {@code FeatureTypeBuilder} getter 
methods.

Reply via email to