/*
 * 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.factory;

import java.util.Set;
import java.util.Optional;
import java.util.logging.Logger;
import javax.measure.Unit;
import org.opengis.referencing.*;
import org.opengis.referencing.cs.*;
import org.opengis.referencing.crs.*;
import org.opengis.referencing.datum.*;
import org.opengis.referencing.operation.*;
import org.opengis.metadata.Identifier;
import org.opengis.metadata.extent.Extent;
import org.opengis.metadata.citation.Citation;
import org.opengis.parameter.ParameterDescriptor;
import org.opengis.util.FactoryException;
import org.opengis.util.InternationalString;
import org.apache.sis.metadata.iso.citation.Citations;
import org.apache.sis.system.Loggers;
import org.apache.sis.util.CharSequences;
import org.apache.sis.util.Classes;
import org.apache.sis.util.internal.shared.Constants;
import org.apache.sis.referencing.IdentifiedObjects;
import org.apache.sis.referencing.AbstractIdentifiedObject;
import org.apache.sis.referencing.internal.shared.ReferencingUtilities;
import org.apache.sis.util.iso.AbstractFactory;
import org.apache.sis.util.resources.Errors;

// Specific to the main branch:
import org.apache.sis.referencing.cs.DefaultParametricCS;
import org.apache.sis.referencing.crs.DefaultParametricCRS;
import org.apache.sis.referencing.datum.DefaultDatumEnsemble;
import org.apache.sis.referencing.datum.DefaultParametricDatum;


/**
 * Creates geodetic objects from codes defined by an authority.
 * An <dfn>authority</dfn> is an organization that maintains definitions of authority codes.
 * An <dfn>authority code</dfn> is a compact string defined by an authority to reference a particular spatial reference object.
 * A frequently used set of authority codes is the <a href="https://epsg.org/">EPSG geodetic dataset</a>,
 * a database of coordinate systems and other spatial referencing objects where each object has a code number ID.
 *
 * <div class="note"><b>Example:</b>
 * the EPSG code for a <cite>World Geodetic System 1984</cite> (WGS84) coordinate reference system
 * with latitude and longitude axes is {@code "4326"}.</div>
 *
 * <p>This class defines a default implementation for most methods defined in the {@link DatumAuthorityFactory},
 * {@link CSAuthorityFactory} and {@link CRSAuthorityFactory} interfaces. However, those interfaces do not appear
 * in the {@code implements} clause of this class declaration. This is up to subclasses to decide which interfaces
 * they declare to implement.</p>
 *
 * <p>The default implementation for all {@code createFoo(String)} methods ultimately invokes
 * {@link #createObject(String)}, which may be the only method that a subclass need to override.
 * However, other methods may be overridden as well for better performances.</p>
 *
 * @author  Martin Desruisseaux (IRD, Geomatys)
 * @author  Johann Sorel (Geomatys)
 * @version 1.5
 * @since   0.7
 */
public abstract class GeodeticAuthorityFactory extends AbstractFactory implements AuthorityFactory {
    /**
     * The logger to use for reporting object creations.
     */
    static final Logger LOGGER = Logger.getLogger(Loggers.CRS_FACTORY);

    /**
     * Creates a new authority factory for geodetic objects.
     */
    protected GeodeticAuthorityFactory() {
    }

    /**
     * Returns the database or specification that defines the codes recognized by this factory.
     * This method may return {@code null} if it cannot obtain this information, for example because
     * the connection to a database is not available.
     *
     * <h4>Example</h4>
     * A factory that create coordinate reference system objects from EPSG codes could return a citation like below:
     *
     * <pre class="text">
     *   Citation
     *   ├─ Title ……………………………………………………… EPSG Geodetic Parameter Dataset
     *   ├─ Identifier ………………………………………… EPSG
     *   ├─ Online resource (1 of 2)
     *   │  ├─ Linkage ………………………………………… https://epsg.org/
     *   │  └─ Function ……………………………………… Browse
     *   └─ Online resource (2 of 2)
     *      ├─ Linkage ………………………………………… jdbc:derby:/my/path/to/SIS_DATA/Databases/SpatialMetadata
     *      ├─ Description ……………………………… EPSG dataset version 9.1 on “Apache Derby Embedded JDBC Driver” version 10.14.
     *      └─ Function ……………………………………… Connection</pre>
     *
     * The online resource description with a “Connection” function is a SIS extension.
     *
     * @return the organization responsible for definition of the database, or {@code null} if unknown.
     *
     * @see #getVendor()
     */
    @Override
    public abstract Citation getAuthority();

    /**
     * Returns all namespaces recognized by this factory. Those namespaces can appear before codes in
     * calls to {@code createFoo(String)} methods, for example {@code "EPSG"} in {@code "EPSG:4326"}.
     * Namespaces are case-insensitive.
     *
     * <p>The namespaces are closely related to the {@linkplain #getAuthority() authority}. Often the namespace is
     * the authority {@linkplain org.apache.sis.metadata.iso.citation.DefaultCitation#getIdentifiers() identifier},
     * but not always.</p>
     *
     * <h4>Examples</h4>
     * <ul class="verbose">
     *   <li>The {@link org.apache.sis.referencing.factory.sql.EPSGFactory} authority identifier is {@code "EPSG"}
     *       and its {@code getCodeSpaces()} method returns a set containing {@code "EPSG"}. So in this example,
     *       authority and namespace match. That namespace value means that {@code EPSGFactory.createFoo(String)}
     *       methods accept both {@code "EPSG:4326"} (case-insensitive) and {@code "4326"} codes as argument.</li>
     *
     *   <li>The {@link org.apache.sis.referencing.factory.sql.EPSGDataAccess} authority identifier is {@code "EPSG"}
     *       but its {@code getCodeSpaces()} method returns an empty set. This means that despite the EPSG authority,
     *       {@code EPSGDataAccess.createFoo(String)} methods accept only codes like {@code "4326"} without
     *       {@code "EPSG:"} prefix (the reason is that {@code EPSGDataAccess} is not expected to be used directly).</li>
     *
     *   <li>The {@link CommonAuthorityFactory} authority identifiers are ISO 19128 and OGC 06-042 (the WMS specification)
     *       but its {@code getCodeSpaces()} method returns a set containing {@code "CRS"}, {@code "AUTO"} and {@code "AUTO2"}.
     *       While ISO 19128 is defined as the first authority, the namespace is actually defined by OGC.</li>
     * </ul>
     *
     * <h4>Default implementation</h4>
     * The default implementation infers the namespace from the {@linkplain #getAuthority() authority}.
     * Subclasses can override this method, but the set should always contain the same elements during
     * all factory lifetime.
     *
     * @return the namespaces recognized by this factory, or an empty set if none.
     * @throws FactoryException if an error occurred while listing the code spaces managed by this factory.
     */
    public Set<String> getCodeSpaces() throws FactoryException {
        final String authority = Citations.toCodeSpace(getAuthority());
        return (authority != null) ? Set.of(authority) : Set.of();
    }

    /**
     * Returns a description of the object corresponding to a code.
     * The description can be used for example in a combo box in a graphical user interface.
     *
     * <h4>Default implementation</h4>
     * The default implementation invokes {@link #createObject(Class, String)} for the given class and code,
     * then returns the {@linkplain IdentifiedObjects#getDisplayName(IdentifiedObject) display name} of the object.
     * This implementation may be costly because it involves a full object creation.
     * Subclasses are encouraged to provide a more efficient implementation if they can.
     *
     * @param  type  the type of object for which to get a description.
     * @param  code  value allocated by authority.
     * @return a description of the object, or empty if the object exists but has no description.
     * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
     * @throws FactoryException if an error occurred while fetching the description.
     *
     * @since 1.5
     */
    public Optional<InternationalString> getDescriptionText(Class<? extends IdentifiedObject> type, String code)
            throws FactoryException
    {
        return Optional.ofNullable(IdentifiedObjects.getDisplayName(createObject(type, code)));
    }

    /**
     * Returns a description of the object corresponding to a code.
     *
     * @param  code  value allocated by authority.
     * @return a description of the object, or {@code null} if the object
     *         corresponding to the specified {@code code} has no description.
     * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
     * @throws FactoryException if the query failed for some other reason.
     *
     * @deprecated This method is ambiguous because the EPSG geodetic registry may allocate
     *             the same code to different kinds of object.
     */
    @Override
    @Deprecated(since = "1.5")
    public InternationalString getDescriptionText(final String code) throws FactoryException {
        return getDescriptionText(CoordinateReferenceSystem.class, code).orElse(null);
    }

    /**
     * Returns an object of the specified type from a code. This implementation forwards
     * the method call to the most specialized methods determined by the given type.
     * For example, a call to {@code createObject(GeodeticCRS.class, code)} delegates
     * to <code>{@linkplain #createGeodeticCRS(String) createGeodeticCRS}(code)</code>.
     *
     * @param  <T>   the compile-time value of the {@code type} argument.
     * @param  type  the type of object to create.
     * @param  code  value allocated by authority.
     * @return the object for the given code.
     * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
     * @throws FactoryException if the object creation failed for some other reason.
     *
     * @since 1.5
     */
    public final <T extends IdentifiedObject> T createObject(final Class<T> type, final String code)
            throws NoSuchAuthorityCodeException, FactoryException
    {
        return cast(type, (IdentifiedObject) AuthorityFactoryProxy.getInstance(type).create(this, code), code);
    }

