package com.github.tinselspoon.intellij.kubernetes.model; import java.util.Comparator; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Implementation of a {@link Comparator} for Kubernetes API version strings in the conventional format, e.g. {@code apps/v1beta1} etc. * <p> * The comparator has the following characteristics: * <ul> * <li>API groups (apps, extensions, settings.k8s.io etc) are compared lexicographically. A version with a group present is considered to be after a version without.</li> * <li>Then, the major version (v1, v2) etc is compared numerically.</li> * <li>Finally, the minor version or qualifier (alpha1, beta1, beta2 etc) is compared lexicographically. * An API version string with this part missing is considered to be greater than an API version string with this part present, as if it is missing this signifies a stable version.</li> * </ul> * <p> * If either string does not match the expected format for an API version, then the comparator falls back to simple lexicographic comparison. */ public class ApiVersionComparator implements Comparator<String> { /** Singleton instance. */ public static final ApiVersionComparator INSTANCE = new ApiVersionComparator(); /** Regex for parsing the conventional API version strings. */ private static final Pattern VERSION_FORMAT_REGEX = Pattern.compile("(?:(?<group>.+)/)?v(?<major>\\d+)(?<minor>[A-Za-z]+\\d+)?"); /** Private constructor for singleton instance. */ private ApiVersionComparator() { } @Override public int compare(final String o1, final String o2) { final Matcher matcherOne = VERSION_FORMAT_REGEX.matcher(o1); final Matcher matcherTwo = VERSION_FORMAT_REGEX.matcher(o2); // Fall back to simple string comparision if either of the versions cannot be parsed if (!matcherOne.matches() || !matcherTwo.matches()) { return o1.compareTo(o2); } Comparator<Matcher> comparator = Comparator.comparing(m -> m.group("group"), Comparator.nullsFirst(Comparator.naturalOrder())); comparator = comparator.thenComparingInt(m -> Integer.parseInt(m.group("major"))); comparator = comparator.thenComparing(m -> m.group("minor"), Comparator.nullsLast(Comparator.naturalOrder())); return comparator.compare(matcherOne, matcherTwo); } }