package be.nikiroo.utils; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; /** * This class describe a program {@link Version}. * * @author niki */ public class Version implements Comparable { private String version; private int major; private int minor; private int patch; private String tag; private int tagVersion; /** * Create a new, empty {@link Version}. * */ public Version() { } /** * Create a new {@link Version} with the given values. * * @param major * the major version * @param minor * the minor version * @param patch * the patch version */ public Version(int major, int minor, int patch) { this(major, minor, patch, null, -1); } /** * Create a new {@link Version} with the given values. * * @param major * the major version * @param minor * the minor version * @param patch * the patch version * @param tag * a tag name for this version */ public Version(int major, int minor, int patch, String tag) { this(major, minor, patch, tag, -1); } /** * Create a new {@link Version} with the given values. * * @param major * the major version * @param minor * the minor version * @param patch * the patch version the patch version * @param tag * a tag name for this version * @param tagVersion * the version of the tagged version */ public Version(int major, int minor, int patch, String tag, int tagVersion) { if (tagVersion >= 0 && tag == null) { throw new java.lang.IllegalArgumentException( "A tag version cannot be used without a tag"); } this.major = major; this.minor = minor; this.patch = patch; this.tag = tag; this.tagVersion = tagVersion; this.version = generateVersion(); } /** * Create a new {@link Version} with the given value, which must be in the * form MAJOR.MINOR.PATCH(-TAG(TAG_VERSION)). * * @param version * the version (MAJOR.MINOR.PATCH, * MAJOR.MINOR.PATCH-TAG or * MAJOR.MINOR.PATCH-TAGVERSIONTAG) */ public Version(String version) { try { String[] tab = version.split("\\."); this.major = Integer.parseInt(tab[0].trim()); this.minor = Integer.parseInt(tab[1].trim()); if (tab[2].contains("-")) { int posInVersion = version.indexOf('.'); posInVersion = version.indexOf('.', posInVersion + 1); String rest = version.substring(posInVersion + 1); int posInRest = rest.indexOf('-'); this.patch = Integer.parseInt(rest.substring(0, posInRest) .trim()); posInVersion = version.indexOf('-'); this.tag = version.substring(posInVersion + 1).trim(); this.tagVersion = -1; StringBuilder str = new StringBuilder(); while (!tag.isEmpty() && tag.charAt(tag.length() - 1) >= '0' && tag.charAt(tag.length() - 1) <= '9') { str.insert(0, tag.charAt(tag.length() - 1)); tag = tag.substring(0, tag.length() - 1); } if (str.length() > 0) { this.tagVersion = Integer.parseInt(str.toString()); } } else { this.patch = Integer.parseInt(tab[2].trim()); this.tag = null; this.tagVersion = -1; } this.version = generateVersion(); } catch (Exception e) { this.major = 0; this.minor = 0; this.patch = 0; this.tag = null; this.tagVersion = -1; this.version = null; } } /** * The 'major' version. *

* This version should only change when API-incompatible changes are made to * the program. * * @return the major version */ public int getMajor() { return major; } /** * The 'minor' version. *

* This version should only change when new, backwards-compatible * functionality has been added to the program. * * @return the minor version */ public int getMinor() { return minor; } /** * The 'patch' version. *

* This version should change when backwards-compatible bugfixes have been * added to the program. * * @return the patch version */ public int getPatch() { return patch; } /** * A tag name for this version. * * @return the tag */ public String getTag() { return tag; } /** * The version of the tag, or -1 for no version. * * @return the tag version */ public int getTagVersion() { return tagVersion; } /** * Check if this {@link Version} is "empty" (i.e., the version was not * parse-able or not given). * * @return TRUE if it is empty */ public boolean isEmpty() { return version == null; } /** * Check if we are more recent than the given {@link Version}. *

* Note that a tagged version is considered newer than a non-tagged version, * but two tagged versions with different tags are not comparable. *

* Also, an empty version is always considered older. * * @param o * the other {@link Version} * @return TRUE if this {@link Version} is more recent than the given one */ public boolean isNewerThan(Version o) { if (isEmpty()) { return false; } else if (o.isEmpty()) { return true; } if (major > o.major) { return true; } if (major == o.major && minor > o.minor) { return true; } if (major == o.major && minor == o.minor && patch > o.patch) { return true; } // a tagged version is considered newer than a non-tagged one if (major == o.major && minor == o.minor && patch == o.patch && tag != null && o.tag == null) { return true; } // 2 <> tagged versions are not comparable boolean sameTag = (tag == null && o.tag == null) || (tag != null && tag.equals(o.tag)); if (major == o.major && minor == o.minor && patch == o.patch && sameTag && tagVersion > o.tagVersion) { return true; } return false; } /** * Check if we are older than the given {@link Version}. *

* Note that a tagged version is considered newer than a non-tagged version, * but two tagged versions with different tags are not comparable. *

* Also, an empty version is always considered older. * * @param o * the other {@link Version} * @return TRUE if this {@link Version} is older than the given one */ public boolean isOlderThan(Version o) { if (o.isEmpty()) { return false; } else if (isEmpty()) { return true; } // 2 <> tagged versions are not comparable boolean sameTag = (tag == null && o.tag == null) || (tag != null && tag.equals(o.tag)); if (major == o.major && minor == o.minor && patch == o.patch && !sameTag) { return false; } return !equals(o) && !isNewerThan(o); } /** * Return the version of the running program if it follows the VERSION * convention (i.e., if it has a file called VERSION containing the version * as a {@link String} in its binary root, and if this {@link String} * follows the Major/Minor/Patch convention). *

* If it does not, return an empty {@link Version} object. * * @return the {@link Version} of the program, or an empty {@link Version} * (does not return NULL) */ public static Version getCurrentVersion() { String version = null; InputStream in = IOUtils.openResource("VERSION"); if (in != null) { try { ByteArrayOutputStream ba = new ByteArrayOutputStream(); IOUtils.write(in, ba); in.close(); version = ba.toString("UTF-8").trim(); } catch (IOException e) { } } return new Version(version); } @Override public int compareTo(Version o) { if (equals(o)) { return 0; } else if (isNewerThan(o)) { return 1; } else { return -1; } } @Override public boolean equals(Object obj) { if (obj instanceof Version) { Version o = (Version) obj; if (isEmpty()) { return o.isEmpty(); } boolean sameTag = (tag == null && o.tag == null) || (tag != null && tag.equals(o.tag)); return o.major == major && o.minor == minor && o.patch == patch && sameTag && o.tagVersion == tagVersion; } return false; } @Override public int hashCode() { return version == null ? 0 : version.hashCode(); } /** * Return a user-readable form of this {@link Version}. */ @Override public String toString() { return version == null ? "[unknown]" : version; } /** * Generate the clean version {@link String} from the current values. * * @return the clean version string */ private String generateVersion() { String tagSuffix = ""; if (tag != null) { tagSuffix = "-" + tag + (tagVersion >= 0 ? Integer.toString(tagVersion) : ""); } return String.format("%d.%d.%d%s", major, minor, patch, tagSuffix); } }