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


/**
 * Interfaces of classes for which instances can be compared for equality using different levels of strictness.
 * For example, {@link org.opengis.referencing.operation.MathTransform} implementations can be
 * compared ignoring some properties (remarks, <i>etc.</i>) that are not relevant to the
 * coordinates calculation.
 *
 * <h2>Conditions for equality</h2>
 * <ul>
 *   <li>{@link org.apache.sis.metadata.iso.ISOMetadata} subclasses
 *     <ol>
 *       <li>{@link ComparisonMode#STRICT STRICT} – Objects must be of the same class
 *           and all attributes must be equal, including {@code xlink} and others
 *           {@linkplain org.apache.sis.metadata.iso.ISOMetadata#getIdentifiers() identifiers}.</li>
 *       <li>{@link ComparisonMode#BY_CONTRACT BY_CONTRACT} – The same attributes as the above
 *           {@code STRICT} mode must be equal, but the metadata object don't need to be implemented
 *           by the same class provided that they implement the same GeoAPI interface.</li>
 *       <li>{@link ComparisonMode#IGNORE_METADATA IGNORE_METADATA} – Only the attributes defined
 *           in the GeoAPI interfaces are compared. The above-cited identifiers and {@code xlinks}
 *           attributes are ignored.</li>
 *       <li>{@link ComparisonMode#APPROXIMATE APPROXIMATE} – The same attributes as the above
 *           {@code IGNORE_METADATA} mode are compared, but a slight (implementation dependent)
 *           difference is tolerated in floating point numbers.</li>
 *     </ol>
 *   </li>
 *   <li>{@link org.apache.sis.referencing.AbstractIdentifiedObject} subclasses
 *     <ol>
 *       <li>{@link ComparisonMode#STRICT STRICT} – Objects must be of the same class
 *           and all attributes must be equal.</li>
 *       <li>{@link ComparisonMode#BY_CONTRACT BY_CONTRACT} – The same attributes as the above
 *           {@code STRICT} mode must be equal, but the referencing object don't need to be
 *           implemented by the same class provided that they implement the same GeoAPI interface.</li>
 *       <li>{@link ComparisonMode#IGNORE_METADATA IGNORE_METADATA} – The
 *           {@linkplain org.apache.sis.referencing.crs.AbstractCRS#getIdentifiers() identifiers},
 *           {@linkplain org.apache.sis.referencing.crs.AbstractCRS#getAlias() aliases},
 *           {@linkplain org.apache.sis.referencing.crs.AbstractCRS#getDomains() domains} and
 *           {@linkplain org.apache.sis.referencing.crs.AbstractCRS#getRemarks() remarks}
 *           are ignored because they have no incidence on the coordinate values to be computed by
 *           {@linkplain org.opengis.referencing.operation.ConcatenatedOperation coordinate operations}.
 *           All other attributes that are relevant to coordinate calculations, must be equal.</li>
 *       <li>{@link ComparisonMode#COMPATIBILITY COMPATIBILITY} – The same attributes as the above
 *           {@code IGNORE_METADATA} mode are compared, but some structural changes are accepted for historical reasons.</li>
 *       <li>{@link ComparisonMode#APPROXIMATE APPROXIMATE} – The same attributes as the above
 *           {@code COMPATIBILITY} mode are compared, but a slight (implementation dependent)
 *           difference is tolerated in floating point numbers.</li>
 *     </ol>
 *   </li>
 *   <li>{@link org.apache.sis.referencing.operation.transform.AbstractMathTransform} subclasses
 *       except {@link org.apache.sis.referencing.operation.transform.LinearTransform}
 *     <ol>
 *       <li>{@link ComparisonMode#STRICT STRICT} – Objects must be of the same class and all
 *           attributes must be equal, including the
 *           {@linkplain org.apache.sis.referencing.operation.transform.AbstractMathTransform#getParameterValues() parameter values}.</li>
 *       <li>{@link ComparisonMode#BY_CONTRACT BY_CONTRACT} – Synonymous to the {@code STRICT} mode,
 *            because there is no GeoAPI interfaces for the various kind of math transforms.</li>
 *       <li>{@link ComparisonMode#IGNORE_METADATA IGNORE_METADATA} – Objects must be of the same class,
 *           but the parameter values can be different if they are different way to formulate the same transform.
 *           For example, a <q>Mercator (2SP)</q> projection on a sphere with a <i>standard parallel</i>
 *           value of 60° produces the same results as a <q>Mercator (1SP)</q> projection on the same sphere
 *           with a <i>scale factor</i> value of 0.5.</li>
 *       <li>{@link ComparisonMode#APPROXIMATE APPROXIMATE} – The same attributes as the above
 *           {@code IGNORE_METADATA} mode are compared, but a slight (implementation dependent)
 *           difference is tolerated in floating point numbers.</li>
 *     </ol>
 *   </li>
 *   <li>{@link org.apache.sis.referencing.operation.matrix.MatrixSIS} and
 *       {@link org.apache.sis.referencing.operation.transform.LinearTransform} implementations
 *     <ol>
 *       <li>{@link ComparisonMode#STRICT STRICT} – Objects must be of the same class, matrices
 *           must have the same size and all matrix elements must be equal.</li>
 *       <li>{@link ComparisonMode#BY_CONTRACT BY_CONTRACT} – Matrices must have the same size
 *           and all matrix elements must be equal, but the matrices are not required to be the
 *           same implementation class (any {@link org.opengis.referencing.operation.Matrix} is okay).</li>
 *       <li>{@link ComparisonMode#IGNORE_METADATA IGNORE_METADATA} – Synonymous to the
 *           {@code BY_CONTRACT} mode, because matrices don't have metadata.</li>
 *       <li>{@link ComparisonMode#APPROXIMATE APPROXIMATE} – The same attributes as the above
 *           {@code BY_CONTRACT} mode are compared, but a slight (implementation dependent)
 *           difference is tolerated in floating point numbers.</li>
 *     </ol>
 *   </li>
 * </ul>
 *
 * @author  Martin Desruisseaux (Geomatys)
 * @version 0.3
 * @since   0.3
 */
