This is an automated email from the ASF dual-hosted git repository.

asf-gitbox-commits pushed a commit to branch geoapi-3.1
in repository https://gitbox.apache.org/repos/asf/sis.git

commit 45a58d0acf0d1845d3f99d95ba8e214e40d974bb
Merge: 3b34cf323f dc78d5d1ab
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Thu May 28 17:54:58 2026 +0200

    Merge branch 'geoapi-4.0' into geoapi-3.1.

 .../org/apache/sis/console/OperationParser.java    |   2 +-
 .../org/apache/sis/coverage/RegionOfInterest.java  |   6 +-
 .../org/apache/sis/feature/FeatureOperations.java  |   4 +-
 .../main/org/apache/sis/filter/base/Node.java      |   5 +-
 .../sis/filter/sqlmm/FunctionDescription.java      |  12 +
 .../geometry/wrapper/SpatialOperationContext.java  |   3 +-
 .../org/apache/sis/metadata/AbstractMetadata.java  |  36 +-
 .../main/org/apache/sis/metadata/CacheKey.java     |  29 +-
 .../org/apache/sis/metadata/MetadataCopier.java    |  10 +-
 .../org/apache/sis/metadata/MetadataStandard.java  | 361 +++++++++++++--------
 .../org/apache/sis/metadata/MetadataVisitor.java   |  14 +-
 .../apache/sis/metadata/ModifiableMetadata.java    |  11 +-
 .../org/apache/sis/metadata/PropertyAccessor.java  |   2 +-
 .../main/org/apache/sis/metadata/Pruner.java       |   2 +-
 .../sis/metadata/StandardImplementation.java       |   6 +-
 .../main/org/apache/sis/metadata/StateChanger.java |   2 +-
 .../org/apache/sis/metadata/TitleProperty.java     |  11 +-
 .../main/org/apache/sis/metadata/TreeNode.java     |  41 ++-
 .../org/apache/sis/metadata/TreeNodeChildren.java  |  78 +++--
 .../apache/sis/metadata/ValueExistencePolicy.java  |   1 +
 .../sis/metadata/internal/shared/Merger.java       |  17 +-
 .../metadata/internal/shared/SecondaryTrait.java   |  45 ---
 .../iso/DefaultApplicationSchemaInformation.java   |   4 +-
 .../sis/metadata/iso/DefaultMetadataScope.java     |   4 +-
 .../sis/metadata/iso/acquisition/DefaultEvent.java |   4 +-
 .../iso/acquisition/DefaultInstrument.java         |   4 +-
 .../metadata/iso/acquisition/DefaultObjective.java |   2 +-
 .../metadata/iso/acquisition/DefaultOperation.java |   4 +-
 .../sis/metadata/iso/acquisition/DefaultPlan.java  |   4 +-
 .../metadata/iso/acquisition/DefaultPlatform.java  |   4 +-
 .../iso/acquisition/DefaultPlatformPass.java       |   4 +-
 .../iso/acquisition/DefaultRequirement.java        |   2 +
 .../sis/metadata/iso/acquisition/package-info.java |   2 +-
 .../iso/citation/DefaultOnlineResource.java        |   4 +-
 .../sis/metadata/iso/citation/DefaultSeries.java   |   4 +-
 .../metadata/iso/citation/DefaultTelephone.java    |   4 +-
 .../sis/metadata/iso/citation/package-info.java    |   2 +-
 .../iso/constraint/DefaultReleasability.java       |   4 +-
 .../iso/constraint/DefaultSecurityConstraints.java |   4 +-
 .../sis/metadata/iso/constraint/package-info.java  |   2 +-
 .../DefaultFeatureCatalogueDescription.java        |   4 +-
 .../metadata/iso/distribution/DefaultDataFile.java |   8 +-
 .../metadata/iso/distribution/DefaultFormat.java   |   4 +-
 .../metadata/iso/distribution/DefaultMedium.java   |   4 +-
 .../metadata/iso/distribution/package-info.java    |   2 +-
 .../iso/extent/DefaultGeographicDescription.java   |   4 +-
 .../sis/metadata/iso/extent/package-info.java      |   2 +-
 .../iso/identification/AbstractIdentification.java |   4 +-
 .../DefaultAggregateInformation.java               |   4 +-
 .../identification/DefaultAssociatedResource.java  |   4 +-
 .../iso/identification/DefaultBrowseGraphic.java   |   4 +-
 .../identification/DefaultDataIdentification.java  |   4 +-
 .../metadata/iso/identification/package-info.java  |   2 +-
 .../sis/metadata/iso/lineage/DefaultAlgorithm.java |   4 +-
 .../sis/metadata/iso/lineage/DefaultLineage.java   |   4 +-
 .../metadata/iso/lineage/DefaultProcessing.java    |   4 +-
 .../sis/metadata/iso/lineage/package-info.java     |   2 +-
 .../org/apache/sis/metadata/iso/package-info.java  |   2 +-
 .../sis/metadata/iso/quality/AbstractElement.java  |   4 +-
 .../iso/quality/DefaultConformanceResult.java      |   4 +-
 .../iso/quality/DefaultEvaluationMethod.java       |   4 +-
 .../DefaultEvaluationReportInformation.java        |   4 +-
 .../iso/quality/DefaultSourceReference.java        |   4 +-
 .../sis/metadata/iso/quality/package-info.java     |   2 +-
 .../sis/metadata/iso/spatial/DefaultGCP.java       |   2 +-
 .../metadata/iso/spatial/DefaultGCPCollection.java |  16 +-
 .../metadata/simple/SimpleIdentifiedObject.java    |  23 +-
 .../apache/sis/metadata/simple/SimpleMetadata.java |  31 +-
 .../apache/sis/metadata/sql/MetadataSource.java    |  14 +-
 .../apache/sis/metadata/sql/MetadataWriter.java    |  30 +-
 .../org/apache/sis/temporal/DefaultInstant.java    |  13 +
 .../org/apache/sis/temporal/DefaultPeriod.java     |  13 +
 .../main/org/apache/sis/temporal/TemporalDate.java |  13 +
 .../sis/xml/bind/metadata/replace/Parameter.java   |  15 +-
 .../metadata/replace/ReferenceSystemMetadata.java  |  13 +
 .../apache/sis/metadata/MetadataStandardTest.java  |   8 +-
 .../sis/metadata/ModifiableMetadataTest.java       |   1 +
 .../sis/metadata/PropertyConsistencyCheck.java     |  16 +-
 .../apache/sis/metadata/TreeNodeChildrenTest.java  |   2 +-
 .../sis/openoffice/ReferencingFunctions.java       |  11 +-
 .../gazetteer/AbstractLocationType.java            |  12 +
 .../gazetteer/ReferencingByIdentifiers.java        |   9 +-
 .../sis/referencing/gazetteer/package-info.java    |   2 +-
 .../sis/coordinate/DefaultCoordinateMetadata.java  |  15 +-
 .../main/org/apache/sis/io/wkt/WKTDictionary.java  |   8 +-
 .../sis/parameter/AbstractParameterDescriptor.java |   8 +-
 .../sis/parameter/DefaultParameterDescriptor.java  |  18 +-
 .../parameter/DefaultParameterDescriptorGroup.java |  15 +-
 .../sis/parameter/DefaultParameterValue.java       |  15 +-
 .../apache/sis/parameter/ParameterizedType.java    |  49 +++
 .../main/org/apache/sis/parameter/Parameters.java  |  17 +-
 .../org/apache/sis/parameter/package-info.java     |   2 +-
 .../sis/referencing/AbstractIdentifiedObject.java  |  65 ++--
 .../sis/referencing/AbstractReferenceSystem.java   |   8 +-
 .../main/org/apache/sis/referencing/Builder.java   |   6 +-
 .../sis/referencing/DefaultObjectDomain.java       |  15 +-
 .../apache/sis/referencing/GeodeticCalculator.java |   5 +-
 .../apache/sis/referencing/crs/AbstractCRS.java    |   8 +-
 .../sis/referencing/crs/AbstractDerivedCRS.java    |   2 +-
 .../sis/referencing/crs/AbstractSingleCRS.java     |   5 +-
 .../sis/referencing/crs/DefaultCompoundCRS.java    |  22 +-
 .../sis/referencing/crs/DefaultDerivedCRS.java     |  29 +-
 .../sis/referencing/crs/DefaultEngineeringCRS.java |  15 +-
 .../sis/referencing/crs/DefaultGeocentricCRS.java  |  10 +-
 .../sis/referencing/crs/DefaultGeodeticCRS.java    |  10 +-
 .../sis/referencing/crs/DefaultGeographicCRS.java  |  13 +-
 .../sis/referencing/crs/DefaultParametricCRS.java  |  15 +-
 .../sis/referencing/crs/DefaultProjectedCRS.java   |  15 +-
 .../sis/referencing/crs/DefaultTemporalCRS.java    |  15 +-
 .../sis/referencing/crs/DefaultVerticalCRS.java    |  15 +-
 .../apache/sis/referencing/crs/package-info.java   |   2 +-
 .../org/apache/sis/referencing/cs/AbstractCS.java  |  11 +-
 .../apache/sis/referencing/cs/DefaultAffineCS.java |   7 +-
 .../sis/referencing/cs/DefaultCartesianCS.java     |  14 +-
 .../sis/referencing/cs/DefaultCompoundCS.java      |   2 +-
 .../cs/DefaultCoordinateSystemAxis.java            |  15 +-
 .../sis/referencing/cs/DefaultCylindricalCS.java   |  14 +-
 .../sis/referencing/cs/DefaultEllipsoidalCS.java   |  14 +-
 .../apache/sis/referencing/cs/DefaultLinearCS.java |  14 +-
 .../sis/referencing/cs/DefaultParametricCS.java    |  14 +-
 .../apache/sis/referencing/cs/DefaultPolarCS.java  |  14 +-
 .../sis/referencing/cs/DefaultSphericalCS.java     |  14 +-
 .../apache/sis/referencing/cs/DefaultTimeCS.java   |  12 +-
 .../sis/referencing/cs/DefaultVerticalCS.java      |  14 +-
 .../org/apache/sis/referencing/cs/Normalizer.java  |   2 +-
 .../sis/referencing/datum/AbstractDatum.java       |   7 +-
 .../referencing/datum/DefaultDatumEnsemble.java    |  26 +-
 .../sis/referencing/datum/DefaultEllipsoid.java    |  15 +-
 .../referencing/datum/DefaultEngineeringDatum.java |  15 +-
 .../referencing/datum/DefaultGeodeticDatum.java    |  15 +-
 .../referencing/datum/DefaultParametricDatum.java  |  15 +-
 .../referencing/datum/DefaultPrimeMeridian.java    |  15 +-
 .../referencing/datum/DefaultTemporalDatum.java    |  15 +-
 .../referencing/datum/DefaultVerticalDatum.java    |  15 +-
 .../factory/GeodeticAuthorityFactory.java          |   3 +-
 .../referencing/factory/GeodeticObjectFactory.java |   2 +-
 .../referencing/internal/ParameterizedType.java    | 118 +++++++
 .../internal/shared/AffineTransform2D.java         |  11 +
 .../internal/shared/ReferencingUtilities.java      |  50 ---
 .../sis/referencing/legacy/DefaultImageCRS.java    |   3 +-
 .../sis/referencing/legacy/DefaultImageDatum.java  |  12 +-
 .../referencing/legacy/DefaultUserDefinedCS.java   |  11 +-
 .../operation/AbstractCoordinateOperation.java     |   6 +-
 .../apache/sis/referencing/operation/CRSPair.java  |   3 +-
 .../operation/CoordinateOperationRegistry.java     |   2 +-
 .../operation/DefaultConcatenatedOperation.java    |  12 +-
 .../referencing/operation/DefaultConversion.java   |  10 +-
 .../DefaultCoordinateOperationFactory.java         |   6 +-
 .../operation/DefaultOperationMethod.java          |  15 +-
 .../operation/DefaultPassThroughOperation.java     |  15 +-
 .../referencing/operation/DefaultProjection.java   |   2 +-
 .../operation/DefaultTransformation.java           |  15 +-
 .../referencing/operation/matrix/MatrixSIS.java    |  20 +-
 .../referencing/operation/matrix/package-info.java |   2 +-
 .../operation/transform/AbstractMathTransform.java |  20 +-
 .../operation/transform/package-info.java          |   2 +-
 .../DefaultParameterDescriptorGroupTest.java       |  20 +-
 .../parameter/DefaultParameterDescriptorTest.java  | 119 +++----
 .../sis/parameter/DefaultParameterValueTest.java   |  46 +--
 .../datum/DefaultDatumEnsembleTest.java            |  99 ++++++
 .../datum/DefaultGeodeticDatumTest.java            |   6 +-
 .../sis/referencing/datum/HardCodedDatum.java      |   2 +-
 .../apache/sis/storage/landsat/LandsatStore.java   |   4 +-
 .../sis/storage/landsat/LandsatStoreProvider.java  |   4 +-
 .../apache/sis/storage/geotiff/GeoTiffStore.java   |   6 +-
 .../sis/storage/geotiff/GeoTiffStoreProvider.java  |  13 +-
 .../sis/storage/geotiff/writer/GeoEncoder.java     |   6 +-
 .../org/apache/sis/storage/netcdf/NetcdfStore.java |   4 +-
 .../sis/storage/netcdf/NetcdfStoreProvider.java    |  10 +-
 .../sis/storage/netcdf/classic/VariableInfo.java   |   4 +-
 .../org/apache/sis/storage/sql/feature/Column.java |   3 +-
 .../apache/sis/storage/sql/feature/Database.java   |  21 ++
 .../sis/storage/sql/feature/InfoStatements.java    |   8 +-
 .../apache/sis/storage/sql/postgis/Postgres.java   |  23 ++
 .../main/org/apache/sis/storage/gpx/Metadata.java  |   6 +-
 .../main/org/apache/sis/storage/gpx/Store.java     |   2 +-
 .../org/apache/sis/storage/gpx/StoreProvider.java  |  16 +-
 .../storage/xml/stream/StaxDataStoreProvider.java  |   8 +-
 .../main/org/apache/sis/storage/DataStore.java     |  27 +-
 .../org/apache/sis/storage/DataStoreProvider.java  |  32 +-
 .../main/org/apache/sis/storage/OptionKey.java     |  99 +++++-
 .../org/apache/sis/storage/StorageConnector.java   |   1 +
 .../org/apache/sis/storage/base/PRJDataStore.java  | 100 ++----
 .../apache/sis/storage/base/StoreUtilities.java    | 155 ++-------
 .../org/apache/sis/storage/base/URIDataStore.java  |  96 ++++--
 .../sis/storage/base/URIDataStoreOption.java       | 333 +++++++++++++++++++
 .../sis/storage/base/URIDataStoreProvider.java     | 178 ++++------
 .../main/org/apache/sis/storage/csv/Store.java     |  27 +-
 .../org/apache/sis/storage/csv/StoreProvider.java  |  67 +---
 .../apache/sis/storage/esri/AsciiGridStore.java    |   7 +-
 .../sis/storage/esri/AsciiGridStoreProvider.java   |   7 +-
 .../org/apache/sis/storage/esri/RasterStore.java   |   6 +-
 .../apache/sis/storage/esri/RawRasterStore.java    |   2 +-
 .../sis/storage/esri/RawRasterStoreProvider.java   |   7 +-
 .../main/org/apache/sis/storage/folder/Store.java  |  42 +--
 .../apache/sis/storage/folder/StoreProvider.java   |  90 ++---
 .../apache/sis/storage/folder/WritableStore.java   |  10 +-
 .../sis/storage/image/WorldFileStoreProvider.java  |   9 +-
 .../org/apache/sis/storage/internal/Resources.java |   7 +-
 .../sis/storage/internal/Resources.properties      |   3 +-
 .../sis/storage/internal/Resources_fr.properties   |   3 +-
 .../main/org/apache/sis/storage/wkt/Store.java     |  10 +-
 .../org/apache/sis/storage/wkt/StoreFormat.java    |   2 +-
 .../org/apache/sis/storage/wkt/StoreProvider.java  |  63 ++--
 .../apache/sis/storage/xml/AbstractProvider.java   |  13 +-
 .../org/apache/sis/storage/xml/StoreProvider.java  |   2 +-
 .../sis/storage/base/StoreUtilitiesTest.java       |  15 +-
 ...tiesTest.java => URIDataStoreProviderTest.java} |  20 +-
 .../test/org/apache/sis/storage/csv/StoreTest.java |   3 +-
 .../org/apache/sis/storage/folder/StoreTest.java   |   2 +-
 .../apache/sis/storage/wkt/StoreProviderTest.java  |   6 +-
 .../org/apache/sis/measure/AbstractConverter.java  |  21 +-
 .../main/org/apache/sis/measure/AbstractUnit.java  |  25 +-
 .../apache/sis/measure/ConcatenatedConverter.java  |   8 +-
 .../org/apache/sis/measure/LinearConverter.java    |   8 +-
 .../main/org/apache/sis/measure/package-info.java  |  10 +-
 .../main/org/apache/sis/util/Classes.java          | 211 +++++++-----
 .../main/org/apache/sis/util/ComparisonMode.java   |   3 +
 .../org/apache/sis/util/LenientComparable.java     |  39 ++-
 .../sis/util/resources/IndexedResourceBundle.java  |   3 +
 .../test/org/apache/sis/util/ClassesTest.java      |   9 +
 .../test/org/apache/sis/util/UtilitiesTest.java    |   7 +
 .../storage/coveragejson/CoverageJsonStore.java    |   4 +-
 .../coveragejson/CoverageJsonStoreProvider.java    |   5 +-
 .../apache/sis/storage/geoheif/GeoHeifStore.java   |   4 +-
 .../sis/storage/geoheif/GeoHeifStoreProvider.java  |   9 +-
 .../sis/storage/geopackage/GpkgStoreProvider.java  |   7 +-
 .../main/org/apache/sis/storage/gsf/GSFStore.java  |   6 +-
 .../apache/sis/storage/gsf/GSFStoreProvider.java   |  25 +-
 .../sis/storage/shapefile/ShapefileProvider.java   |  45 +--
 .../sis/storage/shapefile/ShapefileStore.java      |  70 ++--
 .../sis/storage/shapefile/ShapefileStoreTest.java  |  27 +-
 .../org/apache/sis/storage/shapefile/Snippets.java |   7 +-
 .../org/apache/sis/gui/dataset/PathAction.java     |  43 ++-
 .../org/apache/sis/gui/dataset/ResourceCell.java   |   3 +-
 .../apache/sis/gui/referencing/FilterByDatum.java  |   3 +-
 .../org/apache/sis/storage/gdal/GDALStore.java     |   4 +-
 .../apache/sis/storage/gdal/GDALStoreProvider.java |   7 +-
 238 files changed, 2776 insertions(+), 1864 deletions(-)

