sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] branch geoapi-4.0 updated: Refactor MetadataCopier on top of MetadataVisitor.
Date Mon, 02 Jul 2018 14:58:46 GMT
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


The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
     new 20c4fcb  Refactor MetadataCopier on top of MetadataVisitor.
20c4fcb is described below

commit 20c4fcb0e78e89991e243458f3e15103403a871a
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Mon Jul 2 15:48:59 2018 +0200

    Refactor MetadataCopier on top of MetadataVisitor.
---
 .../java/org/apache/sis/metadata/HashCode.java     | 10 +--
 .../org/apache/sis/metadata/MetadataCopier.java    | 88 ++++++++++++++------
 .../org/apache/sis/metadata/MetadataVisitor.java   | 95 ++++++++++++++++++----
 .../apache/sis/metadata/ModifiableMetadata.java    | 25 +++---
 .../org/apache/sis/metadata/PropertyAccessor.java  | 63 +++++---------
 .../main/java/org/apache/sis/metadata/Pruner.java  | 13 ++-
 .../java/org/apache/sis/metadata/StateChanger.java |  8 +-
 7 files changed, 188 insertions(+), 114 deletions(-)

diff --git a/core/sis-metadata/src/main/java/org/apache/sis/metadata/HashCode.java b/core/sis-metadata/src/main/java/org/apache/sis/metadata/HashCode.java
index 161e814..8aca3df 100644
--- a/core/sis-metadata/src/main/java/org/apache/sis/metadata/HashCode.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/metadata/HashCode.java
@@ -64,13 +64,13 @@ final class HashCode extends MetadataVisitor<Integer> {
      * Resets the hash code to an initial value for a new metadata instance.
      * If another hash code computation was in progress, that code shall be saved before
this method is invoked.
      *
-     * @param  type  the standard interface of the metadata for which a hash code value will
be computed.
-     * @return {@code false} since this visitor is not restricted to writable properties.
+     * @param  accessor  contains the standard interface of the metadata for which a hash
code value will be computed.
+     * @return {@link Filter#NON_EMPTY} since this visitor is not restricted to writable
properties.
      */
     @Override
-    boolean preVisit(final Class<?> type) {
-        code = type.hashCode();
-        return false;
+    Filter preVisit(final PropertyAccessor accessor) {
+        code = accessor.type.hashCode();
+        return Filter.NON_EMPTY;
     }
 
     /**
diff --git a/core/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataCopier.java b/core/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataCopier.java
index 88da1c2..329a6e9 100644
--- a/core/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataCopier.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataCopier.java
@@ -21,7 +21,7 @@ import java.util.EnumSet;
 import java.util.LinkedHashSet;
 import java.util.Map;
 import java.util.LinkedHashMap;
-import java.util.IdentityHashMap;
+import java.util.List;
 import java.util.Arrays;
 import java.util.Collection;
 import java.lang.reflect.Constructor;
@@ -63,7 +63,7 @@ import org.apache.sis.util.collection.CodeListSet;
  * @since   0.8
  * @module
 */
-public class MetadataCopier {
+public class MetadataCopier extends MetadataVisitor<Object> {
     /**
      * The default metadata standard to use for object that are not {@link AbstractMetadata}
instances,
      * or {@code null} if none.
@@ -71,10 +71,16 @@ public class MetadataCopier {
     private final MetadataStandard standard;
 
     /**
-     * The metadata objects that have been copied so far.
-     * This is used for resolving cyclic graphs.
+     * The current metadata instance where to copy the property values.
      */
-    final Map<Object,Object> copies;
+    private Object target;
+
+    /**
+     * Creates a new {@code MetadataCopier} instance.
+     */
+    private MetadataCopier() {
+        standard = null;
+    }
 
     /**
      * Creates a new metadata copier.
@@ -84,7 +90,6 @@ public class MetadataCopier {
      */
     public MetadataCopier(final MetadataStandard standard) {
         this.standard = standard;
-        copies = new IdentityHashMap<>();
     }
 
     /**
@@ -123,11 +128,7 @@ public class MetadataCopier {
      *         or an implementation class does not provide a public default constructor.
      */
     public Object copy(final Object metadata) {
-        try {
-            return copyRecursively(null, metadata);
-        } finally {
-            copies.clear();
-        }
+        return copyRecursively(null, metadata);
     }
 
     /**
@@ -143,11 +144,7 @@ public class MetadataCopier {
      */
     public <T> T copy(final Class<T> type, final T metadata) {
         ArgumentChecks.ensureNonNull("type", type);
-        try {
-            return type.cast(copyRecursively(type, metadata));
-        } finally {
-            copies.clear();
-        }
+        return type.cast(copyRecursively(type, metadata));
     }
 
     /**
@@ -171,12 +168,9 @@ public class MetadataCopier {
                 std = ((AbstractMetadata) metadata).getStandard();
             }
             if (std != null) {
-                final PropertyAccessor accessor = std.getAccessor(new CacheKey(metadata.getClass(),
type), false);
-                if (accessor != null) try {
-                    return accessor.copy(metadata, this);
-                } catch (ReflectiveOperationException e) {
-                    throw new UnsupportedOperationException(Errors.format(Errors.Keys.CanNotCopy_1,
accessor.type),
-                            Exceptions.unwrap(e));
+                final Object result = walk(std, type, metadata, false);
+                if (result != null) {
+                    return result;
                 }
             }
         }
@@ -184,11 +178,39 @@ public class MetadataCopier {
     }
 
     /**
+     * Invoked before the properties of a metadata instance are visited. This method creates
a new instance,
+     * to be returned by {@link #result()}, and returns {@link Filter#WRITABLE_RESULT} for
notifying the caller
+     * that write operations need to be performed on that {@code result} object.
+     */
+    @Override
+    final Filter preVisit(final PropertyAccessor accessor) {
+        if (accessor.isWritable()) try {
+            target = accessor.implementation.getConstructor().newInstance();
+            return Filter.WRITABLE_RESULT;
+        } catch (ReflectiveOperationException e) {
+            throw new UnsupportedOperationException(Errors.format(Errors.Keys.CanNotCopy_1,
accessor.type), Exceptions.unwrap(e));
+        } else {
+            target = null;
+            return Filter.NONE;
+        }
+    }
+
+    /**
+     * Returns the metadata instance resulting from the copy. This method is invoked <strong>before</strong>
+     * metadata properties are visited.
+     */
+    @Override
+    final Object result() {
+        return target;
+    }
+
+    /**
      * Verifies if the given metadata value is a map or a collection before to invoke
      * {@link #copyRecursively(Class, Object)} for metadata elements.  This method is
-     * invoked by {@link PropertyAccessor#copy(Object, MetadataCopier)}.
+     * invoked by {@link PropertyAccessor#walkWritable(MetadataVisitor, Object, Object)}.
      */
-    final Object copyAny(final Class<?> type, final Object metadata) {
+    @Override
+    final Object visit(final Class<?> type, final Object metadata) {
         if (!type.isInstance(metadata)) {
             if (metadata instanceof Collection<?>) {
                 Collection<?> c = (Collection<?>) metadata;
@@ -222,4 +244,22 @@ public class MetadataCopier {
         }
         return copyRecursively(type, metadata);
     }
+
+    /**
+     * Returns the path to the currently copied property.
+     * Each element in the list is the UML identifier of a property.
+     * Element at index 0 is the name of the property of the root metadata object being copied.
+     * Element at index 1 is the name of a property which is a children of above property,
<i>etc.</i>
+     *
+     * <p>The returned list is valid only during {@link #copyRecursively(Class, Object)}
method execution.
+     * The content of this list become undetermined after the {@code copyRecursively} method
returned.</p>
+     *
+     * @return the path to the currently copied property.
+     *
+     * @since 1.0
+     */
+    @Override
+    protected List<String> getCurrentPropertyPath() {
+        return super.getCurrentPropertyPath();
+    }
 }
diff --git a/core/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataVisitor.java
b/core/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataVisitor.java
index e3e5225..b64e160 100644
--- a/core/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataVisitor.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/metadata/MetadataVisitor.java
@@ -17,10 +17,12 @@
 package org.apache.sis.metadata;
 
 import java.util.Arrays;
+import java.util.List;
 import java.util.Map;
 import java.util.IdentityHashMap;
 import java.util.ConcurrentModificationException;
 import org.apache.sis.internal.system.Semaphores;
+import org.apache.sis.internal.util.UnmodifiableArrayList;
 
 
 /**
@@ -89,7 +91,7 @@ abstract class MetadataVisitor<R> {
     /**
      * Creates a new visitor.
      */
-    protected MetadataVisitor() {
+    MetadataVisitor() {
         visited = new IdentityHashMap<>();
         propertyPath = new String[6];
     }
@@ -97,8 +99,11 @@ abstract class MetadataVisitor<R> {
     /**
      * The thread-local variable that created this {@code MetadataVisitor} instance.
      * This is usually a static final {@code VISITORS} constant defined in the subclass.
+     * May be {@code null} if this visitor does not use thread-local instances.
      */
-    abstract ThreadLocal<? extends MetadataVisitor<?>> creator();
+    ThreadLocal<? extends MetadataVisitor<?>> creator() {
+        return null;
+    }
 
     /**
      * Sets the name of the method being visited. This is invoked by {@code PropertyAccessor.walk}
methods only.
@@ -108,6 +113,21 @@ abstract class MetadataVisitor<R> {
     }
 
     /**
+     * Returns the path to the currently visited property.
+     * Each element in the list is the UML identifier of a property.
+     * Element at index 0 is the name of the property of the root metadata object being visited.
+     * Element at index 1 is the name of a property which is a children of above property,
<i>etc.</i>
+     *
+     * <p>The returned list is valid only during {@link #visit(Class, Object)} method
execution.
+     * The content of this list become undetermined after the {@code visit} method returned.</p>
+     *
+     * @return the path to the currently visited property.
+     */
+    List<String> getCurrentPropertyPath() {
+        return UnmodifiableArrayList.wrap(propertyPath, 0, nestedCount);
+    }
+
+    /**
      * Invokes {@link #visit(Class, Object)} for all elements of the given metadata if that
metadata has not
      * already been visited. The computation result is returned (may be the result of a previous
computation).
      *
@@ -127,8 +147,15 @@ abstract class MetadataVisitor<R> {
         if (!visited.containsKey(metadata)) {               // Reminder: the associated value
may be null.
             final PropertyAccessor accessor = standard.getAccessor(new CacheKey(metadata.getClass(),
type), mandatory);
             if (accessor != null) {
-                final boolean write = preVisit(accessor.type);
-                if (visited.put(metadata, null) != null) {
+                final Filter filter = preVisit(accessor);
+                final boolean preconstructed;
+                final R sentinel;
+                switch (filter) {
+                    case NONE:            return null;
+                    case WRITABLE_RESULT: preconstructed = true;  sentinel = result(); break;
+                    default:              preconstructed = false; sentinel = null;     break;
+                }
+                if (visited.put(metadata, sentinel) != null) {
                     // Should never happen, unless this method is invoked concurrently in
another thread.
                     throw new ConcurrentModificationException();
                 }
@@ -146,10 +173,10 @@ abstract class MetadataVisitor<R> {
                     allowNull = Semaphores.queryAndSet(Semaphores.NULL_COLLECTION);
                 }
                 try {
-                    if (write) {
-                        accessor.walkWritable(this, metadata);
-                    } else {
-                        accessor.walkReadable(this, metadata);
+                    switch (filter) {
+                        case NON_EMPTY:       accessor.walkReadable(this, metadata); break;
+                        case WRITABLE:        accessor.walkWritable(this, metadata, metadata);
break;
+                        case WRITABLE_RESULT: accessor.walkWritable(this, metadata, sentinel);
break;
                     }
                 } catch (MetadataVisitorException e) {
                     throw e;
@@ -160,11 +187,12 @@ abstract class MetadataVisitor<R> {
                         if (!allowNull) {
                             Semaphores.clear(Semaphores.NULL_COLLECTION);
                         }
-                        creator().remove();
+                        final ThreadLocal<? extends MetadataVisitor<?>> creator
= creator();
+                        if (creator != null) creator.remove();
                     }
                 }
-                final R result = result();
-                if (visited.put(metadata, result) != null) {
+                final R result = preconstructed ? sentinel : result();
+                if (visited.put(metadata, result) != sentinel) {
                     throw new ConcurrentModificationException();
                 }
                 return result;
@@ -174,15 +202,43 @@ abstract class MetadataVisitor<R> {
     }
 
     /**
+     * Filter the properties to visit. A value of this enumeration is returned by {@link
#preVisit(PropertyAccessor)}
+     * before the properties of a metadata instance are visited.
+     */
+    enum Filter {
+        /**
+         * Do not visit any property (skip completely the metadata).
+         */
+        NONE,
+
+        /**
+         * Visit all non-null and non-empty standard properties.
+         */
+        NON_EMPTY,
+
+        /**
+         * Visit all writable properties. May include some non-standard properties.
+         */
+        WRITABLE,
+
+        /**
+         * Same as {@link #WRITABLE}, but write properties in the object returned by {@link
#result()}.
+         * This mode implies that {@code result()} is invoked <strong>before</strong>
metadata properties
+         * are visited instead than after.
+         */
+        WRITABLE_RESULT
+    }
+
+    /**
      * Invoked when a new metadata is about to be visited. After this method has been invoked,
      * {@link #visit(Class, Object)} will be invoked for each property in the metadata object.
      *
-     * @param  type  the standard interface implemented by the metadata instance being visited.
-     * @return {@code true} for visiting only writable properties, or
-     *         {@code false} for visiting all readable properties.
+     * @param  accessor  information about the standard interface and implementation of the
metadata being visited.
+     * @return most common values are {@code NON_EMPTY} for visiting all non-empty properties
(the default),
+     *         or {@code WRITABLE} for visiting only writable properties.
      */
-    boolean preVisit(Class<?> type) {
-        return false;
+    Filter preVisit(PropertyAccessor accessor) {
+        return Filter.NON_EMPTY;
     }
 
     /**
@@ -210,8 +266,11 @@ abstract class MetadataVisitor<R> {
 
     /**
      * Returns the result of visiting all elements in a metadata instance.
-     * This method is invoked after all metadata properties have been visited,
-     * or after a {@link #visit(Class, Object)} method call returned {@link #SKIP_SIBLINGS}.
+     * This method is invoked exactly once per metadata instance.
+     * It is usually invoked after all metadata properties have been visited
+     * (or after a {@link #visit(Class, Object)} method call returned {@link #SKIP_SIBLINGS}),
+     * unless {@link #preVisit(PropertyAccessor)} returned {@link Filter#WRITABLE_RESULT}
+     * in which case this method is invoked <strong>before</strong> metadata
properties are visited.
      * The value returned by this method will be cached in case the same metadata instance
is revisited again.
      */
     abstract R result();
diff --git a/core/sis-metadata/src/main/java/org/apache/sis/metadata/ModifiableMetadata.java
b/core/sis-metadata/src/main/java/org/apache/sis/metadata/ModifiableMetadata.java
index 039c427..dc5799c 100644
--- a/core/sis-metadata/src/main/java/org/apache/sis/metadata/ModifiableMetadata.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/metadata/ModifiableMetadata.java
@@ -38,15 +38,20 @@ import static org.apache.sis.util.collection.Containers.isNullOrEmpty;
 
 
 /**
- * Provides convenience methods for support of modifiable properties in metadata implementations.
- * Implementations typically provide {@code set*(…)} methods for each corresponding {@code
get*()}
- * method. Subclasses can follow the pattern below for every {@code get} and {@code set}
methods,
- * with a different processing for singleton value or for {@linkplain Collection collections}.
+ * Base class of metadata having an editable content.
+ * Newly created {@code ModifiableMetadata} are initially in {@linkplain State#EDITABLE editable}
state.
+ * The metadata can be populated using the setter methods provided by subclasses, then transition
to the
+ * {@linkplain State#FINAL final} state for making it safe to share by many consumers.
  *
- * <p>For singleton value:</p>
+ * <div class="section">Tip for subclass implementations</div>
+ * Subclasses can follow the pattern below for every {@code get} and {@code set} methods,
+ * with a different processing for singleton value or for {@linkplain Collection collections}.
  *
  * {@preformat java
  *     public class MyMetadata {
+ *
+ *         // ==== Example for a singleton value =============================
+ *
  *         private Foo property;
  *
  *         public Foo getProperty() {
@@ -57,13 +62,9 @@ import static org.apache.sis.util.collection.Containers.isNullOrEmpty;
  *             checkWritePermission();
  *             property = newValue;
  *         }
- *     }
- * }
  *
- * For collections (note that the call to {@link #checkWritePermission()} is implicit):
+ *         // ==== Example for a collection ==================================
  *
- * {@preformat java
- *     public class MyMetadata {
  *         private Collection<Foo> properties;
  *
  *         public Collection<Foo> getProperties() {
@@ -71,14 +72,12 @@ import static org.apache.sis.util.collection.Containers.isNullOrEmpty;
  *         }
  *
  *         public void setProperties(Collection<Foo> newValues) {
+ *             // the call to checkWritePermission() is implicit
  *             properties = writeCollection(newValues, properties, Foo.class);
  *         }
  *     }
  * }
  *
- * An initially modifiable metadata may become unmodifiable at a later stage
- * (typically after its construction is completed) by the call to {@code apply(State.FINAL)}.
- *
  * @author  Martin Desruisseaux (Geomatys)
  * @version 1.0
  * @since   0.3
diff --git a/core/sis-metadata/src/main/java/org/apache/sis/metadata/PropertyAccessor.java
b/core/sis-metadata/src/main/java/org/apache/sis/metadata/PropertyAccessor.java
index b2d67f0..76f0849 100644
--- a/core/sis-metadata/src/main/java/org/apache/sis/metadata/PropertyAccessor.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/metadata/PropertyAccessor.java
@@ -64,7 +64,7 @@ import static org.apache.sis.util.collection.Containers.hashMapCapacity;
  * <ul>
  *   <li>The standard properties defined by the GeoAPI (or other standard) interfaces.
  *       Those properties are the only ones accessible by most methods in this class, except
- *       {@link #equals(Object, Object, ComparisonMode)} and {@link #walkWritable(MetadataVisitor,
Object)}.</li>
+ *       {@link #equals(Object, Object, ComparisonMode)} and {@link #walkWritable(MetadataVisitor,
Object, Object)}.</li>
  *
  *   <li>Extra properties defined by the {@link IdentifiedObject} interface. Those
properties
  *       invisible in the ISO 19115-1 model, but appears in ISO 19115-3 XML marshalling.
So we
@@ -667,6 +667,13 @@ class PropertyAccessor {
     }
 
     /**
+     * Returns {@code true} if the {@link #implementation} class has at least one setter
method.
+     */
+    final boolean isWritable() {
+        return setters != null;
+    }
+
+    /**
      * Returns {@code true} if the property at the given index is writable.
      */
     final boolean isWritable(final int index) {
@@ -1185,41 +1192,6 @@ class PropertyAccessor {
     }
 
     /**
-     * Returns a potentially deep copy of the given metadata object.
-     *
-     * @param  metadata   the metadata object to copy.
-     * @param  copier     contains a map of metadata objects already copied.
-     * @return a copy of the given metadata object, or {@code metadata} itself if there is
-     *         no known implementation class or that implementation has no setter method.
-     * @throws ReflectiveOperationException if an error occurred while creating the copy.
-     */
-    final Object copy(final Object metadata, final MetadataCopier copier) throws ReflectiveOperationException
{
-        if (setters == null) {
-            return metadata;
-        }
-        Object copy = copier.copies.get(metadata);
-        if (copy == null) {
-            copy = implementation.getConstructor().newInstance();
-            copier.copies.put(metadata, copy);              // Need to be first in case of
cyclic graphs.
-            final Object[] arguments = new Object[1];
-            for (int i=0; i<allCount; i++) {
-                final Method setter = setters[i];
-                if (setter != null && !setter.isAnnotationPresent(Deprecated.class))
{
-                    Object value = get(getters[i], metadata);
-                    if (value != null) {
-                        value = copier.copyAny(elementTypes[i], value);
-                        if (value != null) {
-                            arguments[0] = value;
-                            set(setter, copy, arguments);
-                        }
-                    }
-                }
-            }
-        }
-        return copy;
-    }
-
-    /**
      * Invokes {@link MetadataVisitor#visit(Class, Object)} for all non-null properties in
the given metadata.
      * This method is not recursive, i.e. it does not traverse the children of the elements
in the given metadata.
      *
@@ -1246,13 +1218,18 @@ class PropertyAccessor {
      * Invokes {@link MetadataVisitor#visit(Class, Object)} for all writable properties in
the given metadata.
      * This method is not recursive, i.e. it does not traverse the children of the elements
in the given metadata.
      *
+     * <p><b>Constraint:</b> in current implementation, if {@code source}
and {@code target} are not the same,
+     * then {@code target} is assumed empty. The intent is to skip easily null or empty properties.</p>
+     *
      * @param  visitor   the object on which to invoke {@link MetadataVisitor#visit(Class,
Object)}.
-     * @param  metadata  the metadata instance for which to visit the writable properties.
+     * @param  source    the metadata from which to read properties. May be the same than
{@code target}.
+     * @param  target    the metadata instance where to write properties.
      * @throws Exception if an error occurred while visiting a property.
      */
-    final void walkWritable(final MetadataVisitor<?> visitor, final Object metadata)
throws Exception {
-        assert type.isInstance(metadata) : metadata;
-        if (setters == null || !implementation.isInstance(metadata)) {
+    final void walkWritable(final MetadataVisitor<?> visitor, final Object source,
final Object target) throws Exception {
+        assert type.isInstance(source) : source;
+        assert type.isInstance(target) : target;
+        if (setters == null || !implementation.isInstance(target)) {
             return;
         }
         final Object[] arguments = new Object[1];
@@ -1273,12 +1250,12 @@ class PropertyAccessor {
                      */
                     continue;
                 }
-                final Object value = get(getters[i], metadata);
+                final Object value = get(getters[i], source);
                 final Object result = visitor.visit(elementTypes[i], value);
-                if (result != value) {
+                if (source == target ? (result != value) : !isNullOrEmpty(result)) {    //
See "constraint" in Javadoc
                     if (result == MetadataVisitor.SKIP_SIBLINGS) break;
                     arguments[0] = result;
-                    set(setter, metadata, arguments);
+                    set(setter, target, arguments);
                     /*
                      * We invoke the set(…) method variant that do not perform type conversion
                      * because we do not want it to replace the immutable collections created
diff --git a/core/sis-metadata/src/main/java/org/apache/sis/metadata/Pruner.java b/core/sis-metadata/src/main/java/org/apache/sis/metadata/Pruner.java
index f29ed4d..b2fbc85 100644
--- a/core/sis-metadata/src/main/java/org/apache/sis/metadata/Pruner.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/metadata/Pruner.java
@@ -90,17 +90,16 @@ final class Pruner extends MetadataVisitor<Boolean> {
 
     /**
      * Marks a metadata instance as empty before we start visiting its non-null properties.
-     * If the metadata does not contain any property, then the {@link #isEmpty} field will
-     * stay {@code true}.
+     * If the metadata does not contain any property, then the {@link #isEmpty} field will
stay {@code true}.
      *
-     * @return {@code false} since this visitor is not restricted to writable properties.
-     *         We need to visit all readable properties even for pruning operation since
-     *         we need to determine if the metadata is empty.
+     * @return {@link Filter#NON_EMPTY} since this visitor is not restricted to writable
properties.
+     *         We need to visit all readable properties even for pruning operation since
we need to
+     *         determine if the metadata is empty.
      */
     @Override
-    boolean preVisit(final Class<?> type) {
+    Filter preVisit(final PropertyAccessor accessor) {
         isEmpty = true;
-        return false;
+        return Filter.NON_EMPTY;
     }
 
     /**
diff --git a/core/sis-metadata/src/main/java/org/apache/sis/metadata/StateChanger.java b/core/sis-metadata/src/main/java/org/apache/sis/metadata/StateChanger.java
index 18381f5..8c25e3a 100644
--- a/core/sis-metadata/src/main/java/org/apache/sis/metadata/StateChanger.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/metadata/StateChanger.java
@@ -95,12 +95,12 @@ final class StateChanger extends MetadataVisitor<Boolean> {
     /**
      * Notifies {@link MetadataVisitor} that we want to visit all writable properties.
      *
-     * @param  type  ignored.
-     * @return {@code true}, for iterating over all writable properties.
+     * @param  accessor  ignored.
+     * @return {@link Filter#WRITABLE}, for iterating over all writable properties.
      */
     @Override
-    boolean preVisit(final Class<?> type) {
-        return true;
+    Filter preVisit(final PropertyAccessor accessor) {
+        return Filter.WRITABLE;
     }
 
     /**


Mime
View raw message