public interface LenientComparable {
    /**
     * Compares this object with the given object for equality.
     * The strictness level is controlled by the second argument,
     * from stricter to more permissive values:
     *
     * <table class="compact">
     *   <caption>Description of comparison modes</caption>
     *   <tr><td>{@link ComparisonMode#STRICT STRICT}:</td>
     *        <td>All attributes of the compared objects shall be strictly equal.</td></tr>
     *   <tr><td>{@link ComparisonMode#BY_CONTRACT BY_CONTRACT}:</td>
     *       <td>Only the attributes published in the interface contract need to be compared.</td></tr>
     *   <tr><td>{@link ComparisonMode#IGNORE_METADATA IGNORE_METADATA}:</td>
     *       <td>Only the attributes relevant to the object functionality are compared.</td></tr>
     *   <tr><td>{@link ComparisonMode#APPROXIMATE APPROXIMATE}:</td>
     *       <td>Only the attributes relevant to the object functionality are compared,
     *           with some tolerance threshold on numerical values.</td></tr>
     *   <tr><td>{@link ComparisonMode#DEBUG DEBUG}:</td>
     *        <td>Special mode for figuring out why two objects expected to be equal are not.</td></tr>
     * </table>
     *
     * <h4>Conformance to the {@code equals(Object)} method contract</h4>
     * {@link ComparisonMode#STRICT} is the only mode compliant with the {@link Object#equals(Object)} contract.
     * For all other modes <var>m</var>, the comparison is not guaranteed to be <i>symmetric</i> neither
     * <i>transitive</i>:
     *
     * <ul>
     *   <li>{@code x.equals(y,m)} is <strong>not</strong> guaranteed to be equal to {@code y.equals(x,m)}.
     *       In particular, the {@code BY_CONTRACT} mode and all modes below it will typically compare only the
     *       properties known to {@code this} instance, ignoring any properties that may be known only by the other
     *       instance.</li>
     *   <li>{@code x.equals(y,m)} and {@code y.equals(z,m)} does <strong>not</strong> implies
     *       {@code x.equals(z,m)}. In particular, the use of a comparison threshold for the
     *       {@code APPROXIMATE} mode is incompatible with the transitivity contract.</li>
     * </ul>
     *
     * @param  other  the object to compare to {@code this}.
     * @param  mode   the strictness level of the comparison.
     * @return {@code true} if both objects are equal according the given comparison mode.
     *
     * @see Utilities#deepEquals(Object, Object, ComparisonMode)
     */
    boolean equals(Object other, ComparisonMode mode);

    /**
     * Returns {@code true} if this object is strictly equals to the given object.
     * This method is usually implemented as below:
     *
     * {@snippet lang="java" :
     *     public boolean equals(Object other) {
     *         return equals(other, ComparisonMode.STRICT);
     *     }
     *     }
     *
     * Implementers shall ensure that the following conditions hold. Unless the {@code equals}
     * behavior is clearly documented in the interface javadoc (as for example in the Java
     * collection framework), {@link ComparisonMode#STRICT} is the only reliable mode for
     * this method implementation.
     *
     * <ul>
     *   <li>{@code A.equals(B)} implies {@code B.equals(A)};</li>
     *   <li>{@code A.equals(B)} and {@code B.equals(C)} implies {@code A.equals(C)};</li>
     *   <li>{@code A.equals(B)} implies {@code A.hashCode() == B.hashCode()};</li>
     * </ul>
     *
     * This method is declared {@code final} in most SIS implementations for ensuring that
     * subclasses override the above {@link #equals(Object, ComparisonMode)} method instead
     * than this one.
     *
     * @param  other  the object to compare to {@code this}.
     * @return {@code true} if both objects are strictly equal.
     *
     * @see ComparisonMode#STRICT
     */
    @Override
    boolean equals(Object other);
}
