All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.io7m.changelog.core.CVersionQualifier Maven / Gradle / Ivy

The newest version!
/*
 * Copyright © 2024 Mark Raynsford  https://www.io7m.com
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
 * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package com.io7m.changelog.core;

import java.util.List;
import java.util.Objects;
import java.util.regex.Pattern;

import static java.lang.Integer.compareUnsigned;
import static java.lang.Integer.parseUnsignedInt;

/**
 * A version number qualifier.
 *
 * @param text The qualifier text
 */

public record CVersionQualifier(
  String text)
  implements Comparable
{
  private static final Pattern VALID_QUALIFIER =
    Pattern.compile("[A-Za-z0-9\\-]+(\\.[A-Za-z0-9\\-]+)*");

  private static final Pattern IS_NUMERIC =
    Pattern.compile("[0-9]+");

  /**
   * A version number qualifier.
   *
   * @param text The qualifier text
   */

  public CVersionQualifier
  {
    Objects.requireNonNull(text, "text");

    final var matcher = VALID_QUALIFIER.matcher(text);
    if (!matcher.matches()) {
      throw new IllegalArgumentException(
        "Qualifier '%s' must match the pattern '%s'"
          .formatted(text, VALID_QUALIFIER)
      );
    }
  }

  /**
   * @return {@code true} if this version qualifier denotes a snapshot version
   */

  public boolean isSnapshot()
  {
    return Objects.equals(this.text, "SNAPSHOT");
  }

  @Override
  public int compareTo(
    final CVersionQualifier other)
  {
    if (this.text.equals(other.text)) {
      return 0;
    }

    final var identifiersA =
      List.of(this.text.split("\\."));
    final var identifiersB =
      List.of(other.text.split("\\."));

    /*
     * 1. Identifiers consisting of only digits are compared numerically.
     *
     * 2. Identifiers with letters or hyphens are compared lexically in ASCII
     *    sort order.
     *
     * 3. Numeric identifiers always have lower precedence than non-numeric
     *    identifiers.
     *
     * 4. A larger set of pre-release fields has a higher precedence than a
     *    smaller set, if all the preceding identifiers are equal.
     */

    final var sizeA = identifiersA.size();
    final var sizeB = identifiersB.size();
    final var smallest = Math.min(sizeA, sizeB);

    for (int index = 0; index < smallest; ++index) {
      final var r =
        compareIdentifier(identifiersA.get(index), identifiersB.get(index));
      if (r != 0) {
        return r;
      }
    }

    return (sizeA < sizeB) ? -1 : 1;
  }

  private static int compareIdentifier(
    final String identifierA,
    final String identifierB)
  {
    final var numericA =
      IS_NUMERIC.matcher(identifierA).matches();
    final var numericB =
      IS_NUMERIC.matcher(identifierB).matches();

    /*
     * Identifiers consisting of only digits are compared numerically.
     */

    if (numericA && numericB) {
      return compareUnsigned(
        parseUnsignedInt(identifierA),
        parseUnsignedInt(identifierB)
      );
    }

    /*
     * Identifiers with letters or hyphens are compared lexically in ASCII
     * sort order.
     */

    if (!numericA && !numericB) {
      return identifierA.compareTo(identifierB);
    }

    /*
     * Numeric identifiers always have lower precedence than non-numeric
     * identifiers.
     */

    return numericA ? -1 : 1;
  }

  @Override
  public String toString()
  {
    return this.text;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy