package org.edx.mobile.util;
import android.support.annotation.NonNull;
import java.text.ParseException;
import java.util.Arrays;
/**
* Simple representation of the app's version.
*/
public class Version implements Comparable<Version> {
/**
* The number of version number tokens to parse.
*/
private static final int NUMBERS_COUNT = 3;
/**
* The version numbers
*/
@NonNull
private final int[] numbers = new int[3];
/**
* Create a new instance from the provided version string.
*
* @param version The version string. The first three present dot-separated
* tokens will be parsed as major, minor, and patch version
* numbers respectively, and any further tokens will be
* discarded.
* @throws ParseException If one or more of the first three present dot-
* separated tokens contain non-numeric characters.
*/
public Version(@NonNull String version) throws ParseException {
final String[] numberStrings = version.split("\\.");
final int versionsCount = Math.min(NUMBERS_COUNT, numberStrings.length);
for (int i = 0; i < versionsCount; i++) {
final String numberString = numberStrings[i];
/* Integer.parseInt() parses a string as a signed integer value, and
* there is no available method for parsing as unsigned instead.
* Therefore, we first check the first character manually to see
* whether it's a plus or minus sign, and throw a ParseException if
* it is.
*/
final char firstChar = numberString.charAt(0);
if (firstChar == '-' || firstChar == '+') {
throw new VersionParseException(0);
}
try {
numbers[i] = Integer.parseInt(numberString);
} catch (NumberFormatException e) {
// Rethrow as a checked ParseException
throw new VersionParseException(version.indexOf(numberString));
}
}
}
/**
* @return The major version.
*/
public int getMajorVersion() {
return getVersionAt(0);
}
/**
* @return The minor version.
*/
public int getMinorVersion() {
return getVersionAt(1);
}
/**
* @return The patch version.
*/
public int getPatchVersion() {
return getVersionAt(2);
}
/**
* Returns the version number at the provided index.
*
* @param index The index at which to get the version number
* @return The version number.
*/
private int getVersionAt(int index) {
return index < numbers.length ? numbers[index] : 0;
}
@Override
public boolean equals(@NonNull Object object) {
if (this == object) return true;
if (!(object instanceof Version)) return false;
return Arrays.equals(numbers, ((Version) object).numbers);
}
@Override
public int compareTo(@NonNull Version other) {
for (int i = 0; i < NUMBERS_COUNT; i++) {
final int number = numbers[i];
final int otherNumber = other.numbers[i];
if (number != otherNumber) {
return number < otherNumber ? -1 : 1;
}
}
return 0;
}
@Override
public int hashCode() {
return Arrays.hashCode(numbers);
}
@Override
public String toString() {
switch (numbers.length) {
case 0: return "";
case 1: return Integer.toString(numbers[0]);
}
StringBuilder sb = new StringBuilder();
sb.append(numbers[0]);
for (int i = 1; i < numbers.length; i++) {
sb.append(".");
sb.append(numbers[i]);
}
return sb.toString();
}
/**
* Convenience subclass of {@link ParseException}, with the detail
* message already provided.
*/
private static class VersionParseException extends ParseException {
/**
* Constructs a new instance of this class with its stack
* trace, detail message and the location of the error filled
* in.
*
* @param location The location of the token at which the parse
* exception occurred.
*/
public VersionParseException(final int location) {
super("Token couldn't be parsed as a valid number.", location);
}
}
}