    /**
     * Returns an arbitrary object from a code. The returned object will typically be an instance of {@link Datum},
     * {@link CoordinateSystem}, {@link CoordinateReferenceSystem} or {@link CoordinateOperation}.
     * This method may be used when the type of the object to create is unknown.
     * But it is recommended to invoke the most specific {@code createFoo(String)} method when
     * the desired type is known, both for performance reason and for avoiding ambiguity.
     *
     * <h4>Note for subclasses</h4>
     * In default {@code GeodeticAuthorityFactory} implementation, all {@code createFoo(String)} methods ultimately
     * delegate to this {@code createObject(String)} method and verify if the created object is of the desired type.
     * Overriding this method is sufficient for supporting the more specific {@code createFoo(String)} methods,
     * but subclasses are encouraged to override the latter for efficiency.
     *
     * @param  code  value allocated by authority.
     * @return the object for the given code.
     * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
     * @throws FactoryException if the object creation failed for some other reason.
     *
     * @see org.apache.sis.referencing.AbstractIdentifiedObject
     */
    @Override
    public abstract IdentifiedObject createObject(String code) throws NoSuchAuthorityCodeException, FactoryException;

    /**
     * Creates an arbitrary coordinate reference system from a code.
     * The returned object will typically be an instance of {@link GeographicCRS}, {@link ProjectedCRS},
     * {@link VerticalCRS} or {@link CompoundCRS}.
     * If the coordinate reference system type is known at compile time,
     * it is recommended to invoke the most precise method instead of this one (for example
     * {@link #createGeographicCRS createGeographicCRS(String)} instead of
     * <code>createCoordinateReferenceSystem(code)</code> if the caller know he is asking for a
     * {@linkplain org.apache.sis.referencing.crs.DefaultGeographicCRS geographic coordinate reference system}).
     *
     * <h4>Examples</h4>
     * The {@linkplain #getAuthorityCodes(Class) set of available codes} depends on the defining
     * {@linkplain #getAuthority() authority} and the {@code GeodeticAuthorityFactory} subclass in use.
     * Two frequently used authorities are "CRS" and "EPSG", which include the following codes:
     *
     * <table class="sis">
     * <caption>Authority codes examples</caption>
     *   <tr><th>Code</th>       <th>Type</th>         <th>Description</th></tr>
     *   <tr><td>CRS:84</td>     <td>Geographic</td>   <td>Like EPSG:4326 except for (<var>longitude</var>, <var>latitude</var>) axis order</td></tr>
     *   <tr><td>EPSG:4326</td> <td>Geographic</td>    <td>World Geodetic System 1984</td></tr>
     *   <tr><td>EPSG:4979</td> <td>Geographic 3D</td> <td>World Geodetic System 1984</td></tr>
     *   <tr><td>EPSG:4978</td> <td>Geocentric</td>    <td>World Geodetic System 1984</td></tr>
     *   <tr><td>EPSG:3395</td> <td>Projected</td>     <td>WGS 84 / World Mercator</td></tr>
     *   <tr><td>EPSG:5714</td> <td>Vertical</td>      <td>Mean Sea Level height</td></tr>
     *   <tr><td>EPSG:6349</td> <td>Compound</td>      <td>NAD83(2011) + NAVD88 height</td></tr>
     *   <tr><td>EPSG:5800</td> <td>Engineering</td>   <td>Astra Minas Grid</td></tr>
     * </table>
     *
     * <h4>Default implementation</h4>
     * The default implementation delegates to {@link #createObject(String)} and casts the result.
     * If the result cannot be cast, then a {@link NoSuchAuthorityCodeException} is thrown.
     *
     * @param  code  value allocated by authority.
     * @return the coordinate reference system for the given code.
     * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
     * @throws FactoryException if the object creation failed for some other reason.
     *
     * @see org.apache.sis.referencing.crs.AbstractCRS
     * @see org.apache.sis.referencing.CRS#forCode(String)
     */
    public CoordinateReferenceSystem createCoordinateReferenceSystem(final String code)
            throws NoSuchAuthorityCodeException, FactoryException
    {
        return cast(CoordinateReferenceSystem.class, createObject(code), code);
    }

    /**
     * Creates a 2- or 3-dimensional coordinate reference system based on an ellipsoidal approximation of the geoid.
     * This provides an accurate representation of the geometry of geographic features
     * for a large portion of the earth's surface.
     *
     * <h4>Examples</h4>
     * The {@linkplain #getAuthorityCodes(Class) set of available codes} depends on the defining
     * {@linkplain #getAuthority() authority} and the {@code GeodeticAuthorityFactory} subclass in use.
     * Two frequently used authorities are "CRS" and "EPSG", which include the following codes:
     *
     * <table class="sis">
     * <caption>Authority codes examples</caption>
     *   <tr><th>Code</th>      <th>Type</th>          <th>Description</th></tr>
     *   <tr><td>CRS:27</td>    <td>Geographic</td>    <td>Like EPSG:4267 except for (<var>longitude</var>, <var>latitude</var>) axis order</td></tr>
     *   <tr><td>CRS:83</td>    <td>Geographic</td>    <td>Like EPSG:4269 except for (<var>longitude</var>, <var>latitude</var>) axis order</td></tr>
     *   <tr><td>CRS:84</td>    <td>Geographic</td>    <td>Like EPSG:4326 except for (<var>longitude</var>, <var>latitude</var>) axis order</td></tr>
     *   <tr><td>EPSG:4322</td> <td>Geographic</td>    <td>World Geodetic System 1972</td></tr>
     *   <tr><td>EPSG:4985</td> <td>Geographic 3D</td> <td>World Geodetic System 1972</td></tr>
     *   <tr><td>EPSG:4326</td> <td>Geographic</td>    <td>World Geodetic System 1984</td></tr>
     *   <tr><td>EPSG:4979</td> <td>Geographic 3D</td> <td>World Geodetic System 1984</td></tr>
     *   <tr><td>EPSG:4267</td> <td>Geographic</td>    <td>North American Datum 1927</td></tr>
     *   <tr><td>EPSG:4269</td> <td>Geographic</td>    <td>North American Datum 1983</td></tr>
     *   <tr><td>EPSG:4230</td> <td>Geographic</td>    <td>European Datum 1950</td></tr>
     *   <tr><td>EPSG:4258</td> <td>Geographic</td>    <td>European Terrestrial Reference Frame 1989</td></tr>
     *   <tr><td>EPSG:4937</td> <td>Geographic 3D</td> <td>European Terrestrial Reference Frame 1989</td></tr>
     * </table>
     *
     * <h4>Default implementation</h4>
     * The default implementation delegates to {@link #createCoordinateReferenceSystem(String)} and casts the result.
     * If the result cannot be cast, then a {@link NoSuchAuthorityCodeException} is thrown.
     *
     * @param  code  value allocated by authority.
     * @return the coordinate reference system for the given code.
     * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
     * @throws FactoryException if the object creation failed for some other reason.
     *
     * @see org.apache.sis.referencing.crs.DefaultGeographicCRS
     * @see org.apache.sis.referencing.CommonCRS#geographic()
     * @see org.apache.sis.referencing.CommonCRS#geographic3D()
     */
    public GeographicCRS createGeographicCRS(final String code) throws NoSuchAuthorityCodeException, FactoryException {
        return cast(GeographicCRS.class, createCoordinateReferenceSystem(code), code);
    }

    /**
     * Creates a 3-dimensional coordinate reference system with the origin at the approximate centre of mass of the earth.
     * A geocentric CRS deals with the earth's curvature by taking a 3-dimensional spatial view, which obviates
     * the need to model the earth's curvature.
     *
     * <h4>Examples</h4>
     * The {@linkplain #getAuthorityCodes(Class) set of available codes} depends on the defining
     * {@linkplain #getAuthority() authority} and the {@code GeodeticAuthorityFactory} subclass in use.
     * A frequently used authority is "EPSG", which includes the following codes:
     *
     * <table class="sis">
     * <caption>Authority codes examples</caption>
     *   <tr><th>Code</th>      <th>Description</th></tr>
     *   <tr><td>EPSG:4936</td> <td>European Terrestrial Reference Frame 1989</td></tr>
     *   <tr><td>EPSG:4978</td> <td>World Geodetic System 1984</td></tr>
     *   <tr><td>EPSG:4984</td> <td>World Geodetic System 1972</td></tr>
     * </table>
     *
     * <div class="warning"><b>Warning:</b> In a future SIS version, the return type may be changed to the
     * {@link GeodeticCRS} parent interface. This is because ISO 19111 does not defines specific interface
     * for the geocentric case. Users should assign the return value to a {@code GeodeticCRS} type.</div>
     *
     * <h4>Default implementation</h4>
     * The default implementation delegates to {@link #createCoordinateReferenceSystem(String)} and casts the result.
     * If the result cannot be cast, then a {@link NoSuchAuthorityCodeException} is thrown.
     *
     * @param  code  value allocated by authority.
     * @return the coordinate reference system for the given code.
     * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
     * @throws FactoryException if the object creation failed.
     *
     * @see org.apache.sis.referencing.crs.DefaultGeocentricCRS
     * @see org.apache.sis.referencing.CommonCRS#geocentric()
     */
    public GeocentricCRS createGeocentricCRS(final String code) throws NoSuchAuthorityCodeException, FactoryException {
        return cast(GeocentricCRS.class, createCoordinateReferenceSystem(code), code);
    }

