MavenVersionComparator.java

package com.soebes.itf.jupiter.extension;

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

import org.apiguardian.api.API;

import java.util.Arrays;
import java.util.List;
import java.util.Objects;

import static org.apiguardian.api.API.Status.INTERNAL;

/**
 * @author Karl Heinz Marbaise
 */
@API(status = INTERNAL, since = "0.13.0")
public final class MavenVersionComparator implements Comparable<MavenVersionComparator> {
  private static final int NOT_DEFINED = -1;
  private static final String NOT_DEFINE_QUALIFIER = "";
  private static final String NOT_DEFINED_STRING = "ND";
  private final int major;
  private final int minor;
  private final int patch;
  private final String qualifier;
  private final int qualifierNumber;

  private MavenVersionComparator(int major, int minor, int patch, String qualifier, int qualifierNumber) {
    this.major = major;
    this.minor = minor;
    this.patch = patch;
    this.qualifier = Objects.requireNonNull(qualifier, "qualifier is not allowed to be null.");
    this.qualifierNumber = qualifierNumber;
  }

  /**
   * Define a {@link MavenVersionComparator} which identifies any version to be equal to it.
   * @return {@link MavenVersionComparator}.
   */
  public static MavenVersionComparator of() {
    return MavenVersionComparator.of(NOT_DEFINED);
  }

  /**
   * Define a {@link MavenVersionComparator} which identifies only the major version.
   * Example: {@code 3}. That means in consequence that the following version will
   * be seen as equal:
   * <ul>
   *   <li>3 == 3.6.0 </li>
   *   <li>3 == 3.1.1 </li>
   *   <li>3 == 3.1.1-alpha-1 </li>
   * </ul>
   * That means you are only interested in the major version.
   *
   * @param major The major version.
   * @return {@link MavenVersionComparator}.
   */
  public static MavenVersionComparator of(int major) {
    return MavenVersionComparator.of(major, NOT_DEFINED);
  }

  /**
   * Define a {@link MavenVersionComparator} which identifies only the major and minor version.
   * Example: {@code 3.6}. That means in consequence that the following version will
   * be seen as equal:
   * <ul>
   *   <li>3.6 == 3.6.0 </li>
   *   <li>3.6 == 3.6.15 </li>
   *   <li>3.6 == 3.6.15-alpha-2 </li>
   * </ul>
   * That means you are only interested in the major and minor version.
   * @param major The major version.
   * @param minor The minor version.
   * @return {@link MavenVersionComparator}.
   */
  public static MavenVersionComparator of(int major, int minor) {
    return MavenVersionComparator.of(major, minor, NOT_DEFINED);
  }

  /**
   * Define a {@link MavenVersionComparator} which identifies only the major, minor and patch version.
   * Example: {@code 3.6.1}.
   * @param major The major version.
   * @param minor The minor version.
   * @param patch The patch version.
   * @return {@link MavenVersionComparator}.
   */
  public static MavenVersionComparator of(int major, int minor, int patch) {
    return MavenVersionComparator.of(major, minor, patch, NOT_DEFINE_QUALIFIER, NOT_DEFINED);
  }

  /**
   * Define a {@link MavenVersionComparator} which identifies only the major, minor, patch, qualifier and qualifierNumber.
   * Example: {@code 3.6.1-alpha-1}.
   *
   * @param major The major version.
   * @param minor The minor version.
   * @param patch The patch version.
   * @param qualifier The qualifier version.
   * @param qualifierNumber The qualifierNumber version.
   * @return {@link MavenVersionComparator}.
   */
  public static MavenVersionComparator of(int major, int minor, int patch, String qualifier, int qualifierNumber) {
    return new MavenVersionComparator(major, minor, patch, qualifier, qualifierNumber);
  }

  public int major() {
    return major;
  }

  public int minor() {
    return minor;
  }

  public int patch() {
    return patch;
  }

  public String qualifier() {
    return qualifier;
  }

  public int qualifierNumber() {
    return qualifierNumber;
  }

  @Override
  public boolean equals(Object obj) {
    if (obj == this) return true;
    if (obj == null || obj.getClass() != this.getClass()) return false;
    MavenVersionComparator that = (MavenVersionComparator) obj;
    return this.major == that.major &&
           this.minor == that.minor &&
           this.patch == that.patch &&
           Objects.equals(this.qualifier, that.qualifier) &&
           this.qualifierNumber == that.qualifierNumber;
  }

  @Override
  public int hashCode() {
    return Objects.hash(major, minor, patch, qualifier, qualifierNumber);
  }

  @Override
  public String toString() {
    String q = this.qualifier.isEmpty() ? "" : "-" + this.qualifier + "-" + qualifierNumber;
    String majorStr = this.major == NOT_DEFINED ? NOT_DEFINED_STRING : Integer.toString(this.major);
    String minorStr = this.minor == NOT_DEFINED ? NOT_DEFINED_STRING : Integer.toString(this.minor);
    String patchStrg = this.patch == NOT_DEFINED ? NOT_DEFINED_STRING : Integer.toString(this.patch);
    return "{" + majorStr + "." + minorStr + "." + patchStrg + q + "}";
  }

  /**
   * This defines the order for qualifiers:
   * <ol>
   *   <li>alpha</li>
   *   <li>beta</li>
   *   <li>rc</li>
   * </ol>
   * That means based on the time line, {@code beta} comes before {@code rc} while {@code alpha} comes before {@code beta}.
   */
  private static final List<String> QUALIFIER_LIST = Arrays.asList(
      "alpha",
      "beta",
      "rc"
  );

  @Override
  public int compareTo(MavenVersionComparator rhs) {
    if (this.major() == NOT_DEFINED || rhs.major() == NOT_DEFINED) {
      return 0;
    }

    int majorComparison = Integer.compare(this.major(), rhs.major());
    if (majorComparison != 0) {
      return majorComparison;
    }

    if (this.minor() == NOT_DEFINED || rhs.minor() == NOT_DEFINED) {
      return 0;
    }

    int minorComparison = Integer.compare(this.minor(), rhs.minor());
    if (minorComparison != 0) {
      return minorComparison;
    }

    if (this.patch() == NOT_DEFINED || rhs.patch() == NOT_DEFINED) {
      return 0;
    }

    int patchComparison = Integer.compare(this.patch(), rhs.patch());
    if (patchComparison != 0) {
      return patchComparison;
    }

    boolean lhsContained = QUALIFIER_LIST.contains(this.qualifier());
    boolean rhsContained = QUALIFIER_LIST.contains(rhs.qualifier());
    if (lhsContained && rhsContained) {
      int qualifierComparison = Integer.compare(QUALIFIER_LIST.indexOf(this.qualifier()), QUALIFIER_LIST.indexOf(rhs.qualifier()));
      if (qualifierComparison != 0) {
        return qualifierComparison;
      }
      return Integer.compare(this.qualifierNumber(), rhs.qualifierNumber());
    } else {
      return lhsContained ? -1 : rhsContained ? +1 : Boolean.compare(this.qualifier().isEmpty(), rhs.qualifier().isEmpty());
    }
  }
}