package org.celllife.idart.misc; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.Locale; import java.util.StringTokenizer; /** * This class represents a plug-in version identifier. * <br> * @version $Id: Version.java,v 1.3 2007/01/20 15:01:54 ddimon Exp $ */ public final class Version implements Serializable, Comparable<Version> { private static final long serialVersionUID = -3054349171116917643L; /** * Version identifier parts separator. */ public static final char SEPARATOR = '.'; /** * Parses given string as version identifier. All missing parts will be * initialized to 0 or empty string. Parsing starts from left side of the * string. * @param str version identifier as string * @return version identifier object */ public static Version parse(final String str) { Version result = new Version(); result.parseString(str); return result; } private transient int major; private transient int minor; private transient int revision; private transient String name; private transient String asString; private Version() { // no-op } private void parseString(final String str) { major = 0; minor = 0; revision = 0; name = ""; //$NON-NLS-1$ StringTokenizer st = new StringTokenizer(str, "" + SEPARATOR, false); //$NON-NLS-1$ // major segment if (!st.hasMoreTokens()) return; String token = st.nextToken(); try { major = Integer.parseInt(token, 10); } catch (NumberFormatException nfe) { name = token; while (st.hasMoreTokens()) { name += st.nextToken(); } return; } // minor segment if (!st.hasMoreTokens()) return; token = st.nextToken(); try { minor = Integer.parseInt(token, 10); } catch (NumberFormatException nfe) { name = token; while (st.hasMoreTokens()) { name += st.nextToken(); } return; } // revision segment if (!st.hasMoreTokens()) return; token = st.nextToken(); try { revision = Integer.parseInt(token, 10); } catch (NumberFormatException nfe) { name = token; while (st.hasMoreTokens()) { name += st.nextToken(); } return; } // name segment if (st.hasMoreTokens()) { name = st.nextToken(); while (st.hasMoreTokens()) { name += st.nextToken(); } } } /** * Creates version identifier object from given parts. No validation * performed during object instantiation, all values become parts of version * identifier as they are. * * @param aMajor * major version number * @param aMinor * minor version number * @param aRevision * revision number * @param aName * revision name, <code>null</code> value becomes empty string */ public Version(final int aMajor, final int aMinor, final int aRevision, final String aName) { major = aMajor; minor = aMinor; revision = aRevision; name = (aName == null) ? "" : aName; //$NON-NLS-1$ } /** * @return revision number */ public int getRevision() { return revision; } /** * @return major version number */ public int getMajor() { return major; } /** * @return minor version number */ public int getMinor() { return minor; } /** * @return revision name */ public String getName() { return name; } /** * Compares two version identifiers to see if this one is greater than or * equal to the argument. * <p> * A version identifier is considered to be greater than or equal if its * major component is greater than the argument major component, or the * major components are equal and its minor component is greater than the * argument minor component, or the major and minor components are equal and * its revision component is greater than the argument revision component, * or all components are equal. * </p> * * @param other * the other version identifier * @return <code>true</code> if this version identifier is compatible with * the given version identifier, and <code>false</code> otherwise */ public boolean isGreaterOrEqualTo(final Version other) { if (other == null) return false; if (major > other.major) return true; if ((major == other.major) && (minor > other.minor)) return true; if ((major == other.major) && (minor == other.minor) && (revision > other.revision)) return true; if ((major == other.major) && (minor == other.minor) && (revision == other.revision) && name.equalsIgnoreCase(other.name)) return true; return false; } /** * Compares two version identifiers for compatibility. * <p> * A version identifier is considered to be compatible if its major * component equals to the argument major component, and its minor component * is greater than or equal to the argument minor component. If the minor * components are equal, than the revision component of the version * identifier must be greater than or equal to the revision component of the * argument identifier. * </p> * * @param other * the other version identifier * @return <code>true</code> if this version identifier is compatible with * the given version identifier, and <code>false</code> otherwise */ public boolean isCompatibleWith(final Version other) { if (other == null) return false; if (major != other.major) return false; if (minor > other.minor) return true; if (minor < other.minor) return false; if (revision >= other.revision) return true; return false; } /** * Compares two version identifiers for equivalency. * <p> * Two version identifiers are considered to be equivalent if their major * and minor components equal and are at least at the same revision level as * the argument. * </p> * * @param other * the other version identifier * @return <code>true</code> if this version identifier is equivalent to the * given version identifier, and <code>false</code> otherwise */ public boolean isEquivalentTo(final Version other) { if (other == null) return false; if (major != other.major) return false; if (minor != other.minor) return false; if (revision >= other.revision) return true; return false; } /** * Compares two version identifiers for order using multi-decimal * comparison. * * @param other the other version identifier * @return <code>true</code> if this version identifier * is greater than the given version identifier, and * <code>false</code> otherwise */ public boolean isGreaterThan(final Version other) { if (other == null) return false; if (major > other.major) return true; if (major < other.major) return false; if (minor > other.minor) return true; if (minor < other.minor) return false; if (revision > other.revision) return true; return false; } /** * @see java.lang.Object#hashCode() */ @Override public int hashCode() { return toString().hashCode(); } /** * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(final Object obj) { if (this == obj) return true; if (!(obj instanceof Version)) return false; Version other = (Version) obj; if ((major != other.major) || (minor != other.minor) || (revision != other.revision) || !name.equalsIgnoreCase(other.name)) return false; return true; } /** * Returns the string representation of this version identifier. * The result satisfies * <code>version.equals(new Version(version.toString()))</code>. * @return the string representation of this version identifier */ @Override public String toString() { if (asString == null) { asString = "" + major + SEPARATOR + minor + SEPARATOR + revision //$NON-NLS-1$ + (name.length() == 0 ? "" : SEPARATOR + name); //$NON-NLS-1$ } return asString; } /** * @param obj version to compare this instance with * @return comparison result * @see java.lang.Comparable#compareTo(java.lang.Object) */ @Override public int compareTo(final Version obj) { if (equals(obj)) return 0; if (major != obj.major) return major - obj.major; if (minor != obj.minor) return minor - obj.minor; if (revision != obj.revision) return revision - obj.revision; return name.toLowerCase(Locale.ENGLISH).compareTo( obj.name.toLowerCase(Locale.ENGLISH)); } // Serialization related stuff. private void writeObject(final ObjectOutputStream out) throws IOException { out.writeUTF(toString()); } private void readObject(final ObjectInputStream in) throws IOException { parseString(in.readUTF()); } }