diff --cc 
endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/DefaultApplicationSchemaInformation.java
index be816d5c6b,42007ffc81..56181738ff
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/DefaultApplicationSchemaInformation.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/DefaultApplicationSchemaInformation.java
@@@ -23,10 -23,12 +23,11 @@@ import jakarta.xml.bind.annotation.adap
  import org.opengis.metadata.ApplicationSchemaInformation;
  import org.opengis.metadata.citation.Citation;
  import org.opengis.metadata.citation.OnlineResource;
+ import org.apache.sis.metadata.TitleProperty;
  import org.apache.sis.xml.Namespaces;
  
 -// Specific to the geoapi-4.0 branch:
 -import org.apache.sis.xml.bind.gco.CharSequenceAdapter;
 -import org.apache.sis.xml.bind.metadata.CI_OnlineResource;
 +// Specific to the main and geoapi-3.1 branches:
 +import java.net.URI;
  
  
  /**
diff --cc 
endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/acquisition/DefaultPlatform.java
index ab02dcb472,a820dc9f19..40896e1616
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/acquisition/DefaultPlatform.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/acquisition/DefaultPlatform.java
@@@ -25,10 -25,11 +25,11 @@@ import org.opengis.metadata.acquisition
  import org.opengis.metadata.acquisition.Platform;
  import org.opengis.metadata.citation.Citation;
  import org.opengis.util.InternationalString;
+ import org.apache.sis.metadata.TitleProperty;
  import org.apache.sis.metadata.iso.ISOMetadata;
  
 -// Specific to the geoapi-4.0 branch:
 -import org.opengis.metadata.citation.Responsibility;
 +// Specific to the main and geoapi-3.1 branches:
 +import org.opengis.metadata.citation.ResponsibleParty;
  
  
  /**
diff --cc 
endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/acquisition/DefaultRequirement.java
index c8e29c1e58,e37b0d4a29..bffe05b346
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/acquisition/DefaultRequirement.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/acquisition/DefaultRequirement.java
@@@ -27,12 -27,11 +27,13 @@@ import org.opengis.metadata.acquisition
  import org.opengis.metadata.acquisition.RequestedDate;
  import org.opengis.metadata.acquisition.Requirement;
  import org.opengis.metadata.citation.Citation;
+ import org.apache.sis.metadata.TitleProperty;
  import org.apache.sis.metadata.iso.ISOMetadata;
  
 -// Specific to the geoapi-4.0 branch:
 -import org.opengis.metadata.citation.Responsibility;
 +// Specific to the main and geoapi-3.1 branches:
 +import java.util.Date;
 +import org.opengis.metadata.citation.ResponsibleParty;
 +import org.apache.sis.temporal.TemporalDate;
  
  
  /**
@@@ -65,9 -64,10 +66,10 @@@
   *
   * @author  Cédric Briançon (Geomatys)
   * @author  Martin Desruisseaux (Geomatys)
 - * @version 2.0
 + * @version 1.5
   * @since   0.3
   */