    /**
     * Creates a 2-dimensional coordinate reference system used to approximate the shape of the earth on a planar surface.
     * It is done in such a way that the distortion that is inherent to the approximation is carefully controlled and known.
     * Distortion correction is commonly applied to calculated bearings and distances to produce values
     * that are a close match to actual field values.
     *
     * <h4>Examples</h4>
     * The {@linkplain #getAuthorityCodes(Class) set of available codes} depends on the defining
     * {@linkplain #getAuthority() authority} and the {@code GeodeticAuthorityFactory} subclass in use.
     * A frequently used authority is "EPSG", which contains more than 4000 codes for projected CRS.
     * Some of them are:
     *
     * <table class="sis">
     * <caption>Authority codes examples</caption>
     *   <tr><th>Code</th>      <th>Description</th></tr>
     *   <tr><td>EPSG:3034</td> <td>ETRS89 / Lambert Conic Conformal Europe</td></tr>
     *   <tr><td>EPSG:3395</td> <td>WGS 84 / World Mercator</td></tr>
     *   <tr><td>EPSG:6350</td> <td>NAD83(2011) / Conus Albers Equal Area</td></tr>
     * </table>
     *
     * <h4>Default implementation</h4>
     * The default implementation delegates to {@link #createCoordinateReferenceSystem(String)} and casts the result.
     * If the result cannot be cast, then a {@link NoSuchAuthorityCodeException} is thrown.
     *
     * @param  code  value allocated by authority.
     * @return the coordinate reference system for the given code.
     * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
     * @throws FactoryException if the object creation failed for some other reason.
     *
     * @see org.apache.sis.referencing.crs.DefaultProjectedCRS
     */
    public ProjectedCRS createProjectedCRS(final String code) throws NoSuchAuthorityCodeException, FactoryException {
        return cast(ProjectedCRS.class, createCoordinateReferenceSystem(code), code);
    }

    /**
     * Creates a 1-dimensional coordinate reference system used for recording heights or depths.
     * Vertical CRSs make use of the direction of gravity to define the concept of height or depth,
     * but the relationship with gravity may not be straightforward.
     *
     * <h4>Examples</h4>
     * The {@linkplain #getAuthorityCodes(Class) set of available codes} depends on the defining
     * {@linkplain #getAuthority() authority} and the {@code GeodeticAuthorityFactory} subclass in use.
     * A frequently used authority is "EPSG", which includes the following codes:
     *
     * <table class="sis">
     * <caption>Authority codes examples</caption>
     *   <tr><th>Code</th>      <th>Description</th></tr>
     *   <tr><td>EPSG:5715</td> <td>Mean Sea Level depth</td></tr>
     *   <tr><td>EPSG:5714</td> <td>Mean Sea Level height</td></tr>
     * </table>
     *
     * <h4>Default implementation</h4>
     * The default implementation delegates to {@link #createCoordinateReferenceSystem(String)} and casts the result.
     * If the result cannot be cast, then a {@link NoSuchAuthorityCodeException} is thrown.
     *
     * @param  code  value allocated by authority.
     * @return the coordinate reference system for the given code.
     * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
     * @throws FactoryException if the object creation failed for some other reason.
     *
     * @see org.apache.sis.referencing.crs.DefaultVerticalCRS
     * @see org.apache.sis.referencing.CommonCRS.Vertical#crs()
     */
    public VerticalCRS createVerticalCRS(final String code) throws NoSuchAuthorityCodeException, FactoryException {
        return cast(VerticalCRS.class, createCoordinateReferenceSystem(code), code);
    }

    /**
     * Creates a 1-dimensional coordinate reference system used for the recording of time.
     *
     * <h4>Default implementation</h4>
     * The default implementation delegates to {@link #createCoordinateReferenceSystem(String)} and casts the result.
     * If the result cannot be cast, then a {@link NoSuchAuthorityCodeException} is thrown.
     *
     * @param  code  value allocated by authority.
     * @return the coordinate reference system for the given code.
     * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
     * @throws FactoryException if the object creation failed for some other reason.
     *
     * @see org.apache.sis.referencing.crs.DefaultTemporalCRS
     * @see org.apache.sis.referencing.CommonCRS.Temporal#crs()
     */
    public TemporalCRS createTemporalCRS(final String code) throws NoSuchAuthorityCodeException, FactoryException {
        return cast(TemporalCRS.class, createCoordinateReferenceSystem(code), code);
    }

    /**
     * Creates a 1-dimensional parametric coordinate reference system.
     *
     * <h4>Default implementation</h4>
     * The default implementation delegates to {@link #createCoordinateReferenceSystem(String)} and casts the result.
     * If the result cannot be cast, then a {@link NoSuchAuthorityCodeException} is thrown.
     *
     * <div class="warning"><b>Warning:</b> in a future SIS version, the return type may be changed
     * to {@code org.opengis.referencing.crs.ParametricCRS}. This change is pending GeoAPI revision.</div>
     *
     * @param  code  value allocated by authority.
     * @return the coordinate reference system for the given code.
     * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
     * @throws FactoryException if the object creation failed for some other reason.
     *
     * @see org.apache.sis.referencing.crs.DefaultParametricCRS
     */
    public DefaultParametricCRS createParametricCRS(final String code) throws NoSuchAuthorityCodeException, FactoryException {
        return cast(DefaultParametricCRS.class, createCoordinateReferenceSystem(code), code);
    }

    /**
     * Creates a CRS describing the position of points through two or more independent coordinate reference systems.
     *
     * <h4>Examples</h4>
     * The {@linkplain #getAuthorityCodes(Class) set of available codes} depends on the defining
     * {@linkplain #getAuthority() authority} and the {@code GeodeticAuthorityFactory} subclass in use.
     * A frequently used authority is "EPSG", which includes the following codes:
     *
     * <table class="sis">
     * <caption>Authority codes examples</caption>
     *   <tr><th>Code</th>      <th>Description</th></tr>
     *   <tr><td>EPSG:6349</td> <td>NAD83(2011) + NAVD88 height</td></tr>
     *   <tr><td>EPSG:7423</td> <td>ETRS89 + EVRF2007 height</td></tr>
     * </table>
     *
     * <h4>Default implementation</h4>
     * The default implementation delegates to {@link #createCoordinateReferenceSystem(String)} and casts the result.
     * If the result cannot be cast, then a {@link NoSuchAuthorityCodeException} is thrown.
     *
     * @param  code  value allocated by authority.
     * @return the coordinate reference system for the given code.
     * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
     * @throws FactoryException if the object creation failed for some other reason.
     *
     * @see org.apache.sis.referencing.crs.DefaultCompoundCRS
     */
    public CompoundCRS createCompoundCRS(final String code) throws NoSuchAuthorityCodeException, FactoryException {
        return cast(CompoundCRS.class, createCoordinateReferenceSystem(code), code);
    }

    /**
     * Creates a CRS that is defined by its coordinate conversion from another CRS (not by a datum).
     * {@code DerivedCRS} cannot be {@code ProjectedCRS} themselves, but may be derived from a projected CRS.
     *
     * <h4>Default implementation</h4>
     * The default implementation delegates to {@link #createCoordinateReferenceSystem(String)} and casts the result.
     * If the result cannot be cast, then a {@link NoSuchAuthorityCodeException} is thrown.
     *
     * @param  code  value allocated by authority.
     * @return the coordinate reference system for the given code.
     * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
     * @throws FactoryException if the object creation failed for some other reason.
     *
     * @see org.apache.sis.referencing.crs.DefaultDerivedCRS
     */
    public DerivedCRS createDerivedCRS(final String code) throws NoSuchAuthorityCodeException, FactoryException {
        return cast(DerivedCRS.class, createCoordinateReferenceSystem(code), code);
    }

    /**
     * Creates a 1-, 2- or 3-dimensional contextually local coordinate reference system.
     *
     * <h4>Examples</h4>
     * The {@linkplain #getAuthorityCodes(Class) set of available codes} depends on the defining
     * {@linkplain #getAuthority() authority} and the {@code GeodeticAuthorityFactory} subclass in use.
     * A frequently used authority is "EPSG", which includes the following codes:
     *
     * <table class="sis">
     * <caption>Authority codes examples</caption>
     *   <tr><th>Code</th>      <th>Description</th></tr>
     *   <tr><td>EPSG:5800</td> <td>Astra Minas Grid</td></tr>
     * </table>
     *
     * <h4>Default implementation</h4>
     * The default implementation delegates to {@link #createCoordinateReferenceSystem(String)} and casts the result.
     * If the result cannot be cast, then a {@link NoSuchAuthorityCodeException} is thrown.
     *
     * @param  code  value allocated by authority.
     * @return the coordinate reference system for the given code.
     * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
     * @throws FactoryException if the object creation failed for some other reason.
     *
     * @see org.apache.sis.referencing.crs.DefaultEngineeringCRS
     */
    public EngineeringCRS createEngineeringCRS(final String code) throws NoSuchAuthorityCodeException, FactoryException {
        return cast(EngineeringCRS.class, createCoordinateReferenceSystem(code), code);
    }

