1 /*
2  * Copyright (C) 2023 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 android.server.wm.jetpack.extensions.util;
18 
19 import android.text.TextUtils;
20 
21 import androidx.annotation.NonNull;
22 import androidx.annotation.Nullable;
23 
24 import java.math.BigInteger;
25 import java.util.regex.Matcher;
26 import java.util.regex.Pattern;
27 
28 
29 /**
30  * Class encapsulating a version with major, minor, patch and description values. Copied from
31  * androidx.window.Version.
32  *
33  * This is used for {@link SidecarUtil}.
34  */
35 public final class Version implements Comparable<Version> {
36     public static final Version UNKNOWN = new Version(0, 0, 0, "");
37 
38     private static final String VERSION_PATTERN_STRING =
39             "(\\d+)(?:\\.(\\d+))(?:\\.(\\d+))(?:-(.+))?";
40 
41     private final int mMajor;
42     private final int mMinor;
43     private final int mPatch;
44     private final String mDescription;
45     // Cached BigInteger value of the version.
46     private BigInteger mBigInteger;
47 
Version(int major, int minor, int patch, String description)48     public Version(int major, int minor, int patch, String description) {
49         mMajor = major;
50         mMinor = minor;
51         mPatch = patch;
52         mDescription = description;
53     }
54 
55     /**
56      * Parses a string to a version object.
57      *
58      * @param versionString string in the format "1.2.3" or "1.2.3-Description"
59      *                      (major.minor.patch[-description])
60      * @return the parsed Version object or {@code null}> if the versionString format is invalid.
61      */
62     @Nullable
parse(String versionString)63     public static Version parse(String versionString) {
64         if (TextUtils.isEmpty(versionString)) {
65             return null;
66         }
67 
68         Matcher matcher = Pattern.compile(VERSION_PATTERN_STRING).matcher(versionString);
69         if (!matcher.matches()) {
70             return null;
71         }
72 
73         int major = Integer.parseInt(matcher.group(1));
74         int minor = Integer.parseInt(matcher.group(2));
75         int patch = Integer.parseInt(matcher.group(3));
76         String description = matcher.group(4) != null ? matcher.group(4) : "";
77         return new Version(major, minor, patch, description);
78     }
79 
80     /** Checks whether the version is in the correct format. */
isValidVersion(String versionString)81     public static boolean isValidVersion(String versionString) {
82         Matcher matcher = Pattern.compile(VERSION_PATTERN_STRING).matcher(versionString);
83         return matcher.matches();
84     }
85 
86     /** Major version of the vendor implementation. */
getMajor()87     public int getMajor() {
88         return mMajor;
89     }
90 
getMinor()91     int getMinor() {
92         return mMinor;
93     }
94 
getPatch()95     int getPatch() {
96         return mPatch;
97     }
98 
getDescription()99     String getDescription() {
100         return mDescription;
101     }
102 
103     @NonNull
104     @Override
toString()105     public String toString() {
106         final StringBuilder sb = new StringBuilder()
107                 .append(getMajor())
108                 .append(".")
109                 .append(getMinor())
110                 .append(".")
111                 .append(getPatch());
112         if (!TextUtils.isEmpty(getDescription())) {
113             sb.append("-").append(getDescription());
114         }
115         return sb.toString();
116     }
117 
118     /**
119      * To compare the major, minor and patch version with another.
120      *
121      * @param other The version to compare to this one.
122      * @return 0 if it have the same major minor and patch version; less than 0 if this version
123      * sorts ahead of <var>other</var>; greater than 0 if this version sorts after <var>other</var>.
124      */
125     @Override
compareTo(@onNull Version other)126     public int compareTo(@NonNull Version other) {
127         return toBigInteger().compareTo(other.toBigInteger());
128     }
129 
130     @NonNull
toBigInteger()131     private BigInteger toBigInteger() {
132         if (mBigInteger == null) {
133             mBigInteger = BigInteger.valueOf(mMajor)
134                     .shiftLeft(32)
135                     .or(BigInteger.valueOf(mMinor))
136                     .shiftLeft(32)
137                     .or(BigInteger.valueOf(mPatch));
138         }
139         return mBigInteger;
140     }
141 
142     @Override
equals(Object obj)143     public boolean equals(Object obj) {
144         if (!(obj instanceof Version)) {
145             return false;
146         }
147 
148         Version otherVersionObj = (Version) obj;
149 
150         // The equals checking ignores the description.
151         return mMajor == otherVersionObj.mMajor
152                 && mMinor == otherVersionObj.mMinor
153                 && mPatch == otherVersionObj.mPatch;
154     }
155 
156     @Override
hashCode()157     public int hashCode() {
158         // The hash code ignores the description.
159         int result = 17;
160         result = result * 31 + mMajor;
161         result = result * 31 + mMinor;
162         result = result * 31 + mPatch;
163         return result;
164     }
165 }
166