+ @TitleProperty(name = "identifier")
  @XmlType(name = "MI_Requirement_Type", propOrder = {
      "citation",
      "identifier",
diff --cc 
endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/distribution/DefaultDataFile.java
index 85b4111de4,a8c0d6e559..8f186cab2e
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/distribution/DefaultDataFile.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/distribution/DefaultDataFile.java
@@@ -22,17 -22,18 +22,18 @@@ import jakarta.xml.bind.annotation.XmlT
  import jakarta.xml.bind.annotation.XmlElement;
  import jakarta.xml.bind.annotation.XmlRootElement;
  import jakarta.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
 -import org.opengis.util.InternationalString;
  import org.opengis.metadata.distribution.Format;
  import org.opengis.metadata.distribution.DataFile;
 +import org.opengis.util.InternationalString;
- import org.apache.sis.xml.Namespaces;
+ import org.apache.sis.metadata.TitleProperty;
  import org.apache.sis.metadata.iso.ISOMetadata;
+ import org.apache.sis.xml.Namespaces;
  import org.apache.sis.xml.bind.FilterByVersion;
- import org.apache.sis.xml.internal.shared.LegacyNamespaces;
  import org.apache.sis.xml.bind.gcx.MimeFileTypeAdapter;
+ import org.apache.sis.xml.internal.shared.LegacyNamespaces;
  
 -// Specific to the geoapi-4.0 branch:
 -import org.opengis.util.GenericName;
 +// Specific to the main and geoapi-3.1 branches:
 +import org.opengis.util.LocalName;
  
  
  /**
diff --cc 
endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/distribution/DefaultMedium.java
index a3c7498a39,21701bbe7e..5506b95521
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/distribution/DefaultMedium.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/distribution/DefaultMedium.java
@@@ -33,9 -33,10 +33,10 @@@ import org.apache.sis.xml.bind.FilterBy
  import org.apache.sis.xml.bind.gco.GO_Real;
  import org.apache.sis.xml.bind.metadata.CI_Citation;
  import org.apache.sis.xml.bind.metadata.MD_Identifier;
 -import org.apache.sis.xml.internal.shared.LegacyNamespaces;
+ import org.apache.sis.metadata.TitleProperty;
 -import org.apache.sis.metadata.iso.legacy.LegacyPropertyAdapter;
  import org.apache.sis.metadata.internal.Dependencies;
 +import org.apache.sis.metadata.iso.legacy.LegacyPropertyAdapter;
 +import org.apache.sis.xml.internal.shared.LegacyNamespaces;
  import org.apache.sis.util.collection.Containers;
  import static 
org.apache.sis.metadata.internal.shared.ImplementationHelper.ensurePositive;
  
diff --cc 
endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/lineage/DefaultLineage.java
index 4faa4c3ed4,0929d48ebd..5c44d14f68
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/lineage/DefaultLineage.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/iso/lineage/DefaultLineage.java
@@@ -26,7 -26,8 +26,8 @@@ import org.opengis.metadata.citation.Ci
  import org.opengis.metadata.lineage.Source;
  import org.opengis.metadata.lineage.Lineage;
  import org.opengis.metadata.lineage.ProcessStep;
 +import org.opengis.metadata.maintenance.ScopeCode;
+ import org.apache.sis.metadata.TitleProperty;
  import org.apache.sis.metadata.iso.ISOMetadata;
  import org.apache.sis.metadata.iso.maintenance.DefaultScope;
  import org.apache.sis.xml.bind.FilterByVersion;
diff --cc 
endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/simple/SimpleMetadata.java
index 2a89923dcc,3357d29223..0c0d38f7d5
--- 
a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/simple/SimpleMetadata.java
+++ 
b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/simple/SimpleMetadata.java
@@@ -107,9 -113,11 +113,11 @@@ public class SimpleMetadata implements 
  
      /**
       * Parties responsible for the metadata information.
+      *
+      * @return empty collection.
       */
      @Override
 -    public Collection<Responsibility> getContacts() {
 +    public Collection<ResponsibleParty> getContacts() {
          return Collections.emptyList();
      }
  
diff --cc 
endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/AbstractIdentifiedObject.java
index 40eef73fc0,43076c6565..f3023924fa
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/AbstractIdentifiedObject.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/AbstractIdentifiedObject.java
@@@ -800,20 -813,13 +818,25 @@@ public class AbstractIdentifiedObject e
          }
      }
  
 +    /**
-      * Returns {@code true} if the given object implements the same GeoAPI 
interface as this object.
++     * Returns the type of the given object, ignoring the deprecated {@link 
Projection} class.
++     * Allows the code to hehave as of the deprecated interface didn't 
existed.
 +     */
-     private boolean implementsSameInterface(final Object object) {
-         Class<? extends IdentifiedObject> type = getInterface();
-         if (Projection.class.isAssignableFrom(type)) {  // Deprecated 
interface, behave as if it does not exist.
++    private static Type getStandardType(final LenientComparable object) {
++        Type type = object.getStandardType();
++        if ((type instanceof Class<?>) && 
Projection.class.isAssignableFrom((Class<?>) type)) {
 +            type = Conversion.class;
 +        }
-         if (object instanceof AbstractIdentifiedObject) {
-             Class<?> other = ((AbstractIdentifiedObject) 
object).getInterface();
-             if (Projection.class.isAssignableFrom(other)) {
-                 other = Conversion.class;
-             }
-             return other == type;
++        return type;
++    }
++
+     /**
+      * Returns {@code true} if the given object implements the same GeoAPI 
interface as this object.
+      */
+     private boolean implementsSameInterface(final Object object) {
 -        Type type = getStandardType();
++        Type type = getStandardType(this);
+         if (object instanceof LenientComparable) {
 -            return type.equals(((LenientComparable) 
object).getStandardType());
++            return type.equals(getStandardType((LenientComparable) object));
          }
          /*
           * Fallback for non-SIS implementations.
@@@ -1071,10 -1078,11 +1095,11 @@@
          if (identifiers != null) {
              propertyAlreadySet("setIdentifier", "identifier");
          } else if (identifier != null) {
 -            final Identifier id = identifier.getIdentifier();
 +            final ReferenceIdentifier id = identifier.getIdentifier();
              if (id != null) {
                  identifiers = Collections.singleton(id);
-                 ScopedIdentifier<IdentifiedObject> key = new 
ScopedIdentifier<>(getInterface(), identifier.toString());
+                 Class<? extends IdentifiedObject> type = 
Classes.getStandardClass(this, IdentifiedObject.class);
+                 ScopedIdentifier<IdentifiedObject> key = new 
ScopedIdentifier<>(type, identifier.toString());
                  key.store(IdentifiedObject.class, this, 
AbstractIdentifiedObject.class, "setIdentifier");
                  if (key != (key = key.rename(identifier.code))) {
                      key.store(IdentifiedObject.class, this, null, null);      
  // Shorter form without codespace.
diff --cc 
endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/Builder.java
index 14602bfa74,efb262a864..b3a05802a3
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/Builder.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/Builder.java
@@@ -276,8 -273,8 +276,8 @@@ public abstract class Builder<B extend
          this();
          if (object != null) {
              properties.putAll(IdentifiedObjects.getProperties(object));
-             final GenericName[] valueAlias = (GenericName[]) 
properties.remove(IdentifiedObject.ALIAS_KEY);
-             final ReferenceIdentifier[] valueIds = (ReferenceIdentifier[]) 
properties.remove(IdentifiedObject.IDENTIFIERS_KEY);
+             final var valueAlias = (GenericName[]) 
properties.remove(IdentifiedObject.ALIAS_KEY);
 -            final var  valueIds  = (Identifier[])  
properties.remove(IdentifiedObject.IDENTIFIERS_KEY);
++            final var valueIds   = (ReferenceIdentifier[]) 
properties.remove(IdentifiedObject.IDENTIFIERS_KEY);
              if (valueAlias != null) Collections.addAll(aliases, valueAlias);
              if (valueIds   != null) Collections.addAll(identifiers, valueIds);
          }
diff --cc 
endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultDerivedCRS.java
index 70283c6670,da86912eaa..6158368b35
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultDerivedCRS.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultDerivedCRS.java
@@@ -381,28 -382,15 +382,24 @@@ public class DefaultDerivedCRS extends 
          }
      }
  
 +    /**
 +     * Returns the type of conversion associated to this {@code 
DefaultDerivedCRS}.
 +     * Must be a hard-coded, constant value (not dependent on object state).
 +     */
 +    @Override
 +    final Class<Conversion> getConversionType() {
 +        return Conversion.class;
 +    }
 +
      /**
-      * Returns the GeoAPI interface implemented by this class.
-      * The SIS implementation returns {@code DerivedCRS.class}.
-      *
-      * <h4>Note for implementers</h4>
-      * Subclasses usually do not need to override this method since GeoAPI 
does not define {@code DerivedCRS}
-      * sub-interface. Overriding possibility is left mostly for implementers 
who wish to extend GeoAPI with
-      * their own set of interfaces.
+      * Returns the GeoAPI interface that defines the contract of this 
implementation class.
+      * This is the base type required by {@code equals(…)} methods for 
returning a potentially {@code true} value.
       *
       * @return {@code DerivedCRS.class} or a user-defined sub-interface.
+      * @since 1.7
       */
      @Override
-     public Class<? extends DerivedCRS> getInterface() {
+     public Type getStandardType() {
          return DerivedCRS.class;
      }
  
diff --cc 
endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultGeocentricCRS.java
index 91115e1be5,5e0ee051f1..c221b6873b
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultGeocentricCRS.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultGeocentricCRS.java
@@@ -221,14 -218,15 +222,15 @@@ public class DefaultGeocentricCRS exten
      }
  
      /**
-      * Returns the GeoAPI interface implemented by this class.
-      * The SIS implementation returns {@code GeocentricCRS.class}.
+      * Returns the GeoAPI interface that defines the contract of this 
implementation class.
+      * This is the base type required by {@code equals(…)} methods for 
returning a potentially {@code true} value.
       *
 -     * @return {@code GeodeticCRS.class} or a user-defined sub-interface.
 +     * @return {@code GeocentricCRS.class} or a user-defined sub-interface.
+      * @since 1.7
       */
      @Override
-     public Class<? extends GeocentricCRS> getInterface() {
+     public Type getStandardType() {
 -        return GeodeticCRS.class;
 +        return GeocentricCRS.class;
      }
  
      /**
diff --cc 
endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultProjectedCRS.java
index d1026ad824,7e3209d2d1..c6de7da338
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultProjectedCRS.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/crs/DefaultProjectedCRS.java
@@@ -197,28 -194,15 +198,24 @@@ public class DefaultProjectedCRS extend
                  ? (DefaultProjectedCRS) object : new 
DefaultProjectedCRS(object);
      }
  
 +    /**
 +     * Returns the type of conversion associated to this {@code 
DefaultProjectedCRS}.
 +     * Must be a hard-coded, constant value (not dependent on object state).
 +     */
 +    @Override
 +    final Class<Projection> getConversionType() {
 +        return Projection.class;
 +    }
 +
      /**
-      * Returns the GeoAPI interface implemented by this class.
-      * The SIS implementation returns {@code ProjectedCRS.class}.
-      *
-      * <h4>Note for implementers</h4>
-      * Subclasses usually do not need to override this method since GeoAPI 
does not define {@code ProjectedCRS}
-      * sub-interface. Overriding possibility is left mostly for implementers 
who wish to extend GeoAPI with
-      * their own set of interfaces.
+      * Returns the GeoAPI interface that defines the contract of this 
implementation class.
+      * This is the base type required by {@code equals(…)} methods for 
returning a potentially {@code true} value.
       *
       * @return {@code ProjectedCRS.class} or a user-defined sub-interface.
+      * @since 1.7
       */
      @Override
-     public Class<? extends ProjectedCRS> getInterface() {
+     public Type getStandardType() {
          return ProjectedCRS.class;
      }
  
diff --cc 
endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/legacy/DefaultImageCRS.java
index 4034b5a263,370a464e6b..fb0dcfafba
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/legacy/DefaultImageCRS.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/legacy/DefaultImageCRS.java
@@@ -167,8 -132,8 +168,8 @@@ public final class DefaultImageCRS exte
       * @return the coordinate reference system interface implemented by this 
class.
       */
      @Override
-     public Class<? extends ImageCRS> getInterface() {
+     public Type getStandardType() {
 -        return SingleCRS.class;
 +        return ImageCRS.class;
      }
  
      /**
diff --cc 
endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/legacy/DefaultImageDatum.java
index 9d706c2f6b,e8568f24e3..782194c540
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/legacy/DefaultImageDatum.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/legacy/DefaultImageDatum.java
@@@ -18,6 -18,6 +18,7 @@@ package org.apache.sis.referencing.lega
  
  import java.util.Map;
  import java.util.Objects;
++import java.lang.reflect.Type;
  import jakarta.xml.bind.annotation.XmlType;
  import jakarta.xml.bind.annotation.XmlElement;
  import jakarta.xml.bind.annotation.XmlRootElement;
@@@ -122,53 -122,6 +123,48 @@@ public final class DefaultImageDatum ex
          this.pixelInCell = Objects.requireNonNull(pixelInCell);
      }
  
 +    /**
 +     * Creates a new datum with the same values as the specified one.
 +     * This copy constructor provides a way to convert an arbitrary 
implementation into a SIS one
 +     * or a user-defined one (as a subclass), usually in order to leverage 
some implementation-specific API.
 +     *
 +     * <p>This constructor performs a shallow copy, i.e. the properties are 
not cloned.</p>
 +     *
 +     * @param  datum  the datum to copy.
 +     *
 +     * @see #castOrCopy(ImageDatum)
 +     */
 +    protected DefaultImageDatum(final ImageDatum datum) {
 +        super(datum);
 +        pixelInCell = datum.getPixelInCell();
 +    }
 +
 +    /**
 +     * Returns a SIS datum implementation with the same values as the given 
arbitrary implementation.
 +     * If the given object is {@code null}, then this method returns {@code 
null}.
 +     * Otherwise if the given object is already a SIS implementation, then 
the given object is returned unchanged.
 +     * Otherwise a new SIS implementation is created and initialized to the 
attribute values of the given object.
 +     *
 +     * @param  object  the object to get as a SIS implementation, or {@code 
null} if none.
 +     * @return a SIS implementation containing the values of the given object 
(may be the
 +     *         given object itself), or {@code null} if the argument was null.
 +     */
 +    public static DefaultImageDatum castOrCopy(final ImageDatum object) {
 +        return (object == null) || (object instanceof DefaultImageDatum)
 +                ? (DefaultImageDatum) object : new DefaultImageDatum(object);
 +    }
 +
 +    /**
-      * Returns the GeoAPI interface implemented by this class.
-      * The SIS implementation returns {@code ImageDatum.class}.
-      *
-      * <h4>Note for implementers</h4>
-      * Subclasses usually do not need to override this method since GeoAPI 
does not define {@code ImageDatum}
-      * sub-interface. Overriding possibility is left mostly for implementers 
who wish to extend GeoAPI with
-      * their own set of interfaces.
++     * Returns the GeoAPI interface that defines the contract of this 
implementation class.
++     * This is the base type required by {@code equals(…)} methods for 
returning a potentially {@code true} value.
 +     *
 +     * @return {@code ImageDatum.class} or a user-defined sub-interface.
 +     */
 +    @Override
-     public Class<? extends ImageDatum> getInterface() {
++    public Type getStandardType() {
 +        return ImageDatum.class;
 +    }
 +
      /**
       * Specification of the way the image grid is associated with the image 
data attributes.
       *
diff --cc 
endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/legacy/DefaultUserDefinedCS.java
index 99ff3a8e10,771156d8b2..2e35b393a2
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/legacy/DefaultUserDefinedCS.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/legacy/DefaultUserDefinedCS.java
@@@ -116,51 -113,6 +116,46 @@@ public final class DefaultUserDefinedC
          super(properties, axis0, axis1, axis2);
      }
  
 +    /**
 +     * Creates a new coordinate system with the same values as the specified 
one.
 +     * This copy constructor provides a way to convert an arbitrary 
implementation into a SIS one
 +     * or a user-defined one (as a subclass), usually in order to leverage 
some implementation-specific API.
 +     *
 +     * <p>This constructor performs a shallow copy, i.e. the properties are 
not cloned.</p>
 +     *
 +     * @param  original  the coordinate system to copy.
 +     *
 +     * @see #castOrCopy(UserDefinedCS)
 +     */
 +    protected DefaultUserDefinedCS(final UserDefinedCS original) {
 +        super(original);
 +    }
 +
 +    /**
 +     * Returns a SIS coordinate system implementation with the same values as 
the given arbitrary implementation.
 +     * If the given object is {@code null}, then this method returns {@code 
null}.
 +     * Otherwise if the given object is already a SIS implementation, then 
the given object is returned unchanged.
 +     * Otherwise a new SIS implementation is created and initialized to the 
attribute values of the given object.
 +     *
 +     * @param  object  the object to get as a SIS implementation, or {@code 
null} if none.
 +     * @return a SIS implementation containing the values of the given object 
(may be the
 +     *         given object itself), or {@code null} if the argument was null.
 +     */
 +    public static DefaultUserDefinedCS castOrCopy(final UserDefinedCS object) 
{
 +        return (object == null) || (object instanceof DefaultUserDefinedCS)
 +                ? (DefaultUserDefinedCS) object : new 
DefaultUserDefinedCS(object);
 +    }
 +
 +    /**
-      * Returns the GeoAPI interface implemented by this class.
-      * The SIS implementation returns {@code UserDefinedCS.class}.
-      *
-      * <h4>Note for implementers</h4>
-      * Subclasses usually do not need to override this method since GeoAPI 
does not define {@code UserDefinedCS}
-      * sub-interface. Overriding possibility is left mostly for implementers 
who wish to extend GeoAPI with their
-      * own set of interfaces.
++     * Returns the GeoAPI interface that defines the contract of this 
implementation class.
++     * This is the base type required by {@code equals(…)} methods for 
returning a potentially {@code true} value.
 +     *
 +     * @return {@code UserDefinedCS.class} or a user-defined sub-interface.
 +     */
 +    @Override
-     public Class<? extends UserDefinedCS> getInterface() {
++    public Class<? extends UserDefinedCS> getStandardType() {
 +        return UserDefinedCS.class;
 +    }
  
  
  
diff --cc 
endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java
index 49ba178ccf,088f11239a..838108dad8
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/AbstractCoordinateOperation.java
@@@ -468,19 -466,8 +469,19 @@@ check:      for (int isTarget=0; ; isTa
          return new AbstractCoordinateOperation(object);
      }
  
 +    /**
 +     * Returns {@code true} if the given operation is a single operation but 
not a pass-through operation.
 +     * In an older ISO 19111 model, {@link PassThroughOperation} extended 
{@link SingleOperation}, which
 +     * was a problem for providing a value to the inherited {@link 
SingleOperation#getMethod()} method.
 +     * This has been fixed in newer ISO 19111 model, but for safety with 
objects following the older model
 +     * (e.g. GeoAPI 3.0) we are better to perform an explicit exclusion of 
{@link PassThroughOperation}.
 +     */
 +    static boolean isSingleOperation(final CoordinateOperation operation) {
 +        return (operation instanceof SingleOperation) && !(operation 
instanceof PassThroughOperation);
 +    }
 +
      /**
-      * Returns the GeoAPI interface implemented by this class.
+      * Returns the GeoAPI interface that defines the contract of this 
implementation class.
       * The default implementation returns {@code CoordinateOperation.class}.
       * Subclasses implementing a more specific GeoAPI interface shall 
override this method.
       *
diff --cc 
endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefaultProjection.java
index fd914eddcc,0000000000..126cd4204f
mode 100644,000000..100644
--- 
a/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefaultProjection.java
+++ 
b/endorsed/src/org.apache.sis.referencing/main/org/apache/sis/referencing/operation/DefaultProjection.java
@@@ -1,110 -1,0 +1,110 @@@
 +/*
 + * 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.referencing.operation;
 +
 +import java.util.Map;
 +import jakarta.xml.bind.annotation.XmlTransient;
 +import org.opengis.util.FactoryException;
 +import org.opengis.referencing.operation.Conversion;
 +import org.opengis.referencing.operation.Projection;
 +import org.opengis.referencing.operation.OperationMethod;
 +import org.opengis.referencing.operation.MathTransform;
 +import org.opengis.referencing.operation.MathTransformFactory;
 +import org.opengis.referencing.crs.ProjectedCRS;
 +import org.opengis.referencing.crs.GeographicCRS;
 +import org.opengis.referencing.crs.CoordinateReferenceSystem;
 +import org.apache.sis.util.ArgumentChecks;
 +
 +
 +/**
 + * A conversion from (<var>longitude</var>, <var>latitude</var>) coordinates 
to Cartesian coordinates
 + * (<var>x</var>,<var>y</var>).
 + *
 + * @author  Martin Desruisseaux (IRD, Geomatys)
 + *
 + * @see org.apache.sis.referencing.crs.DefaultProjectedCRS
 + */
 +@XmlTransient
 +final class DefaultProjection extends DefaultConversion implements Projection 
{
 +    /**
 +     * Serial number for inter-operability with different versions.
 +     */
 +    private static final long serialVersionUID = -7176751851369816864L;
 +
 +    /**
 +     * Creates a projection from the given properties.
 +     *
 +     * @param  properties  the properties to be given to the identified 
object.
 +     * @param  sourceCRS   the source CRS.
 +     * @param  targetCRS   the target CRS.
 +     * @param  method      the coordinate operation method.
 +     * @param  transform   transform from positions in the source CRS to 
positions in the target CRS.
 +     */
 +    public DefaultProjection(final Map<String,?>   properties,
 +                             final GeographicCRS   sourceCRS,
 +                             final ProjectedCRS    targetCRS,
 +                             final OperationMethod method,
 +                             final MathTransform   transform)
 +    {
 +        super(properties, sourceCRS, targetCRS, null, method, transform);
 +    }
 +
 +    /**
 +     * Creates a new projection with the same values as the specified one, 
together with the
 +     * specified source and target CRS. While the source conversion can be an 
arbitrary one,
 +     * it is typically a defining conversion.
 +     *
 +     * @param  definition  the defining conversion.
 +     * @param  sourceCRS   the source CRS.
 +     * @param  targetCRS   the target CRS.
 +     * @param  factory     the factory to use for creating a transform from 
the parameters or for performing axis changes.
 +     * @throws IllegalArgumentException if the source or targe CRS is not of 
the expected types.
 +     */
 +    DefaultProjection(final Conversion definition,
 +                      final boolean normalized,
 +                      final CoordinateReferenceSystem sourceCRS,
 +                      final CoordinateReferenceSystem targetCRS,
 +                      final MathTransformFactory factory) throws 
FactoryException
 +    {
 +        super(definition, normalized, sourceCRS, targetCRS, factory);
 +        ArgumentChecks.ensureCanCast("sourceCRS", GeographicCRS.class, 
sourceCRS);
 +        ArgumentChecks.ensureCanCast("targetCRS", ProjectedCRS .class, 
targetCRS);
 +    }
 +
 +    /**
 +     * Creates a new coordinate operation with the same values as the 
specified one.
 +     * This copy constructor provides a way to convert an arbitrary 
implementation
 +     * into a SIS one, usually in order to leverage some 
implementation-specific API.
 +     *
 +     * <p>This constructor performs a shallow copy, i.e. the properties are 
not cloned.</p>
 +     *
 +     * @param  operation  the coordinate operation to copy.
 +     */
 +    protected DefaultProjection(final Projection operation) {
 +        super(operation);
 +    }
 +
 +    /**
 +     * Returns the GeoAPI interface implemented by this class.
 +     *
 +     * @return the conversion interface implemented by this class.
 +     */
 +    @Override
-     public Class<? extends Projection> getInterface() {
++    public Class<? extends Projection> getStandardType() {
 +        return Projection.class;
 +    }
 +}
diff --cc 
endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/URIDataStoreOption.java
index 0000000000,2a11917c63..186806e78b
mode 000000,100644..100644
--- 
a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/URIDataStoreOption.java
+++ 
b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/URIDataStoreOption.java
@@@ -1,0 -1,333 +1,333 @@@
+ /*
+  * 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.storage.base;
+ 
+ import java.net.URI;
+ import java.nio.file.StandardOpenOption;
+ import java.util.EnumSet;
+ import java.util.Collection;
+ import org.opengis.parameter.ParameterValueGroup;
+ import org.opengis.parameter.ParameterDescriptor;
+ import org.opengis.parameter.ParameterDescriptorGroup;
+ import org.opengis.parameter.ParameterNotFoundException;
+ import org.apache.sis.parameter.ParameterBuilder;
+ import org.apache.sis.storage.DataStoreProvider;
+ import org.apache.sis.storage.OptionKey;
+ import org.apache.sis.storage.StorageConnector;
+ import org.apache.sis.storage.IllegalOpenParameterException;
+ import org.apache.sis.storage.internal.Resources;
+ import org.apache.sis.util.ObjectConverters;
+ import org.apache.sis.util.UnconvertibleObjectException;
+ import org.apache.sis.util.resources.Errors;
+ import org.apache.sis.util.logging.Logging;
+ 
+ 
+ /**
+  * Options supported by {@link URIDataStoreProvider}.
+  * This enumeration does the link between {@link ParameterDescriptor} and 
{@link OptionKey}.
+  * This enumeration contains all parameters used by all subclasses of {@link 
URIDataStore}
+  * in the Apache <abbr>SIS</abbr> code base. This is an enumeration for a 
closed universe,
+  * which is why it cannot be in exported <abbr>API</abbr>.
+  * This enumeration is useful for data stores that are containers of other 
data stores,
+  * such as the data store that read all files in a folder.
+  *
+  * <h4>Deferred construction of parameter descriptor</h4>
+  * Some parameters are used by only one {@link URIDataStore}. These 
parameters are constructed
+  * during the static initialization of the {@link URIDataStoreProvider} that 
use them.
+  *
+  * <h4>Future evolution</h4>
+  * This class is an attempt to bring a little bit of order in the mapping 
between parameters and option keys,
+  * at least for the most frequently used parameters. This class may change in 
any future <abbr>SIS</abbr>
+  * version according experience.
+  *
+  * @author  Johann Sorel (Geomatys)
+  * @author  Martin Desruisseaux (Geomatys)
+  */
+ public enum URIDataStoreOption {
+     /**
+      * Description of the mandatory {@value URIDataStoreProvider#LOCATION} 
parameter.
+      * This is the only mandatory parameter in all {@link 
URIDataStoreProvider} implementations.
+      *
+      * @see URIDataStoreProvider#LOCATION
+      * @see #createForLocationOnly(String)
+      */
+     LOCATION(URIDataStoreProvider.LOCATION, Resources.Keys.DataStoreLocation, 
URI.class),
+ 
+     /**
+      * Description of the optional {@value URIDataStoreProvider#FORMAT} 
parameter.
+      *
+      * @see URIDataStoreProvider#FORMAT
+      */
+     FORMAT(URIDataStoreProvider.FORMAT, 
Resources.Keys.DirectoryContentFormatName, String.class),
+ 
+     /**
+      * Description of the optional {@value URIDataStoreProvider#CREATE} 
parameter of writable data stores.
+      *
+      * @see URIDataStoreProvider#CREATE
+      * @see OptionKey#OPEN_OPTIONS
+      */
+     CREATE(URIDataStoreProvider.CREATE, Resources.Keys.DataStoreCreate, 
Boolean.class),
+ 
+     /**
+      * Description of the optional parameter for character encoding used by 
the data store.
+      * This is used only when the data store does not have explicit encoding 
information.
+      *
+      * @see OptionKey#ENCODING
+      */
+     ENCODING(OptionKey.ENCODING),
+ 
+     /**
+      * Description of the parameter for formatting conventions of dates and 
numbers.
+      * This is used only when the data store does not have explicit locale 
information.
+      *
+      * @see OptionKey#LOCALE
+      */
+     LOCALE(OptionKey.LOCALE),
+ 
+     /**
+      * Description of the optional parameter for the time zone used by the 
data store.
+      * This is used only when the data store does not have explicit time zone 
information.
+      *
+      * @see OptionKey#TIMEZONE
+      */
+     TIMEZONE(OptionKey.TIMEZONE),
+ 
+     /**
+      * Description of the optional parameter for the default coordinate 
reference system.
+      * This is used only when the data store does not have explicit 
<abbr>CRS</abbr> information.
+      *
+      * @see OptionKey#DEFAULT_CRS
+      * @todo Use {@link CodeType} when parsing from text.
+      */
+     DEFAULT_CRS(OptionKey.DEFAULT_CRS),
+ 
+     /**
+      * Description of the optional "metadata" parameter.
+      *
+      * @see OptionKey#METADATA_PATH
+      */
+     METADATA(OptionKey.METADATA_PATH),
+ 
+     /**
+      * Description of the optional parameter for specifying whether to 
assemble
+      * distinct lines into a single {@code Feature} instance forming a 
foliation.
+      */
+     FOLIATION(OptionKey.FOLIATION_REPRESENTATION);
+ 
+     /**
+      * The option key for storing the parameter value in a storage connector,
+      * or {@code null} if none.
+      */
+     private final OptionKey<?> option;
+ 
+     /**
+      * The parameter descriptor for this option, or {@code null} if not yet 
constructed.
+      *
+      * @see #getParameterDescriptor()
+      */
+     private volatile ParameterDescriptor<?> parameter;
+ 
+     /**
+      * Creates a new enumeration value with deferred construction of the 
parameter descriptor.
+      *
+      * @param option  associated option in {@link StorageConnector}.
+      */
+     private URIDataStoreOption(final OptionKey<?> option) {
+         this.option = option;
+     }
+ 
+     /**
+      * Creates a new enumeration value with immediate construction of the 
parameter descriptor.
+      *
+      * @param parameterName  name of the parameter to create.
+      * @param description    constant from {@link Resources} keys for the 
localized description.
+      * @param valueClass     type of value of the parameter.
+      */
+     private <V> URIDataStoreOption(final String parameterName, final short 
description, final Class<V> valueClass) {
+         option = null;
+         parameter = new ParameterBuilder()
+                 .addName(parameterName)
+                 .setDescription(Resources.formatInternational(description))
+                 .setRequired(ordinal() == 0)
+                 .create(valueClass, null);
+     }
+ 
+     /**
+      * Returns the parameter associated to this enumeration.
+      *
+      * @return the parameter (never {@code null}).
+      * @throws IllegalStateException if the parameter has not yet been 
initialized.
+      */
+     public final ParameterDescriptor<?> getParameterDescriptor() {
+         ParameterDescriptor<?> p = parameter;
+         if (p == null) {
+             p = option.asOpenParameter().orElseThrow(() -> new 
IllegalStateException(name()));
+         }
+         return p;
+     }
+ 
+     /**
+      * Returns the parameter name.
+      */
+     private String parameterName() {
+         return getParameterDescriptor().getName().getCode();
+     }
+ 
+     /**
+      * Creates a parameter with the same name as this parameter but different 
type or remarks.
+      *
+      * @param  builder     the builder to use for creating the parameter.
+      * @param  valueClass  the new value class, or {@code null} for keeping 
the same class.
+      * @param  remarks     the new parameter remarks, or {@code null} for 
keeping the same remarks.
+      * @return parameter of the same name as {@link #parameter()} but the 
specified value class and remarks.
+      */
+     public final ParameterDescriptor<?> deriveParameterDescriptor(
+             final ParameterBuilder builder, Class<?> valueClass, CharSequence 
remarks)
+     {
+         ParameterDescriptor<?> p = getParameterDescriptor();
+         if (valueClass == null) valueClass = p.getValueClass();
 -        if (remarks    == null) remarks    = p.getRemarks().orElse(null);
++        if (remarks    == null) remarks    = p.getRemarks();
+         return builder.addName(p.getName())
+                 .setDescription(p.getDescription().orElse(null))
+                 .setRemarks(remarks)
+                 .create(valueClass, null);
+     }
+ 
+     /**
+      * Creates a parameter descriptor group containing only the mandatory 
parameters.
+      * The mandatory parameter is: {@link #LOCATION}.
+      *
+      * @param  name  short name of the data store format.
+      * @return the descriptor for open parameters with only mandatory 
parameters.
+      *
+      * @see URIDataStoreProvider#createOpenParameters(ParameterBuilder)
+      */
+     public static ParameterDescriptorGroup createForLocationOnly(final String 
name) {
+         return new 
ParameterBuilder().addName(name).createGroup(LOCATION.getParameterDescriptor());
+     }
+ 
+     /**
+      * Returns a group of parameters set to the given mandatory values.
+      * This is a shortcut when only the mandatory parameters are defined.
+      * If the parameter descriptor group of the given provider contains 
parameters other than location,
+      * those additional parameters are present but without values.
+      *
+      * @param  provider  provider of the data store for which to get the 
parameters, or {@code null}.
+      * @param  location  value of {@link #LOCATION}, or {@code null} if none.
+      * @return the parameters with the given values, or {@code null} if 
{@code provider} or {@code location} is null.
+      */
+     public static ParameterValueGroup createWithLocationOnly(final 
DataStoreProvider provider, final URI location) {
+         if (provider != null && location != null) {
+             final ParameterDescriptorGroup descriptor = 
provider.getOpenParameters();
+             if (descriptor != null) {
+                 final ParameterValueGroup pg = descriptor.createValue();
+                 
pg.parameter(URIDataStoreProvider.LOCATION).setValue(location);
+                 return pg;
+             }
+         }
+         return null;
+     }
+ 
+     /**
+      * Sets the value of the parameter described by this enumeration value.
+      *
+      * @param parameters  the parameters where to set the parameter.
+      * @param value       the value to set, or {@code null} if none.
+      */
+     public final void setValueOf(final ParameterValueGroup parameters, final 
Object value) {
+         if (value != null) {
+             parameters.parameter(parameterName()).setValue(value);
+         }
+     }
+ 
+     /**
+      * Creates a storage connector initialized to the location declared in 
the given parameters.
+      * The {@value #LOCATION} parameter is unconditionally requested. The 
other parameters to be
+      * requested are specified in the {@code options} collection.
+      * Missing optional parameters are ignored.
+      *
+      * @param  provider     the provider for which to create a storage 
connector (for error messages).
+      * @param  parameters   the parameters to use for creating a storage 
connector.
+      * @param  options      options to include, or {@code null} for only the 
mandatory parameters.
+      * @param  openOptions  where to store open options, or {@code null} for 
storing them in the storage connector.
+      * @return the storage connector initialized to the location specified in 
the parameters.
+      * @throws IllegalOpenParameterException if no {@value 
URIDataStoreProvider#LOCATION} parameter has been found.
+      */
+     public static StorageConnector connector(final DataStoreProvider provider,
+                                              final ParameterValueGroup 
parameters,
+                                              Collection<URIDataStoreOption> 
options,
+                                              EnumSet<StandardOpenOption> 
openOptions)
+             throws IllegalOpenParameterException
+     {
+         final StorageConnector connector;
+         try {
+             connector = new 
StorageConnector(parameters.parameter(URIDataStoreProvider.LOCATION).getValue());
+         } catch (ParameterNotFoundException | NullPointerException e) {
+             throw new IllegalOpenParameterException(
+                     Resources.format(Resources.Keys.UndefinedParameter_2,
+                                      provider.getShortName(),
+                                      URIDataStoreProvider.LOCATION), e);
+         }
+         if (options != null) {
+             for (final URIDataStoreOption option : options) {
+                 if (option.option == null) continue;
+                 final Object value;
+                 try {
+                     value = 
parameters.parameter(option.parameterName()).getValue();
+                 } catch (ParameterNotFoundException e) {
+                     Logging.ignorableException(provider.getLogger(), 
URIDataStoreOption.class, "connector", e);
+                     continue;
+                 }
+                 if (value != null) try {
+                     switch (option) {
+                         case CREATE: {
+                             final boolean store = (openOptions == null);
+                             if (store) {
+                                 openOptions = 
EnumSet.of(StandardOpenOption.WRITE);
+                             } else {
+                                 openOptions.add(StandardOpenOption.WRITE);
+                             }
+                             if (ObjectConverters.convert(value, 
Boolean.class)) {
+                                 openOptions.add(StandardOpenOption.CREATE);
+                             }
+                             if (store) {
+                                 connector.setOption(OptionKey.OPEN_OPTIONS, 
openOptions.toArray(StandardOpenOption[]::new));
+                             }
+                             break;
+                         }
+                         default: {
+                             convertAndSet(option.option, value, connector);
+                             break;
+                         }
+                     }
+                 } catch (UnconvertibleObjectException e) {
+                     throw new IllegalOpenParameterException(Errors.format(
+                             Errors.Keys.IllegalOptionValue_2, 
option.parameterName(), value), e);
+                 }
+             }
+         }
+         return connector;
+     }
+ 
+     /**
+      * Set the given option to the given value in the given storage connector.
+      * Defined as a separated method for type-safety.
+      */
+     private static <V> void convertAndSet(final OptionKey<V> option, final 
Object value, final StorageConnector connector) {
+         connector.setOption(option, ObjectConverters.convert(value, 
option.getElementType()));
+     }
+ }

Reply via email to