    /**
     * Creates a 2-dimensional engineering coordinate reference system applied to locations in images.
     * Image coordinate reference systems are treated as a separate sub-type because a separate
     * user community exists for images with its own terms of reference.
     *
     * <h4>Default implementation</h4>
     * The default implementation delegates to {@link #createCoordinateReferenceSystem(String)} and casts the result.
     * If the result cannot be cast, then a {@link NoSuchAuthorityCodeException} is thrown.
     *
     * @param  code  value allocated by authority.
     * @return the coordinate reference system for the given code.
     * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
     * @throws FactoryException if the object creation failed for some other reason.
     *
     * @see org.apache.sis.referencing.crs.DefaultImageCRS
     *
     * @deprecated The {@code ImageCRS} class has been removed in ISO 19111:2019.
     *             It is replaced by {@code EngineeringCRS}.
     */
    @Deprecated(since = "1.5")
    public ImageCRS createImageCRS(final String code) throws NoSuchAuthorityCodeException, FactoryException {
        return cast(ImageCRS.class, createCoordinateReferenceSystem(code), code);
    }

    /**
     * Creates an arbitrary datum ensemble from a code.
     * A datum ensemble is a collection of datums which for low accuracy requirements
     * may be considered to be insignificantly different from each other.
     *
     * <h4>Examples</h4>
     * The {@linkplain #getAuthorityCodes(Class) set of available codes} depends on the defining
     * {@linkplain #getAuthority() authority} and the {@code GeodeticAuthorityFactory} subclass in use.
     * A frequently used authority is "EPSG", which includes the following codes:
     *
     * <table class="sis">
     * <caption>Authority codes examples</caption>
     *   <tr><th>Code</th>      <th>Description</th></tr>
     *   <tr><td>EPSG:6326</td> <td>World Geodetic System 1984</td></tr>
     *   <tr><td>EPSG:6258</td> <td>European Terrestrial Reference System 1989</td></tr>
     * </table>
     *
     * <h4>Default implementation</h4>
     * The default implementation delegates to {@link #createDatum(String)} and casts the result.
     * If the result cannot be cast, then a {@link NoSuchAuthorityCodeException} is thrown.
     * This approach assumes that the datum ensemble implements also the {@link Datum} interface.
     * It is the case of the {@link org.apache.sis.referencing.datum.DefaultDatumEnsemble} class.
     *
     * <p>This default implementation is unusual, but is convenient for the implementation strategy
     * of Apache <abbr>SIS</abbr> and for the structure of the <abbr>EPSG</abbr> geodetic dataset,
     * which uses the same {@code "Datum"} table for storing the properties of the two kinds of object.</p>
     *
     * <div class="warning"><b>Upcoming API change — generalization</b><br>
     * The element type will be changed to the {@code DatumEnsemble} interface
     * when GeoAPI will provide it (tentatively in GeoAPI 3.1).</div>
     *
     * @param  code  value allocated by authority.
     * @return the datum ensemble for the given code.
     * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
     * @throws FactoryException if the object creation failed for some other reason.
     *
     * @see org.apache.sis.referencing.datum.DefaultDatumEnsemble
     *
     * @since 1.5
     */
    public DefaultDatumEnsemble<?> createDatumEnsemble(final String code) throws NoSuchAuthorityCodeException, FactoryException {
        return cast(DefaultDatumEnsemble.class, createDatum(code), code);
    }

    /**
     * Creates an arbitrary datum from a code. The returned object will typically be an
     * instance of {@link GeodeticDatum}, {@link VerticalDatum} or {@link TemporalDatum}.
     * If the datum is known at compile time, it is recommended to invoke the most precise method instead of this one.
     *
     * <h4>Examples</h4>
     * The {@linkplain #getAuthorityCodes(Class) set of available codes} depends on the defining
     * {@linkplain #getAuthority() authority} and the {@code GeodeticAuthorityFactory} subclass in use.
     * A frequently used authority is "EPSG", which contains hundred of datum. Some of them are:
     *
     * <table class="sis">
     * <caption>Authority codes examples</caption>
     *   <tr><th>Code</th>      <th>Type</th>        <th>Description</th></tr>
     *   <tr><td>EPSG:6326</td> <td>Ensemble</td>    <td>World Geodetic System 1984</td></tr>
     *   <tr><td>EPSG:6322</td> <td>Geodetic</td>    <td>World Geodetic System 1972</td></tr>
     *   <tr><td>EPSG:1027</td> <td>Vertical</td>    <td>EGM2008 geoid</td></tr>
     *   <tr><td>EPSG:5100</td> <td>Vertical</td>    <td>Mean Sea Level</td></tr>
     *   <tr><td>EPSG:9315</td> <td>Engineering</td> <td>Seismic bin grid datum</td></tr>
     * </table>
     *
     * <p><b>Note:</b> strictly speaking, the reference frames of type <i>ensemble</i> cannot be returned by this method.
     * They should be obtained by the {@link #createDatumEnsemble(String)} method instead. In the particular case of the
     * Apache <abbr>SIS</abbr> implementation, datum ensembles can nevertheless be obtained by this method, but this is
     * not guaranteed to be true for all implementations of the {@link DatumAuthorityFactory} interface.</p>
     *
     * <h4>Default implementation</h4>
     * The default implementation delegates to {@link #createObject(String)} and casts the result.
     * If the result cannot be cast, then a {@link NoSuchAuthorityCodeException} is thrown.
     *
     * @param  code  value allocated by authority.
     * @return the datum for the given code.
     * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
     * @throws FactoryException if the object creation failed for some other reason.
     *
     * @see org.apache.sis.referencing.datum.AbstractDatum
     */
    public Datum createDatum(final String code) throws NoSuchAuthorityCodeException, FactoryException {
        return cast(Datum.class, createObject(code), code);
    }

    /**
     * Creates a datum defining the location and orientation of an ellipsoid that approximates the shape of the earth.
     * Geodetic reference frame are used together with ellipsoidal coordinate system,
     * and also with Cartesian coordinate system centered in the ellipsoid (or sphere).
     *
     * <h4>Examples</h4>
     * The {@linkplain #getAuthorityCodes(Class) set of available codes} depends on the defining
     * {@linkplain #getAuthority() authority} and the {@code GeodeticAuthorityFactory} subclass in use.
     * A frequently used authority is "EPSG", which contains hundred of datum. Some of them are:
     *
     * <table class="sis">
     * <caption>Authority codes examples</caption>
     *   <tr><th>Code</th>      <th>Type</th>     <th>Description</th></tr>
     *   <tr><td>EPSG:6326</td> <td>Ensemble</td> <td>World Geodetic System 1984</td></tr>
     *   <tr><td>EPSG:6322</td> <td>Dynamic</td>  <td>World Geodetic System 1972</td></tr>
     *   <tr><td>EPSG:6269</td> <td></td>         <td>North American Datum 1983</td></tr>
     *   <tr><td>EPSG:6258</td> <td>Ensemble</td> <td>European Terrestrial Reference System 1989</td></tr>
     * </table>
     *
     * <p><b>Note:</b> strictly speaking, the reference frames of type <i>ensemble</i> cannot be returned by this method.
     * They should be obtained by the {@link #createDatumEnsemble(String)} method instead. In the particular case of the
     * Apache <abbr>SIS</abbr> implementation, datum ensembles can nevertheless be obtained by this method, but this is
     * not guaranteed to be true for all implementations of the {@link DatumAuthorityFactory} interface.</p>
     *
     * <h4>Default implementation</h4>
     * The default implementation delegates to {@link #createDatum(String)} and casts the result.
     * If the result cannot be cast, then a {@link NoSuchAuthorityCodeException} is thrown.
     *
     * @param  code  value allocated by authority.
     * @return the datum for the given code.
     * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
     * @throws FactoryException if the object creation failed for some other reason.
     *
     * @see org.apache.sis.referencing.datum.DefaultGeodeticDatum
     * @see org.apache.sis.referencing.CommonCRS#datum(boolean)
     */
    public GeodeticDatum createGeodeticDatum(final String code) throws NoSuchAuthorityCodeException, FactoryException {
        return cast(GeodeticDatum.class, createDatum(code), code);
    }

