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

com.google.tsunami.common.version.Version Maven / Gradle / Ivy

There is a newer version: 0.0.26
Show newest version
/*
 * Copyright 2020 Google LLC
 *
 * Licensed 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.
 */
package com.google.tsunami.common.version;

import static com.google.common.base.Preconditions.checkArgument;

import com.google.auto.value.AutoValue;
import com.google.auto.value.extension.memoized.Memoized;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.errorprone.annotations.Immutable;
import java.util.Arrays;
import java.util.regex.Pattern;

/**
 * Software version class that support 3 types. Type {@link Type#NORMAL} with a version number, type
 * {@link Type#MAXIMUM} and {@link Type#MINIMUM} for use in version range to indicate unbounded
 * ranges.
 *
 * 

Version is suitable for unstructured version string and supports comparison operations using * extra logic that detects semantic qualifier. * *

The current logic support comparison of versions that respect order, like: * *

 *   10
 *   1.1
 *   1.1a
 *   1.1a1
 *   20170101
 *   2017.01.01
 *   1.1rc1
 *   1.1-1p1
 *   1.1patch1
 *   1.1gg1.0
 *   1-1
 *   1:1.1
 *   1.1.alpha
 *   1.1.beta.1
 *   1.1.alpha.beta
 * 
* * Known limitation of this approach are versions with no order, like commit hashes. */ @AutoValue @Immutable public abstract class Version implements Comparable { private static final Pattern EPOCH_PATTERN = Pattern.compile("\\d+[:|_].*"); private static final Pattern SEMANTIC_SEGMENT_SEPARATORS = Pattern.compile("[-:_~]"); private static final Version MAXIMUM = builder().setVersionType(Type.MAXIMUM).setVersionString("").build(); private static final Version MINIMUM = builder().setVersionType(Type.MINIMUM).setVersionString("").build(); /** * Software version class that support 3 types. Type {@link Type#NORMAL} with a version number, * type {@link Type#MAXIMUM} and {@link Type#MINIMUM} for use in version range to indicate * unbounded ranges. */ public enum Type { NORMAL, MINIMUM, MAXIMUM } abstract Type versionType(); public abstract String versionString(); @Memoized ImmutableList segments() { String normalizedString = versionString(); // Add a default epoch of 0 if one is missing. if (!EPOCH_PATTERN.matcher(normalizedString).matches()) { normalizedString = "0:" + normalizedString; } return Arrays.stream(SEMANTIC_SEGMENT_SEPARATORS.split(normalizedString)) .filter(segment -> !segment.isEmpty()) .map(Segment::fromString) .filter(segment -> !segment.equals(Segment.NULL)) .collect(ImmutableList.toImmutableList()); } public static Builder builder() { return new AutoValue_Version.Builder(); } public static Version fromString(String versionString) { checkArgument(!Strings.isNullOrEmpty(versionString)); Version version = builder().setVersionType(Type.NORMAL).setVersionString(versionString).build(); if (!EPOCH_PATTERN.matcher(versionString).matches()) { versionString = "0:" + versionString; } boolean isValid = version.segments().stream() .flatMap(segment -> segment.tokens().stream()) .anyMatch( token -> (token.isNumeric() && token.getNumeric() != 0) || (token.isText() && !token.getText().isEmpty())); if (!isValid) { throw new IllegalArgumentException( String.format( "Input version string %s is not valid, it should contain at least one non-empty" + " field.", versionString)); } return version; } public static Version maximum() { return MAXIMUM; } public boolean isMaximum() { return versionType().equals(Type.MAXIMUM); } public static Version minimum() { return MINIMUM; } public boolean isMinimum() { return versionType().equals(Type.MINIMUM); } /** * Compare this Version object with the other one using their meaningful segments. * *

IMPORTANT: This compareTo implementation is NOT consistent with {@link * Version#equals(Object)} method, i.e. this.compareTo(that) == 0 does not imply * this.equals(that). The reason is that the raw version string is tokenized and certain tokens * are ignored. Tokenized strings are used for {@link #compareTo} comparison while raw version * string is used for {@link #equals} comparison. Be careful when using {@link Version} object in * collections like {@code HashMap} or {@code TreeMap}. * * @param other the other {@link Version} object to be compared with. * @return 0 if the segments of the two {@link Version} objects are the same, -1 if this {@link * Version} is less than {@code other}, 1 if this {@link Version} is greater than {@code * other}. */ @Override public int compareTo(Version other) { if ((this.isMinimum() && other.isMinimum()) || (this.isMaximum() && other.isMaximum())) { return 0; } if (this.isMinimum() || other.isMaximum()) { return -1; } if (this.isMaximum() || other.isMinimum()) { return 1; } return ComparisonUtility.compareListWithFillValue( this.segments(), other.segments(), Segment.NULL); } public boolean isLessThan(Version version) { return this.compareTo(version) < 0; } /** Builder for {@link Version}. */ @AutoValue.Builder public abstract static class Builder { public abstract Builder setVersionType(Type value); public abstract Builder setVersionString(String value); public abstract Version build(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy