001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*-
002 *
003 * Copyright © 2025 microBean™.
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
006 * the License. You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
011 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
012 * specific language governing permissions and limitations under the License.
013 */
014package org.microbean.assign;
015
016import java.util.Comparator;
017
018import javax.lang.model.type.TypeMirror;
019
020import org.microbean.construct.Domain;
021
022import static java.util.Objects.requireNonNull;
023
024/**
025 * A {@link Comparator} of {@link TypeMirror}s that establishes a <dfn>partial order</dfn> on its arguments, enforcing
026 * only that more specialized types precede less specialized ones.
027 *
028 * <strong>Instances of this class are (deliberately) not consistent with equals.</strong>
029 *
030 * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a>
031 *
032 * @see #compare(TypeMirror, TypeMirror)
033 */
034public final class SpecializationComparator implements Comparator<TypeMirror> {
035
036  private final Domain domain;
037
038  /**
039   * Creates a new {@link SpecializationComparator}.
040   *
041   * @param domain a {@link Domain}; must not be {@code null}
042   *
043   * @exception NullPointerException if {@code domain} is {@code null}
044   */
045  public SpecializationComparator(final Domain domain) {
046    super();
047    this.domain = requireNonNull(domain, "domain");
048  }
049
050  /**
051   * Compares two {@link TypeMirror}s and returns the result.
052   *
053   * <p>The following rules are evaluated in order to calculate the returned result:</p>
054   *
055   * <p>If {@code t} {@code ==} {@code s}, or {@linkplain Domain#sameType(TypeMirror, TypeMirror) <code>t</code> and
056   * <code>s</code> are the same time}, returns {@code 0}.</p>
057   *
058   * <p>If {@code t} is {@code null} and {@code s} is not, returns a positive value.</p>
059   *
060   * <p>If {@code s} is {@code null} and {@code t} is not, returns a negative value.</p>
061   *
062   * <p>If {@code t} {@linkplain Domain#subtype(TypeMirror, TypeMirror) is a subtype of} {@code s}, returns a negative
063   * value.</p>
064   *
065   * <p>If {@code s} {@linkplain Domain#subtype(TypeMirror, TypeMirror) is a subtype of} {@code t}, returns a positive
066   * value.</p>
067   *
068   * <p>In all other cases {@code 0} is returned.</p>.
069   *
070   * @param t a {@link TypeMirror}; may be {@code null}
071   *
072   * @param s a {@link TypeMirror}; may be {@code null}
073   *
074   * @return a comparison result
075   *
076   * @see Domain#sameType(TypeMirror, TypeMirror)
077   *
078   * @see Domain#subtype(TypeMirror, TypeMirror)
079   */
080  @Override // Comparator<TypeMirror, TypeMirror>
081  public final int compare(final TypeMirror t, final TypeMirror s) {
082    if (t == s) {
083      return 0;
084    } else if (t == null) {
085      return 1; // nulls right
086    } else if (s == null) {
087      return -1; // nulls right
088    } else if (domain.sameType(t, s)) {
089      return 0;
090    } else if (domain.subtype(t, s)) {
091      // t is a subtype of s; s is a proper supertype of t
092      return -1;
093    } else if (domain.subtype(s, t)) {
094      // s is a subtype of t; t is a proper supertype of s
095      return 1;
096    } else {
097      return 0;
098    }
099  }
100
101}