    /**
     * Creates a datum identifying a particular reference level surface used as a zero-height surface.
     * There are several types of vertical datums, and each may place constraints on the axis with which
     * it is combined to create a vertical CRS.
     *
     * <h4>Examples</h4>
     * The {@linkplain #getAuthorityCodes(Class) set of available codes} depends on the defining
     * {@linkplain #getAuthority() authority} and the {@code GeodeticAuthorityFactory} subclass in use.
     * A frequently used authority is "EPSG", which includes the following codes:
     *
     * <table class="sis">
     * <caption>Authority codes examples</caption>
     *   <tr><th>Code</th>      <th>Description</th></tr>
     *   <tr><td>EPSG:5100</td> <td>Mean Sea Level</td></tr>
     *   <tr><td>EPSG:1027</td> <td>EGM2008 geoid</td></tr>
     *   <tr><td>EPSG:1131</td> <td>Japanese Geodetic Datum 2011 (vertical)</td></tr>
     *   <tr><td>EPSG:5215</td> <td>European Vertical Reference Frame 2007</td></tr>
     * </table>
     *
     * <h4>Default implementation</h4>
     * The default implementation delegates to {@link #createDatum(String)} and casts the result.
     * If the result cannot be cast, then a {@link NoSuchAuthorityCodeException} is thrown.
     *
     * @param  code  value allocated by authority.
     * @return the datum for the given code.
     * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
     * @throws FactoryException if the object creation failed for some other reason.
     *
     * @see org.apache.sis.referencing.datum.DefaultVerticalDatum
     * @see org.apache.sis.referencing.CommonCRS.Vertical#datum()
     */
    public VerticalDatum createVerticalDatum(final String code) throws NoSuchAuthorityCodeException, FactoryException {
        return cast(VerticalDatum.class, createDatum(code), code);
    }

    /**
     * Creates a datum defining the origin of a temporal coordinate reference system.
     *
     * <h4>Default implementation</h4>
     * The default implementation delegates to {@link #createDatum(String)} and casts the result.
     * If the result cannot be cast, then a {@link NoSuchAuthorityCodeException} is thrown.
     *
     * @param  code  value allocated by authority.
     * @return the datum for the given code.
     * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
     * @throws FactoryException if the object creation failed for some other reason.
     *
     * @see org.apache.sis.referencing.datum.DefaultTemporalDatum
     * @see org.apache.sis.referencing.CommonCRS.Temporal#datum()
     */
    public TemporalDatum createTemporalDatum(final String code) throws NoSuchAuthorityCodeException, FactoryException {
        return cast(TemporalDatum.class, createDatum(code), code);
    }

    /**
     * Creates a datum defining the origin of a parametric coordinate reference system.
     *
     * <h4>Default implementation</h4>
     * The default implementation delegates to {@link #createDatum(String)} and casts the result.
     * If the result cannot be cast, then a {@link NoSuchAuthorityCodeException} is thrown.
     *
     * <div class="warning"><b>Warning:</b> in a future SIS version, the return type may be changed
     * to {@code org.opengis.referencing.datum.ParametricDatum}. This change is pending GeoAPI revision.</div>
     *
     * @param  code  value allocated by authority.
     * @return the datum for the given code.
     * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
     * @throws FactoryException if the object creation failed for some other reason.
     *
     * @see org.apache.sis.referencing.datum.DefaultParametricDatum
     */
    public DefaultParametricDatum createParametricDatum(final String code) throws NoSuchAuthorityCodeException, FactoryException {
        return cast(DefaultParametricDatum.class, createDatum(code), code);
    }

    /**
     * Creates a datum defining the origin of an engineering coordinate reference system.
     * An engineering datum is used in a region around that origin.
     * This origin can be fixed with respect to the earth or be a defined point on a moving vehicle.
     *
     * <h4>Examples</h4>
     * The {@linkplain #getAuthorityCodes(Class) set of available codes} depends on the defining
     * {@linkplain #getAuthority() authority} and the {@code GeodeticAuthorityFactory} subclass in use.
     * A frequently used authority is "EPSG", which includes the following codes:
     *
     * <table class="sis">
     * <caption>Authority codes examples</caption>
     *   <tr><th>Code</th>      <th>Description</th></tr>
     *   <tr><td>EPSG:9315</td> <td>Seismic bin grid datum</td></tr>
     *   <tr><td>EPSG:9300</td> <td>Astra Minas</td></tr>
     * </table>
     *
     * <h4>Default implementation</h4>
     * The default implementation delegates to {@link #createDatum(String)} and casts the result.
     * If the result cannot be cast, then a {@link NoSuchAuthorityCodeException} is thrown.
     *
     * @param  code  value allocated by authority.
     * @return the datum for the given code.
     * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
     * @throws FactoryException if the object creation failed for some other reason.
     *
     * @see org.apache.sis.referencing.datum.DefaultEngineeringDatum
     */
    public EngineeringDatum createEngineeringDatum(final String code) throws NoSuchAuthorityCodeException, FactoryException {
        return cast(EngineeringDatum.class, createDatum(code), code);
    }

    /**
     * Creates a datum defining the origin of an image coordinate reference system.
     * An image datum is used in a local context only.
     * For an image datum, the anchor point is usually either the centre of the image or the corner of the image.
     *
     * <h4>Default implementation</h4>
     * The default implementation delegates to {@link #createDatum(String)} and casts the result.
     * If the result cannot be casted, then a {@link NoSuchAuthorityCodeException} is thrown.
     *
     * @param  code  value allocated by authority.
     * @return the datum for the given code.
     * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
     * @throws FactoryException if the object creation failed for some other reason.
     *
     * @see org.apache.sis.referencing.datum.DefaultImageDatum
     *
     * @deprecated The {@code ImageDatum} class has been removed in ISO 19111:2019.
     *             It is replaced by {@code EngineeringDatum}.
     */
    @Deprecated(since = "1.5")
    public ImageDatum createImageDatum(final String code) throws NoSuchAuthorityCodeException, FactoryException {
        return cast(ImageDatum.class, createDatum(code), code);
    }

    /**
     * Creates a geometric figure that can be used to describe the approximate shape of the earth.
     * In mathematical terms, it is a surface formed by the rotation of an ellipse about its minor axis.
     *
     * <h4>Examples</h4>
     * The {@linkplain #getAuthorityCodes(Class) set of available codes} depends on the defining
     * {@linkplain #getAuthority() authority} and the {@code GeodeticAuthorityFactory} subclass in use.
     * A frequently used authority is "EPSG", which includes the following codes:
     *
     * <table class="sis">
     * <caption>Authority codes examples</caption>
     *   <tr><th>Code</th>      <th>Description</th></tr>
     *   <tr><td>EPSG:7030</td> <td>WGS 84</td></tr>
     *   <tr><td>EPSG:7034</td> <td>Clarke 1880</td></tr>
     *   <tr><td>EPSG:7048</td> <td>GRS 1980 Authalic Sphere</td></tr>
     * </table>
     *
     * <h4>Default implementation</h4>
     * The default implementation delegates to {@link #createObject(String)} and casts the result.
     * If the result cannot be cast, then a {@link NoSuchAuthorityCodeException} is thrown.
     *
     * @param  code  value allocated by authority.
     * @return the ellipsoid for the given code.
     * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
     * @throws FactoryException if the object creation failed for some other reason.
     *
     * @see org.apache.sis.referencing.datum.DefaultEllipsoid
     * @see org.apache.sis.referencing.CommonCRS#ellipsoid()
     */
    public Ellipsoid createEllipsoid(final String code) throws NoSuchAuthorityCodeException, FactoryException {
        return cast(Ellipsoid.class, createObject(code), code);
    }

    /**
     * Creates a prime meridian defining the origin from which longitude values are determined.
     *
     * <h4>Examples</h4>
     * The {@linkplain #getAuthorityCodes(Class) set of available codes} depends on the defining
     * {@linkplain #getAuthority() authority} and the {@code GeodeticAuthorityFactory} subclass in use.
     * A frequently used authority is "EPSG", which includes the following codes:
     *
     * <table class="sis">
     * <caption>Authority codes examples</caption>
     *   <tr><th>Code</th>      <th>Description</th></tr>
     *   <tr><td>EPSG:8901</td> <td>Greenwich</td></tr>
     *   <tr><td>EPSG:8903</td> <td>Paris</td></tr>
     *   <tr><td>EPSG:8904</td> <td>Bogota</td></tr>
     *   <tr><td>EPSG:8905</td> <td>Madrid</td></tr>
     *   <tr><td>EPSG:8906</td> <td>Rome</td></tr>
     * </table>
     *
     * <h4>Default implementation</h4>
     * The default implementation delegates to {@link #createObject(String)} and casts the result.
     * If the result cannot be cast, then a {@link NoSuchAuthorityCodeException} is thrown.
     *
     * @param  code  value allocated by authority.
     * @return the prime meridian for the given code.
     * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
     * @throws FactoryException if the object creation failed for some other reason.
     *
     * @see org.apache.sis.referencing.datum.DefaultPrimeMeridian
     * @see org.apache.sis.referencing.CommonCRS#primeMeridian()
     */
    public PrimeMeridian createPrimeMeridian(final String code) throws NoSuchAuthorityCodeException, FactoryException {
        return cast(PrimeMeridian.class, createObject(code), code);
    }

    /**
     * Creates information about spatial, vertical, and temporal extent (usually a domain of validity) from a code.
     *
     * <h4>Examples</h4>
     * The {@linkplain #getAuthorityCodes(Class) set of available codes} depends on the defining
     * {@linkplain #getAuthority() authority} and the {@code GeodeticAuthorityFactory} subclass in use.
     * A frequently used authority is "EPSG", which includes the following codes:
     *
     * <table class="sis">
     * <caption>Authority codes examples</caption>
     *   <tr><th>Code</th>      <th>Description</th></tr>
     *   <tr><td>EPSG:1262</td> <td>World</td></tr>
     *   <tr><td>EPSG:3391</td> <td>World - between 80°S and 84°N</td></tr>
     * </table>
     *
     * <h4>Default implementation</h4>
     * The default implementation delegates to {@link #createObject(String)} and casts the result.
     * If the result cannot be cast, then a {@link NoSuchAuthorityCodeException} is thrown.
     *
     * @param  code  value allocated by authority.
     * @return the extent for the given code.
     * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
     * @throws FactoryException if the object creation failed for some other reason.
     *
     * @see org.apache.sis.metadata.iso.extent.DefaultExtent
     */
    public Extent createExtent(final String code) throws NoSuchAuthorityCodeException, FactoryException {
        return cast(Extent.class, createObject(code), code);
    }

