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;
017import java.util.Objects;
018
019import javax.lang.model.type.TypeMirror;
020
021import org.microbean.construct.Domain;
022
023/**
024 * A {@link Comparator} of {@link TypeMirror}s that establishes a <dfn>partial order</dfn> on its arguments, enforcing
025 * only that more specialized types precede less specialized ones.
026 *
027 * <strong>Instances of this class are (deliberately) not consistent with equals.</strong>
028 *
029 * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a>
030 *
031 * @see #compare(TypeMirror, TypeMirror)
032 */
033public final class SpecializationComparator implements Comparator<TypeMirror> {
034
035  private final Domain domain;
036
037  /**
038   * Creates a new {@link SpecializationComparator}.
039   *
040   * @param domain a {@link Domain}; must not be {@code null}
041   *
042   * @exception NullPointerException if {@code domain} is {@code null}
043   */
044  public SpecializationComparator(final Domain domain) {
045    super();
046    this.domain = Objects.requireNonNull(domain, "domain");
047  }
048
049  /**
050   * Compares two {@link TypeMirror}s and returns the result.
051   *
052   * <p>The following rules are evaluated in order to calculate the returned result:</p>
053   *
054   * <p>If {@code t} {@code ==} {@code s}, or {@linkplain Domain#sameType(TypeMirror, TypeMirror) <code>t</code> and
055   * <code>s</code> are the same time}, returns {@code 0}.</p>
056   *
057   * <p>If {@code t} is {@code null} and {@code s} is not, returns a positive value.</p>
058   *
059   * <p>If {@code s} is {@code null} and {@code t} is not, returns a negative value.</p>
060   *
061   * <p>If {@code t} {@linkplain Domain#subtype(TypeMirror, TypeMirror) is a subtype of} {@code s}, returns a negative
062   * value.</p>
063   *
064   * <p>If {@code s} {@linkplain Domain#subtype(TypeMirror, TypeMirror) is a subtype of} {@code t}, returns a positive
065   * value.</p>
066   *
067   * <p>In all other cases {@code 0} is returned.</p>.
068   *
069   * @param t a {@link TypeMirror}; may be {@code null}
070   *
071   * @param s a {@link TypeMirror}; may be {@code null}
072   *
073   * @return a comparison result
074   *
075   * @see Domain#sameType(TypeMirror, TypeMirror)
076   *
077   * @see Domain#subtype(TypeMirror, TypeMirror)
078   */
079  @Override // Comparator<TypeMirror, TypeMirror>
080  public final int compare(final TypeMirror t, final TypeMirror s) {
081    if (t == s) {
082      return 0;
083    } else if (t == null) {
084      return 1; // nulls right
085    } else if (s == null) {
086      return -1; // nulls right
087    } else if (domain.sameType(t, s)) {
088      return 0;
089    } else if (domain.subtype(t, s)) {
090      // t is a subtype of s; s is a proper supertype of t
091      return -1;
092    } else if (domain.subtype(s, t)) {
093      // s is a subtype of t; t is a proper supertype of s
094      return 1;
095    } else {
096      return 0;
097    }
098  }
099
100}