You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sis.apache.org by de...@apache.org on 2023/07/02 20:10:03 UTC
[sis] 01/02: Add a `CloneAccess` internal interface as a workaround for accessing the `clone()` method in internal classes of other modules.
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 3808f1393025f5467fd8ed6a018edbaf3e0e38a4
Author: Martin Desruisseaux <ma...@geomatys.com>
AuthorDate: Sat Jun 17 17:23:45 2023 +0200
Add a `CloneAccess` internal interface as a workaround for accessing the `clone()` method in internal classes of other modules.
---
.../org/apache/sis/feature/CharacteristicMap.java | 5 +-
.../java/org/apache/sis/feature/DenseFeature.java | 5 +-
.../apache/sis/feature/MultiValuedAttribute.java | 5 +-
.../org/apache/sis/feature/SingletonAttribute.java | 5 +-
.../java/org/apache/sis/feature/SparseFeature.java | 5 +-
.../sis/internal/jaxb/SpecializedIdentifier.java | 5 +-
.../iso/quality/DefaultEvaluationMethod.java | 3 +-
.../sis/internal/map/coverage/RenderingData.java | 3 +-
.../sis/internal/referencing/AnnotatedMatrix.java | 5 +-
.../sis/internal/referencing/j2d/AffineMatrix.java | 3 +-
.../org/apache/sis/parameter/TensorValues.java | 3 +-
.../org/apache/sis/internal/util/CloneAccess.java | 48 +++++++
.../java/org/apache/sis/internal/util/Cloner.java | 155 +++++++++++----------
.../sis/util/collection/DefaultTreeTable.java | 4 +-
14 files changed, 162 insertions(+), 92 deletions(-)
diff --git a/core/sis-feature/src/main/java/org/apache/sis/feature/CharacteristicMap.java b/core/sis-feature/src/main/java/org/apache/sis/feature/CharacteristicMap.java
index ecca81273a..c5b19ed0ee 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/feature/CharacteristicMap.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/feature/CharacteristicMap.java
@@ -20,6 +20,7 @@ import java.util.Map;
import org.opengis.util.GenericName;
import org.apache.sis.util.ArgumentChecks;
import org.apache.sis.internal.util.Cloner;
+import org.apache.sis.internal.util.CloneAccess;
import org.apache.sis.internal.util.AbstractMap;
import org.apache.sis.internal.util.AbstractMapEntry;
import org.apache.sis.internal.feature.Resources;
@@ -36,10 +37,10 @@ import org.opengis.feature.PropertyNotFoundException;
* This map holds only the attribute characteristics which have been explicitly set or requested.
*
* @author Martin Desruisseaux (Geomatys)
- * @version 0.6
+ * @version 1.4
* @since 0.5
*/
-final class CharacteristicMap extends AbstractMap<String,Attribute<?>> implements Cloneable {
+final class CharacteristicMap extends AbstractMap<String,Attribute<?>> implements CloneAccess {
/**
* The attribute source for which to provide characteristics.
*/
diff --git a/core/sis-feature/src/main/java/org/apache/sis/feature/DenseFeature.java b/core/sis-feature/src/main/java/org/apache/sis/feature/DenseFeature.java
index 54193162b0..83815273c2 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/feature/DenseFeature.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/feature/DenseFeature.java
@@ -20,6 +20,7 @@ import java.util.Map;
import java.util.Arrays;
import org.opengis.metadata.maintenance.ScopeCode;
import org.opengis.metadata.quality.DataQuality;
+import org.apache.sis.internal.util.CloneAccess;
import org.apache.sis.internal.util.Cloner;
import org.apache.sis.util.ArgumentChecks;
@@ -37,14 +38,14 @@ import org.opengis.feature.PropertyNotFoundException;
*
* @author Martin Desruisseaux (Geomatys)
* @author Marc le Bihan
- * @version 1.1
+ * @version 1.4
*
* @see SparseFeature
* @see DefaultFeatureType
*
* @since 0.5
*/
-final class DenseFeature extends AbstractFeature implements Cloneable {
+final class DenseFeature extends AbstractFeature implements CloneAccess {
/**
* For cross-version compatibility.
*/
diff --git a/core/sis-feature/src/main/java/org/apache/sis/feature/MultiValuedAttribute.java b/core/sis-feature/src/main/java/org/apache/sis/feature/MultiValuedAttribute.java
index 1f1b7a2b28..c89fda682b 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/feature/MultiValuedAttribute.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/feature/MultiValuedAttribute.java
@@ -17,6 +17,7 @@
package org.apache.sis.feature;
import java.util.Collection;
+import org.apache.sis.internal.util.CloneAccess;
import org.apache.sis.internal.util.CheckedArrayList;
import org.apache.sis.util.collection.CheckedContainer;
import org.apache.sis.util.ArgumentChecks;
@@ -47,7 +48,7 @@ import org.opengis.feature.MultiValuedPropertyException;
*
* @author Johann Sorel (Geomatys)
* @author Martin Desruisseaux (Geomatys)
- * @version 0.8
+ * @version 1.4
*
* @param <V> the type of the attribute values.
*
@@ -55,7 +56,7 @@ import org.opengis.feature.MultiValuedPropertyException;
*
* @since 0.5
*/
-final class MultiValuedAttribute<V> extends AbstractAttribute<V> implements Cloneable {
+final class MultiValuedAttribute<V> extends AbstractAttribute<V> implements CloneAccess {
/**
* For cross-version compatibility.
*/
diff --git a/core/sis-feature/src/main/java/org/apache/sis/feature/SingletonAttribute.java b/core/sis-feature/src/main/java/org/apache/sis/feature/SingletonAttribute.java
index ab25bc7501..0750738d1c 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/feature/SingletonAttribute.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/feature/SingletonAttribute.java
@@ -17,6 +17,7 @@
package org.apache.sis.feature;
import java.util.Objects;
+import org.apache.sis.internal.util.CloneAccess;
// Branch-dependent imports
import org.opengis.feature.AttributeType;
@@ -38,7 +39,7 @@ import org.opengis.feature.AttributeType;
*
* @author Johann Sorel (Geomatys)
* @author Martin Desruisseaux (Geomatys)
- * @version 0.8
+ * @version 1.4
*
* @param <V> the type of the attribute value.
*
@@ -47,7 +48,7 @@ import org.opengis.feature.AttributeType;
* @since 0.5
*/
@SuppressWarnings("CloneableImplementsClone") // Nothing to add compared to subclass.
-final class SingletonAttribute<V> extends AbstractAttribute<V> implements Cloneable {
+final class SingletonAttribute<V> extends AbstractAttribute<V> implements CloneAccess {
/**
* For cross-version compatibility.
*/
diff --git a/core/sis-feature/src/main/java/org/apache/sis/feature/SparseFeature.java b/core/sis-feature/src/main/java/org/apache/sis/feature/SparseFeature.java
index 7b3ddce242..781bd7475a 100644
--- a/core/sis-feature/src/main/java/org/apache/sis/feature/SparseFeature.java
+++ b/core/sis-feature/src/main/java/org/apache/sis/feature/SparseFeature.java
@@ -22,6 +22,7 @@ import java.util.Objects;
import java.util.ConcurrentModificationException;
import org.opengis.metadata.maintenance.ScopeCode;
import org.opengis.metadata.quality.DataQuality;
+import org.apache.sis.internal.util.CloneAccess;
import org.apache.sis.internal.util.Cloner;
import org.apache.sis.util.ArgumentChecks;
import org.apache.sis.util.CorruptedObjectException;
@@ -41,14 +42,14 @@ import org.opengis.feature.PropertyNotFoundException;
* @author Travis L. Pinney
* @author Johann Sorel (Geomatys)
* @author Martin Desruisseaux (Geomatys)
- * @version 1.2
+ * @version 1.4
*
* @see DenseFeature
* @see DefaultFeatureType
*
* @since 0.5
*/
-final class SparseFeature extends AbstractFeature implements Cloneable {
+final class SparseFeature extends AbstractFeature implements CloneAccess {
/**
* For cross-version compatibility.
*/
diff --git a/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/SpecializedIdentifier.java b/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/SpecializedIdentifier.java
index 4620c3c516..b783a3188f 100644
--- a/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/SpecializedIdentifier.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/internal/jaxb/SpecializedIdentifier.java
@@ -28,6 +28,7 @@ import org.apache.sis.xml.IdentifierMap;
import org.apache.sis.xml.IdentifierSpace;
import org.apache.sis.xml.ValueConverter;
import org.apache.sis.util.resources.Messages;
+import org.apache.sis.internal.util.CloneAccess;
import org.apache.sis.metadata.iso.citation.Citations;
// Branch-dependent imports
@@ -40,13 +41,13 @@ import org.opengis.metadata.Identifier;
* is an object of a type constrained by the authority.
*
* @author Martin Desruisseaux (Geomatys)
- * @version 0.7
+ * @version 1.4
*
* @param <T> the value type, typically {@link XLink}, {@link UUID} or {@link String}.
*
* @since 0.3
*/
-public final class SpecializedIdentifier<T> implements Identifier, Cloneable, Serializable {
+public final class SpecializedIdentifier<T> implements Identifier, CloneAccess, Serializable {
/**
* For cross-version compatibility.
*/
diff --git a/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/quality/DefaultEvaluationMethod.java b/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/quality/DefaultEvaluationMethod.java
index a88ac7b622..98b8c69e3c 100644
--- a/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/quality/DefaultEvaluationMethod.java
+++ b/core/sis-metadata/src/main/java/org/apache/sis/metadata/iso/quality/DefaultEvaluationMethod.java
@@ -32,6 +32,7 @@ import org.opengis.metadata.quality.EvaluationMethodType;
import org.opengis.metadata.quality.DataEvaluation;
import org.opengis.metadata.quality.AggregationDerivation;
import org.apache.sis.internal.system.Semaphores;
+import org.apache.sis.internal.util.CloneAccess;
import org.apache.sis.util.collection.CheckedContainer;
import org.apache.sis.util.resources.Errors;
import org.apache.sis.util.ArgumentChecks;
@@ -108,7 +109,7 @@ public class DefaultEvaluationMethod extends ISOMetadata implements EvaluationMe
* The start and end times as a list of O, 1 or 2 elements.
*/
private static final class Dates extends AbstractList<Temporal>
- implements CheckedContainer<Temporal>, Cloneable, Serializable
+ implements CheckedContainer<Temporal>, CloneAccess, Serializable
{
/**
* For cross-version compatibility.
diff --git a/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/coverage/RenderingData.java b/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/coverage/RenderingData.java
index 0b5f9efe5b..e1d83fbd21 100644
--- a/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/coverage/RenderingData.java
+++ b/core/sis-portrayal/src/main/java/org/apache/sis/internal/map/coverage/RenderingData.java
@@ -57,6 +57,7 @@ import org.apache.sis.internal.coverage.j2d.ColorModelType;
import org.apache.sis.internal.coverage.j2d.ImageUtilities;
import org.apache.sis.internal.referencing.WraparoundApplicator;
import org.apache.sis.internal.system.Modules;
+import org.apache.sis.internal.util.CloneAccess;
import org.apache.sis.io.TableAppender;
import org.apache.sis.math.Statistics;
import org.apache.sis.measure.Quantities;
@@ -106,7 +107,7 @@ import org.apache.sis.portrayal.PlanarCanvas; // For javadoc.
* @version 1.4
* @since 1.1
*/
-public class RenderingData implements Cloneable {
+public class RenderingData implements CloneAccess {
/**
* The logger for portrayal.
*/
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/AnnotatedMatrix.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/AnnotatedMatrix.java
index 33ac141b41..3a53ae65a0 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/AnnotatedMatrix.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/AnnotatedMatrix.java
@@ -18,6 +18,7 @@ package org.apache.sis.internal.referencing;
import org.opengis.referencing.operation.Matrix;
import org.opengis.metadata.quality.PositionalAccuracy;
+import org.apache.sis.internal.util.CloneAccess;
/**
@@ -25,10 +26,10 @@ import org.opengis.metadata.quality.PositionalAccuracy;
* We use this class for passing additional information in methods that returns only a {@link Matrix}.
*
* @author Martin Desruisseaux (Geomatys)
- * @version 1.1
+ * @version 1.4
* @since 1.1
*/
-public final class AnnotatedMatrix implements Matrix, Cloneable {
+public final class AnnotatedMatrix implements Matrix, CloneAccess {
/**
* The matrix which contains the actual values.
*/
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/j2d/AffineMatrix.java b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/j2d/AffineMatrix.java
index 2d719617bd..58aab57c37 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/j2d/AffineMatrix.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/internal/referencing/j2d/AffineMatrix.java
@@ -20,6 +20,7 @@ import java.util.Arrays;
import java.io.Serializable;
import java.awt.geom.AffineTransform;
import org.opengis.referencing.operation.Matrix;
+import org.apache.sis.internal.util.CloneAccess;
import org.apache.sis.internal.referencing.Resources;
import org.apache.sis.internal.referencing.ExtendedPrecisionMatrix;
import org.apache.sis.referencing.operation.matrix.Matrices;
@@ -36,7 +37,7 @@ import org.apache.sis.util.ArgumentChecks;
* @version 1.4
* @since 0.5
*/
-class AffineMatrix extends MatrixSIS implements Serializable, Cloneable {
+class AffineMatrix extends MatrixSIS implements Serializable, CloneAccess {
/**
* For cross-version compatibility.
*/
diff --git a/core/sis-referencing/src/main/java/org/apache/sis/parameter/TensorValues.java b/core/sis-referencing/src/main/java/org/apache/sis/parameter/TensorValues.java
index befec92301..501eca0d68 100644
--- a/core/sis-referencing/src/main/java/org/apache/sis/parameter/TensorValues.java
+++ b/core/sis-referencing/src/main/java/org/apache/sis/parameter/TensorValues.java
@@ -36,6 +36,7 @@ import org.apache.sis.referencing.operation.matrix.Matrices;
import org.apache.sis.internal.referencing.Resources;
import org.apache.sis.internal.referencing.WKTUtilities;
import org.apache.sis.internal.referencing.WKTKeywords;
+import org.apache.sis.internal.util.CloneAccess;
import org.apache.sis.internal.util.Numerics;
import org.apache.sis.internal.util.UnmodifiableArrayList;
import org.apache.sis.io.wkt.Formatter;
@@ -61,7 +62,7 @@ import org.apache.sis.util.resources.Errors;
*/
@XmlTransient
final class TensorValues<E> extends AbstractParameterDescriptor
- implements ParameterDescriptorGroup, ParameterValueGroup, Cloneable
+ implements ParameterDescriptorGroup, ParameterValueGroup, CloneAccess
{
/**
* Serial number for inter-operability with different versions.
diff --git a/core/sis-utility/src/main/java/org/apache/sis/internal/util/CloneAccess.java b/core/sis-utility/src/main/java/org/apache/sis/internal/util/CloneAccess.java
new file mode 100644
index 0000000000..5b54ae7479
--- /dev/null
+++ b/core/sis-utility/src/main/java/org/apache/sis/internal/util/CloneAccess.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.internal.util;
+
+
+/**
+ * Workaround for the absence of public {@code clone()} method in the standard interface.
+ * The purpose of this interface is to avoid the following exception when {@link Cloner}
+ * tries to clone a class defined in an internal package of another module:
+ *
+ * <blockquote>{@link IllegalAccessException}: class {@link Cloner}
+ * (in module {@code org.apache.sis.util}) cannot access class <var>Foo</var>
+ * (in module <var>bar</var>) because module <var>bar</var> does not export
+ * <var>foo</var> to module {@code org.apache.sis.util}</blockquote>
+ *
+ * This workaround is needed only for Apache SIS internal classes, because {@link Cloner}
+ * usage of reflection should work for exported packages.
+ *
+ * <p>This interface may be removed in any future Apache SIS version if we find a better
+ * way to workaround the lack of public {@code clone()} method in {@link Cloneable}.</p>
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.4
+ * @since 1.4
+ */
+public interface CloneAccess extends java.lang.Cloneable {
+ /**
+ * Returns a clone of this object.
+ *
+ * @return a clone of this object.
+ * @throws CloneNotSupportedException if clones are not supported.
+ */
+ Object clone() throws CloneNotSupportedException;
+}
diff --git a/core/sis-utility/src/main/java/org/apache/sis/internal/util/Cloner.java b/core/sis-utility/src/main/java/org/apache/sis/internal/util/Cloner.java
index 29955d6da9..ad15639b7f 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/internal/util/Cloner.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/internal/util/Cloner.java
@@ -82,7 +82,7 @@ public final class Cloner {
}
/**
- * Clones the given array, then clone all array elements recursively.
+ * Clones the given array, then clones all array elements recursively.
*
* @param array the array to clone.
* @param componentType value of {@code array.getClass().getComponentType()}.
@@ -107,8 +107,8 @@ public final class Cloner {
}
/**
- * Clones the given object. If the given object does not provide a public {@code clone()}
- * method, then there is a choice:
+ * Clones the given object.
+ * If the given object does not provide a public {@code clone()} method, then there is a choice:
*
* <ul>
* <li>If {@code isCloneRequired(object)} returns {@code true} (the default),
@@ -128,52 +128,56 @@ public final class Cloner {
if (result != null) {
return result;
}
- final Class<?> valueType = object.getClass();
- final Class<?> componentType = valueType.getComponentType();
- if (componentType != null) {
- return cloneArray(object, componentType);
- }
- RuntimeException security = null;
- result = object;
- try {
- if (valueType != type) {
- method = valueType.getMethod("clone", (Class<?>[]) null);
- type = valueType; // Set only if the above line succeed.
+ if (object instanceof CloneAccess) {
+ result = ((CloneAccess) object).clone();
+ } else {
+ final Class<?> valueType = object.getClass();
+ final Class<?> componentType = valueType.getComponentType();
+ if (componentType != null) {
+ return cloneArray(object, componentType);
+ }
+ RuntimeException security = null;
+ result = object;
+ try {
+ if (valueType != type) {
+ method = valueType.getMethod("clone", (Class<?>[]) null);
+ type = valueType; // Set only if the above line succeed.
+ /*
+ * If the class implementing the `clone()` method is not public, we may not be able to access that
+ * method even if it is public. Try to make the method accessible. If we fail, try to clone anyway
+ * because maybe a parent class is accessible, but we remember the exception in order to report it
+ * in case of failure.
+ */
+ if (!Modifier.isPublic(method.getDeclaringClass().getModifiers())) try {
+ method.setAccessible(true);
+ } catch (SecurityException | InaccessibleObjectException e) {
+ security = e;
+ }
+ }
/*
- * If the class implementing the `clone()` method is not public, we may not be able to access that
- * method even if it is public. Try to make the method accessible. If we fail for security reason,
- * we will still attempt to clone (maybe a parent class is public), but we remember the exception
- * in order to report it in case of failure.
+ * `method` may be null if a previous call to this clone(Object) method threw NoSuchMethodException
+ * (see the first `catch` block below). In this context, `null` means "no public clone() method".
*/
- if (!Modifier.isPublic(method.getDeclaringClass().getModifiers())) try {
- method.setAccessible(true);
- } catch (SecurityException | InaccessibleObjectException e) {
- security = e;
+ if (method != null) {
+ result = method.invoke(object, (Object[]) null);
+ }
+ } catch (NoSuchMethodException e) {
+ if (isCloneRequired) {
+ throw fail(e, valueType);
+ }
+ method = null;
+ type = valueType;
+ } catch (IllegalAccessException e) {
+ if (security != null) {
+ e.addSuppressed(security);
}
- }
- /*
- * `method` may be null if a previous call to this clone(Object) method threw NoSuchMethodException
- * (see the first `catch` block below). In this context, `null` means "no public clone() method".
- */
- if (method != null) {
- result = method.invoke(object, (Object[]) null);
- }
- } catch (NoSuchMethodException e) {
- if (isCloneRequired) {
+ throw fail(e, valueType);
+ } catch (InvocationTargetException e) {
+ rethrow(e.getCause());
+ throw fail(e, valueType);
+ } catch (SecurityException e) {
throw fail(e, valueType);
}
- method = null;
- type = valueType;
- } catch (IllegalAccessException e) {
- if (security != null) {
- e.addSuppressed(security);
- }
- throw fail(e, valueType);
- } catch (InvocationTargetException e) {
- rethrow(e.getCause());
- throw fail(e, valueType);
- } catch (SecurityException e) {
- throw fail(e, valueType);
}
if (cloneResults.put(object, result) != null) {
// Should never happen unless we have a bug.
@@ -219,6 +223,8 @@ public final class Cloner {
* This method may be convenient when there is only one object to clone, otherwise instantiating a new
* {@code Cloner} object is more efficient.
*
+ * <p>Callers should test {@code if (object instanceof Cloneable)} before to invoke this method.</p>
+ *
* @param object the object to clone, or {@code null}.
* @return the given object (which may be {@code null}) or a clone of the given object.
* @throws CloneNotSupportedException if the call to {@link Object#clone()} failed.
@@ -227,36 +233,39 @@ public final class Cloner {
*/
@SuppressWarnings("SuspiciousSystemArraycopy")
public static Object cloneIfPublic(final Object object) throws CloneNotSupportedException {
- if (object != null) {
- final Class<?> type = object.getClass();
- final Class<?> componentType = type.getComponentType();
- if (componentType != null) {
- if (componentType.isPrimitive()) {
- final int length = Array.getLength(object);
- final Object copy = Array.newInstance(componentType, length);
- System.arraycopy(object, 0, copy, 0, length);
- return copy;
- }
- return new Cloner().cloneArray(object, componentType);
- }
- try {
- final Method m = type.getMethod("clone", (Class[]) null);
- if (Modifier.isPublic(m.getModifiers())) {
- return m.invoke(object, (Object[]) null);
- }
- } catch (NoSuchMethodException | IllegalAccessException e) {
- /*
- * Should never happen because all objects have a clone() method
- * and we verified that the method is public.
- */
- throw new AssertionError(e);
- } catch (InvocationTargetException e) {
- rethrow(e.getCause());
- throw fail(e, type);
- } catch (SecurityException e) {
- throw fail(e, type);
+ if (object instanceof CloneAccess) {
+ return ((CloneAccess) object).clone();
+ }
+ final Class<?> type = object.getClass();
+ final Class<?> componentType = type.getComponentType();
+ if (componentType != null) {
+ if (componentType.isPrimitive()) {
+ final int length = Array.getLength(object);
+ final Object copy = Array.newInstance(componentType, length);
+ System.arraycopy(object, 0, copy, 0, length);
+ return copy;
}
+ return new Cloner().cloneArray(object, componentType);
+ }
+ try {
+ final Method method = type.getMethod("clone", (Class[]) null);
+ return method.invoke(object, (Object[]) null);
+ } catch (NoSuchMethodException e) {
+ /*
+ * May happen if the `clone()` method is not public.
+ * The method inherited from `Object` is protected,
+ * and `getMethod(…)` does not return protected methods.
+ */
+ return object;
+ } catch (SecurityException | IllegalAccessException e) {
+ /*
+ * May happen if the class is defined in a module which
+ * does not export the package containing the class.
+ */
+ throw fail(e, type);
+ } catch (InvocationTargetException e) {
+ rethrow(e.getCause());
+ throw fail(e, type);
}
- return object;
}
}
diff --git a/core/sis-utility/src/main/java/org/apache/sis/util/collection/DefaultTreeTable.java b/core/sis-utility/src/main/java/org/apache/sis/util/collection/DefaultTreeTable.java
index f46178efd1..71654783e6 100644
--- a/core/sis-utility/src/main/java/org/apache/sis/util/collection/DefaultTreeTable.java
+++ b/core/sis-utility/src/main/java/org/apache/sis/util/collection/DefaultTreeTable.java
@@ -258,7 +258,9 @@ public class DefaultTreeTable implements TreeTable, Cloneable, Serializable {
@Override
public DefaultTreeTable clone() throws CloneNotSupportedException {
final DefaultTreeTable clone = (DefaultTreeTable) super.clone();
- clone.root = (TreeTable.Node) Cloner.cloneIfPublic(clone.root);
+ if (clone.root instanceof Cloneable) {
+ clone.root = (TreeTable.Node) Cloner.cloneIfPublic(clone.root);
+ }
return clone;
}