    /**
     * Creates an arbitrary coordinate system from a code. The returned object will typically be an
     * instance of {@link EllipsoidalCS}, {@link CartesianCS}, {@link VerticalCS} or {@link TimeCS}.
     * If the coordinate system is known at compile time, it is recommended to invoke the most precise
     * method instead of this one.
     *
     * <h4>Examples</h4>
     * The {@linkplain #getAuthorityCodes(Class) set of available codes} depends on the defining
     * {@linkplain #getAuthority() authority} and the {@code GeodeticAuthorityFactory} subclass in use.
     * A frequently used authority is "EPSG", which includes the following codes:
     *
     * <table class="sis">
     * <caption>Authority codes examples</caption>
     *   <tr><th>Code</th>      <th>Type</th>              <th>Axes</th>                                    <th>Orientations</th>    <th>Unit</th></tr>
     *   <tr><td>EPSG:4496</td> <td>Cartesian 2D CS</td>   <td>easting, northing (E,N)</td>                 <td>east, north</td>     <td>metre</td></tr>
     *   <tr><td>EPSG:6422</td> <td>Ellipsoidal 2D CS</td> <td>latitude, longitude</td>                     <td>north, east</td>     <td>degree</td></tr>
     *   <tr><td>EPSG:6423</td> <td>Ellipsoidal 3D CS</td> <td>latitude, longitude, ellipsoidal height</td> <td>north, east, up</td> <td>degree, degree, metre</td></tr>
     *   <tr><td>EPSG:6404</td> <td>Spherical 3D CS</td>   <td>latitude, longitude, radius</td>             <td>north, east, up</td> <td>degree, degree, metre</td></tr>
     *   <tr><td>EPSG:6499</td> <td>Vertical CS</td>       <td>height (H)</td>                              <td>up</td>              <td>metre</td></tr>
     * </table>
     *
     * <h4>Default implementation</h4>
     * The default implementation delegates to {@link #createObject(String)} and casts the result.
     * If the result cannot be cast, then a {@link NoSuchAuthorityCodeException} is thrown.
     *
     * @param  code  value allocated by authority.
     * @return the coordinate system for the given code.
     * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
     * @throws FactoryException if the object creation failed for some other reason.
     *
     * @see org.apache.sis.referencing.cs.AbstractCS
     * @see org.apache.sis.referencing.cs.CoordinateSystems#getEpsgCode(Unit, AxisDirection...)
     */
    public CoordinateSystem createCoordinateSystem(final String code) throws NoSuchAuthorityCodeException, FactoryException {
        return cast(CoordinateSystem.class, createObject(code), code);
    }

    /**
     * Creates a 2- or 3-dimensional coordinate system for geodetic latitude and longitude,
     * sometimes with ellipsoidal height.
     *
     * <h4>Examples</h4>
     * The {@linkplain #getAuthorityCodes(Class) set of available codes} depends on the defining
     * {@linkplain #getAuthority() authority} and the {@code GeodeticAuthorityFactory} subclass in use.
     * A frequently used authority is "EPSG", which includes the following codes:
     *
     * <table class="sis">
     * <caption>Authority codes examples</caption>
     *   <tr><th>Code</th>      <th>Axes</th>                                    <th>Orientations</th>    <th>Unit</th></tr>
     *   <tr><td>EPSG:6422</td> <td>latitude, longitude</td>                     <td>north, east</td>     <td>degree</td></tr>
     *   <tr><td>EPSG:6424</td> <td>longitude, latitude</td>                     <td>east, north</td>     <td>degree</td></tr>
     *   <tr><td>EPSG:6429</td> <td>longitude, latitude</td>                     <td>east, north</td>     <td>radian</td></tr>
     *   <tr><td>EPSG:6423</td> <td>latitude, longitude, ellipsoidal height</td> <td>north, east, up</td> <td>degree, degree, metre</td></tr>
     * </table>
     *
     * <h4>Default implementation</h4>
     * The default implementation delegates to {@link #createCoordinateSystem(String)} and casts the result.
     * If the result cannot be cast, then a {@link NoSuchAuthorityCodeException} is thrown.
     *
     * @param  code  value allocated by authority.
     * @return the coordinate system for the given code.
     * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
     * @throws FactoryException if the object creation failed for some other reason.
     *
     * @see org.apache.sis.referencing.cs.DefaultEllipsoidalCS
     */
    public EllipsoidalCS createEllipsoidalCS(final String code) throws NoSuchAuthorityCodeException, FactoryException {
        return cast(EllipsoidalCS.class, createCoordinateSystem(code), code);
    }

    /**
     * Creates a 1-dimensional coordinate system for heights or depths of points.
     *
     * <h4>Examples</h4>
     * The {@linkplain #getAuthorityCodes(Class) set of available codes} depends on the defining
     * {@linkplain #getAuthority() authority} and the {@code GeodeticAuthorityFactory} subclass in use.
     * A frequently used authority is "EPSG", which includes the following codes:
     *
     * <table class="sis">
     * <caption>Authority codes examples</caption>
     *   <tr><th>Code</th>      <th>Axes</th>       <th>Orientations</th> <th>Unit</th></tr>
     *   <tr><td>EPSG:6498</td> <td>depth (D)</td>  <td>down</td>         <td>metre</td></tr>
     *   <tr><td>EPSG:6499</td> <td>height (H)</td> <td>up</td>           <td>metre</td></tr>
     * </table>
     *
     * <h4>Default implementation</h4>
     * The default implementation delegates to {@link #createCoordinateSystem(String)} and casts the result.
     * If the result cannot be cast, then a {@link NoSuchAuthorityCodeException} is thrown.
     *
     * @param  code  value allocated by authority.
     * @return the coordinate system for the given code.
     * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
     * @throws FactoryException if the object creation failed for some other reason.
     *
     * @see org.apache.sis.referencing.cs.DefaultVerticalCS
     */
    public VerticalCS createVerticalCS(final String code) throws NoSuchAuthorityCodeException, FactoryException {
        return cast(VerticalCS.class, createCoordinateSystem(code), code);
    }

    /**
     * Creates a 1-dimensional coordinate system for time elapsed in the specified time units
     * from a specified time origin.
     *
     * <h4>Default implementation</h4>
     * The default implementation delegates to {@link #createCoordinateSystem(String)} and casts the result.
     * If the result cannot be cast, then a {@link NoSuchAuthorityCodeException} is thrown.
     *
     * @param  code  value allocated by authority.
     * @return the coordinate system for the given code.
     * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
     * @throws FactoryException if the object creation failed for some other reason.
     *
     * @see org.apache.sis.referencing.cs.DefaultTimeCS
     */
    public TimeCS createTimeCS(final String code) throws NoSuchAuthorityCodeException, FactoryException {
        return cast(TimeCS.class, createCoordinateSystem(code), code);
    }

    /**
     * Creates a 1-dimensional parametric coordinate system.
     *
     * <h4>Default implementation</h4>
     * The default implementation delegates to {@link #createCoordinateSystem(String)} and casts the result.
     * If the result cannot be cast, then a {@link NoSuchAuthorityCodeException} is thrown.
     *
     * <div class="warning"><b>Warning:</b> in a future SIS version, the return type may be changed
     * to {@code org.opengis.referencing.cs.ParametricCS}. This change is pending GeoAPI revision.</div>
     *
     * @param  code  value allocated by authority.
     * @return the coordinate system for the given code.
     * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
     * @throws FactoryException if the object creation failed for some other reason.
     *
     * @see org.apache.sis.referencing.cs.DefaultParametricCS
     */
    public DefaultParametricCS createParametricCS(final String code) throws NoSuchAuthorityCodeException, FactoryException {
        return cast(DefaultParametricCS.class, createCoordinateSystem(code), code);
    }

