1 /*
2  * Copyright 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.camera.extensions.internal;
18 
19 import android.text.TextUtils;
20 
21 import com.google.auto.value.AutoValue;
22 
23 import org.jspecify.annotations.NonNull;
24 import org.jspecify.annotations.Nullable;
25 
26 import java.math.BigInteger;
27 import java.util.Objects;
28 import java.util.regex.Matcher;
29 import java.util.regex.Pattern;
30 
31 /**
32  * Class encapsulating a version with major, minor, patch and description values.
33  */
34 @AutoValue
35 public abstract class Version implements Comparable<Version> {
36     public static final Version VERSION_1_0 = Version.create(1, 0, 0, "");
37     public static final Version VERSION_1_1 = Version.create(1, 1, 0, "");
38     public static final Version VERSION_1_2 = Version.create(1, 2, 0, "");
39     public static final Version VERSION_1_3 = Version.create(1, 3, 0, "");
40     public static final Version VERSION_1_4 = Version.create(1, 4, 0, "");
41     public static final Version VERSION_1_5 = Version.create(1, 5, 0, "");
42 
43     private static final Pattern VERSION_STRING_PATTERN =
44             Pattern.compile("(\\d+)(?:\\.(\\d+))(?:\\.(\\d+))(?:\\-(.+))?");
45 
46     /**
47      * Parses a string to a version object.
48      *
49      * @param versionString string in the format "1.2.3" or "1.2.3-Description"
50      *                      (major.minor.patch[-description])
51      * @return the parsed Version object or <tt>null</tt> if the versionString format is invalid.
52      */
parse(@onNull String versionString)53     public static @Nullable Version parse(@NonNull String versionString) {
54         if (TextUtils.isEmpty(versionString)) {
55             return null;
56         }
57 
58         Matcher matcher = VERSION_STRING_PATTERN.matcher(versionString);
59         if (!matcher.matches()) {
60             return null;
61         }
62 
63         int major = Integer.parseInt(matcher.group(1));
64         int minor = Integer.parseInt(matcher.group(2));
65         int patch = Integer.parseInt(matcher.group(3));
66         String description = matcher.group(4) != null ? matcher.group(4) : "";
67         return create(major, minor, patch, description);
68     }
69 
70     /**
71      * Creates a new instance of the Version object with the given parameters.
72      */
create(int major, int minor, int patch, @NonNull String description)73     public static @NonNull Version create(int major, int minor, int patch,
74             @NonNull String description) {
75         return new AutoValue_Version(major, minor, patch, description);
76     }
77 
78     /** Prevent subclassing. */
Version()79     Version() {
80     }
81 
82     /**
83      * Gets the major version number.
84      */
getMajor()85     public abstract int getMajor();
86 
getMinor()87     abstract int getMinor();
88 
getPatch()89     abstract int getPatch();
90 
getDescription()91     abstract String getDescription();
92 
93     @Override
toString()94     public final @NonNull String toString() {
95         StringBuilder sb = new StringBuilder(getMajor() + "." + getMinor() + "." + getPatch());
96         if (!TextUtils.isEmpty(getDescription())) {
97             sb.append("-" + getDescription());
98         }
99         return sb.toString();
100     }
101 
102     /**
103      * To compare the major, minor and patch version with another.
104      *
105      * @param other The preference to compare to this one.
106      * @return 0 if it have the same major minor and patch version; less than 0 if this
107      * preference sorts ahead of <var>other</var>; greater than 0 if this preference sorts after
108      * <var>other</var>.
109      */
110     @Override
compareTo(@onNull Version other)111     public int compareTo(@NonNull Version other) {
112         return createBigInteger(this).compareTo(createBigInteger(other));
113     }
114 
115     /**
116      * To compare the major number with the input value.
117      */
compareTo(int majorVersion)118     public int compareTo(int majorVersion) {
119         return compareTo(majorVersion, 0);
120     }
121 
122     /**
123      * To compare the major and minor numbers with the input values.
124      */
compareTo(int majorVersion, int minorVersion)125     public int compareTo(int majorVersion, int minorVersion) {
126         if (getMajor() == majorVersion) {
127             return Integer.compare(getMinor(), minorVersion);
128         }
129         return Integer.compare(getMajor(), majorVersion);
130     }
131 
createBigInteger(Version version)132     private static BigInteger createBigInteger(Version version) {
133         return BigInteger.valueOf(version.getMajor())
134                 .shiftLeft(32)
135                 .or(BigInteger.valueOf(version.getMinor()))
136                 .shiftLeft(32)
137                 .or(BigInteger.valueOf(version.getPatch()));
138     }
139 
140     @Override
equals(Object obj)141     public final boolean equals(Object obj) {
142         if (!(obj instanceof Version)) {
143             return false;
144         }
145 
146         Version otherVersionObj = (Version) obj;
147 
148         // The equals checking ignores the description.
149         return Objects.equals(getMajor(), otherVersionObj.getMajor())
150                 && Objects.equals(getMinor(), otherVersionObj.getMinor())
151                 && Objects.equals(getPatch(), otherVersionObj.getPatch());
152     }
153 
154     @Override
hashCode()155     public final int hashCode() {
156         // The hash code ignores the description.
157         return Objects.hash(getMajor(), getMinor(), getPatch());
158     }
159 }
160