/******************************************************************************* * Copyright (c) 2000, 2015 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation * Sergey Prigogin (Google) - use parameterized types (bug 442021) *******************************************************************************/ package org.eclipse.core.runtime; import java.util.StringTokenizer; import java.util.Vector; import org.eclipse.core.internal.runtime.CommonMessages; import org.eclipse.core.internal.runtime.IRuntimeConstants; import org.eclipse.osgi.util.NLS; import org.osgi.framework.Version; /** * <p> * Version identifier for a plug-in. In its string representation, * it consists of up to 4 tokens separated by a decimal point. * The first 3 tokens are positive integer numbers, the last token * is an uninterpreted string (no whitespace characters allowed). * For example, the following are valid version identifiers * (as strings): * <ul> * <li><code>0.0.0</code></li> * <li><code>1.0.127564</code></li> * <li><code>3.7.2.build-127J</code></li> * <li><code>1.9</code> (interpreted as <code>1.9.0</code>)</li> * <li><code>3</code> (interpreted as <code>3.0.0</code>)</li> * </ul> * </p> * <p> * The version identifier can be decomposed into a major, minor, * service level component and qualifier components. A difference * in the major component is interpreted as an incompatible version * change. A difference in the minor (and not the major) component * is interpreted as a compatible version change. The service * level component is interpreted as a cumulative and compatible * service update of the minor version component. The qualifier is * not interpreted, other than in version comparisons. The * qualifiers are compared using lexicographical string comparison. * </p> * <p> * Version identifiers can be matched as perfectly equal, equivalent, * compatible or greaterOrEqual. * </p><p> * This class can be used without OSGi running. * </p><p> * Clients may instantiate; not intended to be subclassed by clients. * </p> * @see java.lang.String#compareTo(java.lang.String) * @deprecated clients should use {@link org.osgi.framework.Version} instead */ @Deprecated public final class PluginVersionIdentifier { private Version version; private static final String SEPARATOR = "."; //$NON-NLS-1$ /** * Creates a plug-in version identifier from its components. * * @param major major component of the version identifier * @param minor minor component of the version identifier * @param service service update component of the version identifier */ public PluginVersionIdentifier(int major, int minor, int service) { this(major, minor, service, null); } /** * Creates a plug-in version identifier from its components. * * @param major major component of the version identifier * @param minor minor component of the version identifier * @param service service update component of the version identifier * @param qualifier qualifier component of the version identifier. * Qualifier characters that are not a letter or a digit are replaced. */ public PluginVersionIdentifier(int major, int minor, int service, String qualifier) { // Do the test outside of the assert so that they 'Policy.bind' // will not be evaluated each time (including cases when we would // have passed by the assert). if (major < 0) Assert.isTrue(false, NLS.bind(CommonMessages.parse_postiveMajor, major + SEPARATOR + minor + SEPARATOR + service + SEPARATOR + qualifier)); if (minor < 0) Assert.isTrue(false, NLS.bind(CommonMessages.parse_postiveMinor, major + SEPARATOR + minor + SEPARATOR + service + SEPARATOR + qualifier)); if (service < 0) Assert.isTrue(false, NLS.bind(CommonMessages.parse_postiveService, major + SEPARATOR + minor + SEPARATOR + service + SEPARATOR + qualifier)); this.version = new Version(major, minor, service, qualifier); } /** * Creates a plug-in version identifier from the given string. * The string representation consists of up to 4 tokens * separated by decimal point. * For example, the following are valid version identifiers * (as strings): * <ul> * <li><code>0.0.0</code></li> * <li><code>1.0.127564</code></li> * <li><code>3.7.2.build-127J</code></li> * <li><code>1.9</code> (interpreted as <code>1.9.0</code>)</li> * <li><code>3</code> (interpreted as <code>3.0.0</code>)</li> * </ul> * </p> * * @param versionId string representation of the version identifier. * Qualifier characters that are not a letter or a digit are replaced. */ public PluginVersionIdentifier(String versionId) { Object[] parts = parseVersion(versionId); version = new Version(((Integer) parts[0]).intValue(), ((Integer) parts[1]).intValue(), ((Integer) parts[2]).intValue(), (String) parts[3]); } /** * Validates the given string as a plug-in version identifier. * * @param version the string to validate * @return a status object with code <code>IStatus.OK</code> if * the given string is valid as a plug-in version identifier, otherwise a status * object indicating what is wrong with the string * @since 2.0 */ public static IStatus validateVersion(String version) { try { parseVersion(version); } catch (RuntimeException e) { return new Status(IStatus.ERROR, IRuntimeConstants.PI_RUNTIME, IStatus.ERROR, e.getMessage(), e); } return Status.OK_STATUS; } private static Object[] parseVersion(String versionId) { // Do the test outside of the assert so that they 'Policy.bind' // will not be evaluated each time (including cases when we would // have passed by the assert). if (versionId == null) Assert.isNotNull(null, CommonMessages.parse_emptyPluginVersion); String s = versionId.trim(); if (s.equals("")) //$NON-NLS-1$ Assert.isTrue(false, CommonMessages.parse_emptyPluginVersion); if (s.startsWith(SEPARATOR)) Assert.isTrue(false, NLS.bind(CommonMessages.parse_separatorStartVersion, s)); if (s.endsWith(SEPARATOR)) Assert.isTrue(false, NLS.bind(CommonMessages.parse_separatorEndVersion, s)); if (s.indexOf(SEPARATOR + SEPARATOR) != -1) Assert.isTrue(false, NLS.bind(CommonMessages.parse_doubleSeparatorVersion, s)); StringTokenizer st = new StringTokenizer(s, SEPARATOR); Vector<String> elements = new Vector<>(4); while (st.hasMoreTokens()) elements.addElement(st.nextToken()); int elementSize = elements.size(); if (elementSize <= 0) Assert.isTrue(false, NLS.bind(CommonMessages.parse_oneElementPluginVersion, s)); if (elementSize > 4) Assert.isTrue(false, NLS.bind(CommonMessages.parse_fourElementPluginVersion, s)); int[] numbers = new int[3]; try { numbers[0] = Integer.parseInt(elements.elementAt(0)); if (numbers[0] < 0) Assert.isTrue(false, NLS.bind(CommonMessages.parse_postiveMajor, s)); } catch (NumberFormatException nfe) { Assert.isTrue(false, NLS.bind(CommonMessages.parse_numericMajorComponent, s)); } try { if (elementSize >= 2) { numbers[1] = Integer.parseInt(elements.elementAt(1)); if (numbers[1] < 0) Assert.isTrue(false, NLS.bind(CommonMessages.parse_postiveMinor, s)); } else numbers[1] = 0; } catch (NumberFormatException nfe) { Assert.isTrue(false, NLS.bind(CommonMessages.parse_numericMinorComponent, s)); } try { if (elementSize >= 3) { numbers[2] = Integer.parseInt(elements.elementAt(2)); if (numbers[2] < 0) Assert.isTrue(false, NLS.bind(CommonMessages.parse_postiveService, s)); } else numbers[2] = 0; } catch (NumberFormatException nfe) { Assert.isTrue(false, NLS.bind(CommonMessages.parse_numericServiceComponent, s)); } // "result" is a 4-element array with the major, minor, service, and qualifier Object[] result = new Object[4]; result[0] = Integer.valueOf(numbers[0]); result[1] = Integer.valueOf(numbers[1]); result[2] = Integer.valueOf(numbers[2]); if (elementSize >= 4) result[3] = elements.elementAt(3); else result[3] = ""; //$NON-NLS-1$ return result; } /** * Compare version identifiers for equality. Identifiers are * equal if all of their components are equal. * * @param object an object to compare * @return whether or not the two objects are equal */ @Override public boolean equals(Object object) { if (!(object instanceof PluginVersionIdentifier)) return false; PluginVersionIdentifier v = (PluginVersionIdentifier) object; return version.equals(v.version); } /** * Returns a hash code value for the object. * * @return an integer which is a hash code value for this object. */ @Override public int hashCode() { return version.hashCode(); } /** * Returns the major (incompatible) component of this * version identifier. * * @return the major version */ public int getMajorComponent() { return version.getMajor(); } /** * Returns the minor (compatible) component of this * version identifier. * * @return the minor version */ public int getMinorComponent() { return version.getMinor(); } /** * Returns the service level component of this * version identifier. * * @return the service level */ public int getServiceComponent() { return version.getMicro(); } /** * Returns the qualifier component of this * version identifier. * * @return the qualifier */ public String getQualifierComponent() { return version.getQualifier(); } /** * 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 service component is * greater than the argument service component, or the major, minor and * service components are equal and the qualifier component is * greater than the argument qualifier component (using lexicographic * string comparison), or all components are equal. * </p> * * @param id the other version identifier * @return <code>true</code> is this version identifier * is compatible with the given version identifier, and * <code>false</code> otherwise * @since 2.0 */ public boolean isGreaterOrEqualTo(PluginVersionIdentifier id) { if (id == null) return false; if (getMajorComponent() > id.getMajorComponent()) return true; if ((getMajorComponent() == id.getMajorComponent()) && (getMinorComponent() > id.getMinorComponent())) return true; if ((getMajorComponent() == id.getMajorComponent()) && (getMinorComponent() == id.getMinorComponent()) && (getServiceComponent() > id.getServiceComponent())) return true; if ((getMajorComponent() == id.getMajorComponent()) && (getMinorComponent() == id.getMinorComponent()) && (getServiceComponent() == id.getServiceComponent()) && (getQualifierComponent().compareTo(id.getQualifierComponent()) >= 0)) 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 service level of the * version identifier must be greater than or equal to the service level * of the argument identifier. If the service levels are equal, the two * version identifiers are considered to be equivalent if this qualifier is * greater or equal to the qualifier of the argument (using lexicographic * string comparison). * </p> * * @param id the other version identifier * @return <code>true</code> is this version identifier * is compatible with the given version identifier, and * <code>false</code> otherwise */ public boolean isCompatibleWith(PluginVersionIdentifier id) { if (id == null) return false; if (getMajorComponent() != id.getMajorComponent()) return false; if (getMinorComponent() > id.getMinorComponent()) return true; if (getMinorComponent() < id.getMinorComponent()) return false; if (getServiceComponent() > id.getServiceComponent()) return true; if (getServiceComponent() < id.getServiceComponent()) return false; if (getQualifierComponent().compareTo(id.getQualifierComponent()) >= 0) return true; return false; } /** * Compares two version identifiers for equivalency. * <p> * Two version identifiers are considered to be equivalent if their major * and minor component equal and are at least at the same service level * as the argument. If the service levels are equal, the two version * identifiers are considered to be equivalent if this qualifier is * greater or equal to the qualifier of the argument (using lexicographic * string comparison). * * </p> * * @param id the other version identifier * @return <code>true</code> is this version identifier * is equivalent to the given version identifier, and * <code>false</code> otherwise */ public boolean isEquivalentTo(PluginVersionIdentifier id) { if (id == null) return false; if (getMajorComponent() != id.getMajorComponent()) return false; if (getMinorComponent() != id.getMinorComponent()) return false; if (getServiceComponent() > id.getServiceComponent()) return true; if (getServiceComponent() < id.getServiceComponent()) return false; if (getQualifierComponent().compareTo(id.getQualifierComponent()) >= 0) return true; return false; } /** * Compares two version identifiers for perfect equality. * <p> * Two version identifiers are considered to be perfectly equal if their * major, minor, service and qualifier components are equal * </p> * * @param id the other version identifier * @return <code>true</code> is this version identifier * is perfectly equal to the given version identifier, and * <code>false</code> otherwise * @since 2.0 */ public boolean isPerfect(PluginVersionIdentifier id) { if (id == null) return false; if ((getMajorComponent() != id.getMajorComponent()) || (getMinorComponent() != id.getMinorComponent()) || (getServiceComponent() != id.getServiceComponent()) || (!getQualifierComponent().equals(id.getQualifierComponent()))) return false; return true; } /** * Compares two version identifiers for order using multi-decimal * comparison. * * @param id the other version identifier * @return <code>true</code> is this version identifier * is greater than the given version identifier, and * <code>false</code> otherwise */ public boolean isGreaterThan(PluginVersionIdentifier id) { if (id == null) { if (getMajorComponent() == 0 && getMinorComponent() == 0 && getServiceComponent() == 0 && getQualifierComponent().equals("")) //$NON-NLS-1$ return false; return true; } if (getMajorComponent() > id.getMajorComponent()) return true; if (getMajorComponent() < id.getMajorComponent()) return false; if (getMinorComponent() > id.getMinorComponent()) return true; if (getMinorComponent() < id.getMinorComponent()) return false; if (getServiceComponent() > id.getServiceComponent()) return true; if (getServiceComponent() < id.getServiceComponent()) return false; if (getQualifierComponent().compareTo(id.getQualifierComponent()) > 0) return true; return false; } /** * Returns the string representation of this version identifier. * The result satisfies * <code>vi.equals(new PluginVersionIdentifier(vi.toString()))</code>. * * @return the string representation of this plug-in version identifier */ @Override public String toString() { return version.toString(); } }