    /**
     * Creates a 2- or 3-dimensional Cartesian coordinate system made of straight orthogonal axes.
     * All axes shall have the same linear unit of measure.
     *
     * <h4>Examples</h4>
     * The {@linkplain #getAuthorityCodes(Class) set of available codes} depends on the defining
     * {@linkplain #getAuthority() authority} and the {@code GeodeticAuthorityFactory} subclass in use.
     * A frequently used authority is "EPSG", which includes the following codes:
     *
     * <table class="sis">
     * <caption>Authority codes examples</caption>
     *   <tr><th>Code</th>      <th>Axes</th>                    <th>Orientations</th> <th>Unit</th></tr>
     *   <tr><td>EPSG:4406</td> <td>easting, northing (E,N)</td> <td>east, north</td>  <td>kilometre</td></tr>
     *   <tr><td>EPSG:4496</td> <td>easting, northing (E,N)</td> <td>east, north</td>  <td>metre</td></tr>
     *   <tr><td>EPSG:4500</td> <td>northing, easting (N,E)</td> <td>north, east</td>  <td>metre</td></tr>
     *   <tr><td>EPSG:4491</td> <td>westing, northing (W,N)</td> <td>west, north</td>  <td>metre</td></tr>
     * </table>
     *
     * <h4>Default implementation</h4>
     * The default implementation delegates to {@link #createCoordinateSystem(String)} and casts the result.
     * If the result cannot be cast, then a {@link NoSuchAuthorityCodeException} is thrown.
     *
     * @param  code  value allocated by authority.
     * @return the coordinate system for the given code.
     * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
     * @throws FactoryException if the object creation failed for some other reason.
     *
     * @see org.apache.sis.referencing.cs.DefaultCartesianCS
     */
    public CartesianCS createCartesianCS(final String code) throws NoSuchAuthorityCodeException, FactoryException {
        return cast(CartesianCS.class, createCoordinateSystem(code), code);
    }

    /**
     * Creates a 3-dimensional coordinate system with one distance measured from the origin and two angular coordinates.
     * Not to be confused with an ellipsoidal coordinate system based on an ellipsoid "degenerated" into a sphere.
     *
     * <h4>Examples</h4>
     * The {@linkplain #getAuthorityCodes(Class) set of available codes} depends on the defining
     * {@linkplain #getAuthority() authority} and the {@code GeodeticAuthorityFactory} subclass in use.
     * A frequently used authority is "EPSG", which includes the following codes:
     *
     * <table class="sis">
     * <caption>Authority codes examples</caption>
     *   <tr><th>Code</th>      <th>Axes</th>                        <th>Orientations</th>    <th>Unit</th></tr>
     *   <tr><td>EPSG:6404</td> <td>latitude, longitude, radius</td> <td>north, east, up</td> <td>degree, degree, metre</td></tr>
     * </table>
     *
     * <h4>Default implementation</h4>
     * The default implementation delegates to {@link #createCoordinateSystem(String)} and casts the result.
     * If the result cannot be cast, then a {@link NoSuchAuthorityCodeException} is thrown.
     *
     * @param  code  value allocated by authority.
     * @return the coordinate system for the given code.
     * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
     * @throws FactoryException if the object creation failed for some other reason.
     *
     * @see org.apache.sis.referencing.cs.DefaultSphericalCS
     */
    public SphericalCS createSphericalCS(final String code) throws NoSuchAuthorityCodeException, FactoryException {
        return cast(SphericalCS.class, createCoordinateSystem(code), code);
    }

    /**
     * Creates a 3-dimensional coordinate system made of a polar coordinate system
     * extended by a straight perpendicular axis.
     *
     * <h4>Default implementation</h4>
     * The default implementation delegates to {@link #createCoordinateSystem(String)} and casts the result.
     * If the result cannot be cast, then a {@link NoSuchAuthorityCodeException} is thrown.
     *
     * @param  code  value allocated by authority.
     * @return the coordinate system for the given code.
     * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
     * @throws FactoryException if the object creation failed for some other reason.
     *
     * @see org.apache.sis.referencing.cs.DefaultCylindricalCS
     */
    public CylindricalCS createCylindricalCS(final String code) throws NoSuchAuthorityCodeException, FactoryException {
        return cast(CylindricalCS.class, createCoordinateSystem(code), code);
    }

    /**
     * Creates a 2-dimensional coordinate system for coordinates represented by a distance from the origin
     * and an angle from a fixed direction.
     *
     * <h4>Default implementation</h4>
     * The default implementation delegates to {@link #createCoordinateSystem(String)} and casts the result.
     * If the result cannot be cast, then a {@link NoSuchAuthorityCodeException} is thrown.
     *
     * @param  code  value allocated by authority.
     * @return the coordinate system for the given code.
     * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
     * @throws FactoryException if the object creation failed for some other reason.
     *
     * @see org.apache.sis.referencing.cs.DefaultPolarCS
     */
    public PolarCS createPolarCS(final String code) throws NoSuchAuthorityCodeException, FactoryException {
        return cast(PolarCS.class, createCoordinateSystem(code), code);
    }

    /**
     * Creates a coordinate system axis with name, direction, unit and range of values.
     *
     * <h4>Examples</h4>
     * The {@linkplain #getAuthorityCodes(Class) set of available codes} depends on the defining
     * {@linkplain #getAuthority() authority} and the {@code GeodeticAuthorityFactory} subclass in use.
     * A frequently used authority is "EPSG", which includes the following codes:
     *
     * <table class="sis">
     * <caption>Authority codes examples</caption>
     *   <tr><th>Code</th>      <th>Description</th>   <th>Unit</th></tr>
     *   <tr><td>EPSG:106</td>  <td>Latitude (φ)</td>  <td>degree</td></tr>
     *   <tr><td>EPSG:107</td>  <td>Longitude (λ)</td> <td>degree</td></tr>
     *   <tr><td>EPSG:1</td>    <td>Easting (E)</td>   <td>metre</td></tr>
     *   <tr><td>EPSG:2</td>    <td>Northing (N)</td>  <td>metre</td></tr>
     * </table>
     *
     * <h4>Default implementation</h4>
     * The default implementation delegates to {@link #createObject(String)} and casts the result.
     * If the result cannot be cast, then a {@link NoSuchAuthorityCodeException} is thrown.
     *
     * @param  code  value allocated by authority.
     * @return the axis for the given code.
     * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
     * @throws FactoryException if the object creation failed for some other reason.
     *
     * @see org.apache.sis.referencing.cs.DefaultCoordinateSystemAxis
     */
    public CoordinateSystemAxis createCoordinateSystemAxis(final String code)
            throws NoSuchAuthorityCodeException, FactoryException
    {
        return cast(CoordinateSystemAxis.class, createObject(code), code);
    }

    /**
     * Creates an unit of measurement from a code.
     *
     * <h4>Examples</h4>
     * The {@linkplain #getAuthorityCodes(Class) set of available codes} depends on the defining
     * {@linkplain #getAuthority() authority} and the {@code GeodeticAuthorityFactory} subclass in use.
     * A frequently used authority is "EPSG", which includes the following codes:
     *
     * <table class="sis">
     * <caption>Authority codes examples</caption>
     *   <tr><th>Code</th>      <th>Description</th></tr>
     *   <tr><td>EPSG:9002</td> <td>decimal degree</td></tr>
     *   <tr><td>EPSG:9001</td> <td>metre</td></tr>
     *   <tr><td>EPSG:9030</td> <td>kilometre</td></tr>
     *   <tr><td>EPSG:1040</td> <td>second</td></tr>
     *   <tr><td>EPSG:1029</td> <td>year</td></tr>
     * </table>
     *
     * See {@link org.apache.sis.measure.Units#valueOfEPSG(int)} for a more complete list of codes.
     *
     * <h4>Default implementation</h4>
     * The default implementation delegates to {@link #createObject(String)} and casts the result.
     * If the result cannot be cast, then a {@link NoSuchAuthorityCodeException} is thrown.
     *
     * @param  code  value allocated by authority.
     * @return the unit of measurement for the given code.
     * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
     * @throws FactoryException if the object creation failed for some other reason.
     *
     * @see org.apache.sis.measure.Units#valueOfEPSG(int)
     */
    public Unit<?> createUnit(final String code) throws NoSuchAuthorityCodeException, FactoryException {
        return cast(Unit.class, createObject(code), code);
    }

    /**
     * Creates a definition of a single parameter used by an operation method.
     *
     * <h4>Examples</h4>
     * The {@linkplain #getAuthorityCodes(Class) set of available codes} depends on the defining
     * {@linkplain #getAuthority() authority} and the {@code GeodeticAuthorityFactory} subclass in use.
     * A frequently used authority is "EPSG", which includes the following codes:
     *
     * <table class="sis">
     * <caption>Authority codes examples</caption>
     *   <tr><th>Code</th>      <th>Description</th></tr>
     *   <tr><td>EPSG:8801</td> <td>Latitude of natural origin</td></tr>
     *   <tr><td>EPSG:8802</td> <td>Longitude of natural origin</td></tr>
     *   <tr><td>EPSG:8805</td> <td>Scale factor at natural origin</td></tr>
     *   <tr><td>EPSG:8806</td> <td>False easting</td></tr>
     *   <tr><td>EPSG:8807</td> <td>False northing</td></tr>
     * </table>
     *
     * <h4>Default implementation</h4>
     * The default implementation delegates to {@link #createObject(String)} and casts the result.
     * If the result cannot be cast, then a {@link NoSuchAuthorityCodeException} is thrown.
     *
     * @param  code  value allocated by authority.
     * @return the parameter descriptor for the given code.
     * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
     * @throws FactoryException if the object creation failed for some other reason.
     *
     * @see org.apache.sis.parameter.DefaultParameterDescriptor
     * @see <a href="https://sis.apache.org/tables/CoordinateOperationMethods.html">Apache SIS™ Coordinate Operation Methods</a>
     */
    public ParameterDescriptor<?> createParameterDescriptor(final String code)
            throws NoSuchAuthorityCodeException, FactoryException
    {
        return cast(ParameterDescriptor.class, createObject(code), code);
    }

