/* * Copyright (C) 2010 Brockmann Consult GmbH (info@brockmann-consult.de) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 3 of the License, or (at your option) * any later version. * 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 GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, see http://www.gnu.org/licenses/ */ package com.bc.ceres.core.runtime; import com.bc.ceres.core.Assert; /** * Represents a version identifier. * * @author Norman Fomferra */ public class Version implements Comparable { private String text; private int[] numbers; private String qualifier; public Version(int major, int minor, int micro, String qualifier) { Assert.argument(major >= 0, "major"); Assert.argument(minor >= 0, "minor"); Assert.argument(micro >= 0, "micro"); Assert.notNull(qualifier, "qualifier"); this.text = null; this.numbers = new int[]{major, minor, micro}; this.qualifier = qualifier; } /** * Parses a version text in the form <code>{<number><sep>}[<qualifier>]</code>, where <sep> is * one of '.', '-' or '_'. * If no number was found <code>1.[<qualifier>]</code> is assumed, e.g. "M1", is the same as "1.0.0.M1". * * @param text the text to parse * * @return the version identifier */ public static Version parseVersion(String text) { return new Version(text.trim()); } public int getNumberCount() { return numbers.length; } public int getNumber(int i) { return i < numbers.length ? numbers[i] : 0; } public int getMajor() { return getNumber(0); } public int getMinor() { return getNumber(1); } public int getMicro() { return getNumber(2); } public String getQualifier() { return qualifier; } ///////////////////////////////////////////////////////////////////////// // Private private Version(String text) { int[] numbers = new int[10]; numbers[0] = 1; String qualifier = ""; int numberCount = 0; int startPos = 0; final char EOS = '\0'; for (int pos = 0; pos <= text.length(); pos++) { char c = pos < text.length() ? text.charAt(pos) : EOS; if (isPartSeparator(c) || c == EOS) { if (startPos < pos) { if (numberCount < numbers.length) { numbers[numberCount] = parseInt(text, startPos, pos); numberCount++; startPos = pos + 1; } else { qualifier = text.substring(startPos); break; } } else { qualifier = text.substring(startPos); break; } } else if (!Character.isDigit(c)) { qualifier = text.substring(startPos); break; } } numberCount = numberCount > 0 ? numberCount : 1; this.text = text; this.qualifier = qualifier; this.numbers = new int[numberCount]; System.arraycopy(numbers, 0, this.numbers, 0, numberCount); } private static boolean isPartSeparator(char c) { return c == '.' || c == '-' || c == '_'; } private static int parseInt(String s, int i1, int i2) { int n = 0; int m = 1; for (int i = i1; i < i2; i++) { n *= m; n += (int) s.charAt(i) - (int) '0'; m = 10; } return n; } public static int compare(Version v1, Version v2) { int d = compareVersionNumbers(v1.numbers, v2.numbers); if (d != 0) { return d; } return compareQualifiers(v1.qualifier, v2.qualifier); } private static int compareVersionNumbers(int[] v1, int[] v2) { int n = Math.max(v1.length, v2.length); for (int i = 0; i < n; i++) { int n1 = 0, n2 = 0; if (i >= v1.length) { n2 = v2[i]; } else if (i >= v2.length) { n1 = v1[i]; } else { n1 = v1[i]; n2 = v2[i]; } int d = n1 - n2; if (d != 0) { return d; } } return 0; } private static int compareQualifiers(String q1, String q2) { int n = Math.max(q1.length(), q2.length()); for (int i = 0; i < n; i++) { char c1, c2; if (i >= q1.length()) { c2 = q2.charAt(i); c1 = deriveMissingQualifierCharacter(c2); } else if (i >= q2.length()) { c1 = q1.charAt(i); c2 = deriveMissingQualifierCharacter(c1); } else { c1 = q1.charAt(i); c2 = q2.charAt(i); } int d = (int) c1 - (int) c2; if (d != 0) { return d; } } return 0; } private static char deriveMissingQualifierCharacter(char c) { if (Character.isDigit(c)) { return '0'; // Compare missing digit with '0' } else if (Character.isLowerCase(c)) { return 'z'; // Compare missing lower letter with 'z' } else if (Character.isUpperCase(c)) { return 'Z'; // Compare missing upper letter with 'Z' } else { return c; // Other charaters are not compared } } /** * Returns the string representation of the version in the * form <code>{major}.{minor}.{micro}-{qualifier}</code>. * * @return a string representation of the version. */ @Override public String toString() { if (text == null) { StringBuilder sb = new StringBuilder(16); for (int versionNumber : numbers) { if (sb.length() > 0) { sb.append('.'); } sb.append(versionNumber); } if (!qualifier.isEmpty()) { sb.append('-'); sb.append(qualifier); } text = sb.toString(); } return text; } @Override public int compareTo(Object o) { Version other = (Version) o; return compare(this, other); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (obj instanceof Version) { Version other = (Version) obj; return compare(this, other) == 0; } return false; } @Override public int hashCode() { int n = 0; for (int versionNumber : numbers) { n += versionNumber; n *= 17; } return n + qualifier.hashCode(); } }