package net.pms.util; import java.util.Arrays; public final class Version implements Comparable<Version> { private final static int MAX_ELEMENTS = 4; private final int[] elements; public Version(String versionString) { if (versionString == null) { throw new NullPointerException("Version string can not be null"); } elements = parse(versionString.split("\\.", MAX_ELEMENTS)); } private int[] parse(String[] elements) { int[] out = new int[elements.length]; for (int i = 0; i < elements.length; i++) { try { out[i] = Integer.parseInt(elements[i]); } catch (NumberFormatException e) { out[i] = 0; } } return out; } /** * Compares this version to the supplied version and returns * an <code>int</code> which indicates whether this version is * less than, equal to, or greater than the supplied version. * * @param other version to compare this version to * @return less than zero if this version is lower, 0 if they're * equal, or greater than zero if this version is higher */ public int compareTo(Version other) { final int[] longerElements, shorterElements; final int sign; if (elements.length >= other.elements.length) { longerElements = elements; shorterElements = other.elements; sign = 1; } else { longerElements = other.elements; shorterElements = elements; sign = -1; } for (int i = 0; i < longerElements.length; ++i) { int val = i < shorterElements.length ? shorterElements[i] : 0; if (longerElements[i] != val) { return (longerElements[i] - val) * sign; } } return 0; } /** * Returns true if this version equals the supplied object, * false otherwise * @param other object to be compared with this version * @return true if this version is equal to the supplied object, false otherwise */ @Override public boolean equals(Object other) { if (other instanceof Version) { return compareTo((Version)other) == 0; } else { return false; } } /** * Returns true if this version is less than the supplied * version, false otherwise * @param other version to be compared with this version * @return true if this version is less than the supplied version, false otherwise */ public boolean isLessThan(Version other) { return compareTo(other) < 0; } /** * Returns true if this version is less than or equal * to the supplied version, false otherwise * @param other version to be compared with this version2yy * @return true if this version is less than or equal to the supplied version, false otherwise */ public boolean isLessThanOrEqualTo(Version other) { return compareTo(other) <= 0; } /** * Returns true if this version is greater than * the supplied version, false otherwise * @param other version to be compared with this version2yy * @return true if this version is greater than the supplied version, false otherwise */ public boolean isGreaterThan(Version other) { return compareTo(other) > 0; } /** * Returns true if this version is greater than or * equal to the supplied version, false otherwise * @param other version to be compared with this version2yy * @return true if this version is greater than or equal to the supplied version,false otherwise */ public boolean isGreaterThanOrEqualTo(Version other) { return compareTo(other) >= 0; } private static int getMostSignificantDigit(int i) { int msd = 0; String msdString = Integer.toString(i).substring(0, 1); try { msd = Integer.parseInt(msdString); } catch (NumberFormatException e) { } return msd; } /** * Compares an initial (current) version and a target version of PMS and * returns true if the initial version can be updated * to the target version. See src/main/external-resources/update/README * for the criteria. * @param vFrom The initial version * @param vTo The target version * @return <code>true</code> if the current version can safely be updated, <code>false</code> otherwise. */ public static boolean isPmsUpdatable(Version vFrom, Version vTo) { return vTo.isGreaterThan(vFrom) && (vFrom.getMajor() == vTo.getMajor()) && (getMostSignificantDigit(vFrom.getMinor()) == getMostSignificantDigit(vTo.getMinor())); } /** * Returns the first integer element of this version string, * or 0 if a first integer was not defined or could not be parsed * @return the major number */ public int getMajor() { if (elements.length > 0) { return elements[0]; } else { return 0; } } /** * Returns the second integer element of this version string, * or 0 if a second integer was not defined or could not be parsed * @return the minor number */ public int getMinor() { if (elements.length > 1) { return elements[1]; } else { return 0; } } /** * Returns the third integer element of this version string, * or 0 if a third integer was not defined or could not be parsed * @return the revision number */ public int getRevision() { if (elements.length > 2) { return elements[2]; } else { return 0; } } /** * Returns the fourth integer element of this version string, * or 0 if a fourth integer was not defined or could not be parsed * @return the build number */ public int getBuild() { if (elements.length > 3) { return elements[3]; } else { return 0; } } /** * Returns the element array with trailing zeros * removed - used to ensure e.g. 2.2 and 2.2.0.0 * (normalized to [ 2, 2 ]) have the same hash code. * * If there are trailing zeros, a new element * array with the trailing zeros removed is returned, * otherwise the original element array is returned. * * @return the element array with trailing zeros removed */ private final int[] getCanonicalElements() { int[] canonicalElements; // this default length is used if a) the loop isn't entered // (elements.length == 1) or b) if all trailing digits are 0 int canonicalElementsLength = 1; // find the last (rightmost) non-zero element. for (int i = elements.length - 1; i > 0; --i) { if (elements[i] != 0) { canonicalElementsLength = i + 1; break; } } if (canonicalElementsLength == elements.length) { canonicalElements = elements; } else { canonicalElements = new int[canonicalElementsLength]; System.arraycopy(elements, 0, canonicalElements, 0, canonicalElementsLength); } return canonicalElements; } /** * Returns this version's hash code * * @return the hash code */ @Override public int hashCode() { return Arrays.hashCode(getCanonicalElements()); } /** * Returns a string representation of the version. The format of the string returned is "major.minor.revision.build". * * @return The string representation of the version. */ // XXX guava (1.8 MB): Ints.join(".", elements) @Override public String toString() { StringBuffer buf = new StringBuffer(); if (elements.length > 0) { buf.append(elements[0]); } for (int i = 1; i < elements.length; ++i) { buf.append("."); buf.append(elements[i]); } return buf.toString(); } }