    /**
     * Creates a description of the algorithm and parameters used to perform a coordinate operation.
     * An {@code OperationMethod} is a kind of metadata: it does not perform any coordinate operation
     * (e.g. map projection) by itself, but tells us what is needed in order to perform such operation.
     *
     * <p>Available methods depend both on the {@linkplain #getAuthorityCodes(Class) set declared by the authority} and on the
     * <a href="https://sis.apache.org/tables/CoordinateOperationMethods.html">list of methods implemented in Apache SIS</a>.
     * In order to be supported, an operation method must have its formulas coded in the Java programming language.
     * See {@link org.apache.sis.referencing.operation.transform.MathTransformProvider} for more information.</p>
     *
     * <h4>Default implementation</h4>
     * The default implementation delegates to {@link #createObject(String)} and casts the result.
     * If the result cannot be cast, then a {@link NoSuchAuthorityCodeException} is thrown.
     *
     * @param  code  value allocated by authority.
     * @return the operation method for the given code.
     * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
     * @throws FactoryException if the object creation failed for some other reason.
     *
     * @see org.apache.sis.referencing.operation.DefaultOperationMethod
     * @see <a href="https://sis.apache.org/tables/CoordinateOperationMethods.html">Apache SIS™ Coordinate Operation Methods</a>
     */
    public OperationMethod createOperationMethod(final String code) throws NoSuchAuthorityCodeException, FactoryException {
        return cast(OperationMethod.class, createObject(code), code);
    }

    /**
     * Creates an operation for transforming coordinates in the source CRS to coordinates in the target CRS.
     * Coordinate operations contain a {@linkplain org.apache.sis.referencing.operation.transform.AbstractMathTransform
     * math transform}, which does the actual work of transforming coordinates.
     *
     * <h4>Examples</h4>
     * The {@linkplain #getAuthorityCodes(Class) set of available codes} depends on the defining
     * {@linkplain #getAuthority() authority} and the {@code GeodeticAuthorityFactory} subclass in use.
     * A frequently used authority is "EPSG", which includes the following codes:
     *
     * <table class="sis">
     * <caption>Authority codes examples</caption>
     *   <tr><th>Code</th>      <th>Description</th></tr>
     *   <tr><td>EPSG:1133</td> <td>ED50 to WGS 84 (1)</td></tr>
     *   <tr><td>EPSG:1241</td> <td>NAD27 to NAD83 (1)</td></tr>
     *   <tr><td>EPSG:1173</td> <td>NAD27 to WGS 84 (4)</td></tr>
     *   <tr><td>EPSG:6326</td> <td>NAD83(2011) to NAVD88 height (1)</td></tr>
     * </table>
     *
     * <h4>Default implementation</h4>
     * The default implementation delegates to {@link #createObject(String)} and casts the result.
     * If the result cannot be cast, then a {@link NoSuchAuthorityCodeException} is thrown.
     *
     * @param  code  value allocated by authority.
     * @return the operation for the given code.
     * @throws NoSuchAuthorityCodeException if the specified {@code code} was not found.
     * @throws FactoryException if the object creation failed for some other reason.
     *
     * @see org.apache.sis.referencing.operation.AbstractCoordinateOperation
     */
    public CoordinateOperation createCoordinateOperation(final String code)
            throws NoSuchAuthorityCodeException, FactoryException
    {
        return cast(CoordinateOperation.class, createObject(code), code);
    }

    /**
     * Creates operations from source and target coordinate reference system codes.
     * This method should only extract the information explicitly declared in a database like EPSG.
     * This method should not attempt to infer by itself operations that are not explicitly recorded in the database.
     *
     * <h4>Default implementation</h4>
     * The default implementation returns an empty set.
     *
     * @param  sourceCRS  coded value of source coordinate reference system.
     * @param  targetCRS  coded value of target coordinate reference system.
     * @return the operations from {@code sourceCRS} to {@code targetCRS}.
     * @throws NoSuchAuthorityCodeException if a specified code was not found.
     * @throws FactoryException if the object creation failed for some other reason.
     */
    public Set<CoordinateOperation> createFromCoordinateReferenceSystemCodes(String sourceCRS, String targetCRS)
            throws NoSuchAuthorityCodeException, FactoryException
    {
        return Set.of();
    }

    /**
     * Creates a finder which can be used for looking up unidentified objects.
     * The finder tries to fetch a fully {@linkplain AbstractIdentifiedObject identified object}
     * from an incomplete one, for example from an object without "{@code ID[…]}" or
     * "{@code AUTHORITY[…]}" element in <i>Well Known Text</i>.
     *
     * @return a finder to use for looking up unidentified objects.
     * @throws FactoryException if the finder cannot be created.
     *
     * @see org.apache.sis.referencing.IdentifiedObjects#newFinder(String)
     */
    public IdentifiedObjectFinder newIdentifiedObjectFinder() throws FactoryException {
        return new IdentifiedObjectFinder(this);
    }

    /**
     * Returns {@code true} if the given portion of the code is equal, ignoring case, to the given namespace.
     *
     * @param  namespace  expected namespace (e.g. "OGC" or "EPSG").
     * @param  code       the code where to check namespace.
     * @param  start      index of first character of the namespace in the code.
     * @param  end        index after last character of the namespace in the code.
     * @return whether the specified region of the code is equal to the namespace.
     */
    static boolean regionMatches(final String namespace, final String code, final int start, final int end) {
        final int length = namespace.length();
        return (length == end - start) && code.regionMatches(true, start, namespace, 0, length);
    }

    /**
     * Trims the namespace, if present. For example if this factory is an EPSG authority factory
     * and the specified code start with the {@code "EPSG:"} prefix, then the prefix is removed.
     * Otherwise, the string is returned unchanged except for leading and trailing spaces which
     * are removed, together with {@link Character#isIdentifierIgnorable(int) ignorable characters}.
     *
     * @param  code  the code to trim.
     * @return the code with the namespace part removed if that part matched one of the values given by {@link #getCodeSpaces()}.
     * @throws FactoryException if an error occurred while listing the code spaces managed by this factory.
     *
     * @since 0.8
     */
    protected final String trimNamespace(String code) throws FactoryException {
        code = CharSequences.trimIgnorables(code).toString();
        int s = code.indexOf(Constants.DEFAULT_SEPARATOR);
        if (s >= 0) {
            final int end   = CharSequences.skipTrailingWhitespaces(code, 0, s);
            final int start = CharSequences.skipLeadingWhitespaces (code, 0, end);
            for (final String codespace : getCodeSpaces()) {
                if (regionMatches(codespace, code, start, end)) {
                    final int n = code.indexOf(Constants.DEFAULT_SEPARATOR, s + 1);
                    if (n >= 0 && code.indexOf(Constants.DEFAULT_SEPARATOR, n + 1) < 0) {
                        /*
                         * The separator sometimes appears twice, as in "EPSG::4326" or "EPSG:8.9:4326".
                         * The part between the two separators is the verion number, which we ignore in
                         * this simple version provided that it is the last semi-colon.
                         */
                        s = n;
                    }
                    final int length = code.length();
                    return CharSequences.trimWhitespaces(code, s+1, length).toString();
                }
            }
        }
        return code.strip();
    }

    /**
     * Casts the given object to the given type, or throws an exception if the object cannot be cast.
     * This convenience method is provided for implementation of {@code createXXX} methods.
     *
     * @param  type    the type to return (e.g. {@code CoordinateReferenceSystem.class}).
     * @param  object  the object to cast.
     * @param  code    the authority code, used only for formatting an error message.
     * @return the object cast to the given type.
     * @throws NoSuchAuthorityCodeException if the given object is not an instance of the given type.
     * @throws FactoryException if an error occurred while listing the code spaces managed by this factory.
     */
    @SuppressWarnings("unchecked")
    private <T> T cast(final Class<T> type, final IdentifiedObject object, final String code)
            throws FactoryException
    {
        if (type.isInstance(object)) {
            return (T) object;
        }
        /*
         * Get the actual type of the object. Returns the GeoAPI type if possible,
         * or fallback on the implementation class otherwise.
         */
        final Class<?> actual = ReferencingUtilities.getInterface(IdentifiedObject.class, object);
        /*
         * Get the authority from the object if possible, in order to avoid a call
         * to the potentially costly (for EPSGDataAccess) getAuthority() method.
         */
        final Identifier id = object.getName();
        final Citation authority = (id != null) ? id.getAuthority() : getAuthority();
        throw new NoSuchAuthorityCodeException(
                Errors.format(Errors.Keys.UnexpectedTypeForReference_3, code, type, actual),
                Citations.getIdentifier(authority),
                trimNamespace(code),
                code);
    }

    /**
     * Returns a string representation of this factory for debugging purpose only.
     * The string returned by this method may change in any future SIS version.
     *
     * @return a string representation for debugging purpose.
     */
    @Override
    public String toString() {
        final var buffer = new StringBuilder(Classes.getShortClassName(this));
        // Do not append `getAuthority()` because it may perform database access.
        appendStringTo(buffer);
        return buffer.append(']').toString();
    }

    /**
     * Hook for subclasses.
     */
    void appendStringTo(final StringBuilder buffer) {
    }
}
