/* Copyright (C) 2009 Mobile Sorcery AB This program is free software; you can redistribute it and/or modify it under the terms of the Eclipse Public License v1.0. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the Eclipse Public License v1.0 for more details. You should have received a copy of the Eclipse Public License v1.0 along with this program. It is also available at http://www.eclipse.org/legal/epl-v10.html */ package com.mobilesorcery.sdk.core; /** * <p>A class representing the most common ways to write versions. This class can handle * these kinds of version representations:</p> * <ul> * <li>1</li> * <li>1.0</li> * <li>1.0.0</li> * <li>1.0b</li> * <li>1.0.0b</li> * <li>1.0.0.b</li> * </ul> * <p>In all the above examples, "b" is retrieved using the method <code>getQualifier()</code><p> * @author Mattias Bybro, mattias.bybro@purplescout.se * */ public class Version implements Comparable<Version> { public static final int UNDEFINED = -1; public static final int MAJOR = 1; public static final int MINOR = 2; public static final int MICRO = 3; public static final int QUALIFIER = 4; private int major = UNDEFINED; private int minor = UNDEFINED; private int micro = UNDEFINED; private String qualifier; private String version; private boolean valid = true; public Version(String version) { this.version = version; parse(version); } public Version truncate(int level) { return new Version(asCanonicalString(level)); } private void parse(String version) { String[] components = version.split("\\.", 4); major = parseInternal(components, 0); minor = parseInternal(components, 1); micro = parseInternal(components, 2); parseInternal(components, 3); valid &= major != UNDEFINED; } private int parseInternal(String[] components, int ix) { if (components.length <= ix) { return UNDEFINED; } int intIx = 0; while (intIx < components[ix].length() && Character.isDigit(components[ix].charAt(intIx))) { intIx++; } // Qualifier is whatever follows a number, or if non-numeric. if (Util.isEmpty(qualifier) && components[ix].length() > intIx - 1 || intIx == 0) { char[] qualifier = components[ix].substring(intIx).toCharArray(); for (int i = 0; i < qualifier.length; i++) { // We replace any non-alphanumeric character with an underscore if (!Character.isLetterOrDigit(qualifier[i])) { qualifier[i] = '_'; valid = false; } } this.qualifier = new String(qualifier); } return intIx == 0 ? UNDEFINED : Integer.parseInt(components[ix].substring(0, intIx)); } public String toString() { return version; } public String asCanonicalString() { return asCanonicalString(QUALIFIER); } public String asCanonicalString(int level) { StringBuffer result = new StringBuffer(); if (level >= MAJOR) addIfDefined(result, false, major); if (level >= MINOR) addIfDefined(result, true, minor); if (level >= MICRO) addIfDefined(result, true, micro); if (level >= QUALIFIER && !Util.isEmpty(qualifier)) { result.append('.' + qualifier); } return result.toString(); } private void addIfDefined(StringBuffer result, boolean addDelimiter, int major) { if (major != UNDEFINED) { if (addDelimiter) { result.append('.'); } result.append(major); } } public int getMajor() { return major; } public int getMinor() { return minor; } public int getMicro() { return micro; } public String getQualifier() { return qualifier; } public boolean isValid() { return valid; } public boolean equals(Object o) { if (o instanceof Version) { return equals((Version) o); } return false; } public boolean equals(Version v) { return equals(v, QUALIFIER); } public boolean equals(Version v, int level) { return getMajor() == v.getMajor()&& (getMinor() == v.getMinor() || level < MINOR) && (getMicro() == v.getMicro() || level < MICRO) && (Util.equals(getQualifier(), v.getQualifier()) || level < QUALIFIER); } /** * Returns whether this version is * "before" another version; ie when * it can be concluded from the major, minor, micro * whether this version is older than * another. (So the qualifier is always disregarded). * @return */ public boolean isOlder(Version other) { if (other == null) { return false; } if (getMajor() < other.getMajor()) { return true; } else if (getMajor() == other.getMajor()) { if (getMinor() < other.getMinor()) { return true; } else if (getMinor() == other.getMinor()) { return getMicro() < other.getMicro(); } } return false; } /** * Returns whether this version is * "after" another version; ie when * it can be concluded from the major, minor, micro * whether this version is newer than * another. (So the qualifier is always disregarded). * @return */ public boolean isNewer(Version other) { if (other == null) { return false; } if (getMajor() > other.getMajor()) { return true; } else if (getMajor() == other.getMajor()) { if (getMinor() > other.getMinor()) { return true; } else if (getMinor() == other.getMinor()) { return getMicro() > other.getMicro(); } } return false; } @Override public int compareTo(Version version) { if (equals(version, MICRO)) { return Util.compare(getQualifier(), version.getQualifier()); } else if (isNewer(version)) { return +1; } else { return -1; } } }