| /******************************************************************************* |
| * Copyright (c) 2003, 2009 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 |
| * Cloudsmith Inc - rewrite to handle non-OSGi versions. |
| *******************************************************************************/ |
| package org.eclipse.equinox.p2.metadata; |
| |
| import java.io.Serializable; |
| import org.eclipse.equinox.internal.p2.metadata.*; |
| import org.eclipse.osgi.util.NLS; |
| |
| /** |
| * This class represents a version range with Omni Version bounds. It is signature |
| * equivalent with the OSGi {@link org.eclipse.osgi.service.resolver.VersionRange VersionRange} |
| * |
| * @Immutable |
| * @noextend This class is not intended to be subclassed by clients. |
| * @since 2.0 |
| */ |
| public class VersionRange implements Serializable { |
| private static final long serialVersionUID = 4988030307298088028L; |
| |
| /** |
| * TODO: This should not be OSGi but it has to be that for now since the resolver creates |
| * a filter where the min and max are converted into strings. When the filter is evaluated an |
| * attempt is made to recreate them as OSGi versions. |
| * |
| * An empty OSGi Version range. |
| */ |
| public static final VersionRange emptyRange = new VersionRange(Version.emptyVersion, true, Version.MAX_VERSION, true); |
| |
| private final Version minVersion; |
| private final boolean includeMin; |
| private final Version maxVersion; |
| private final boolean includeMax; |
| |
| private static int copyEscaped(String vr, int pos, String breakChars, StringBuffer sb) { |
| int top = vr.length(); |
| pos = VersionParser.skipWhite(vr, pos); |
| if (pos >= top) |
| throw new IllegalArgumentException(); |
| |
| char c = vr.charAt(pos); |
| for (;;) { |
| if (c == '\\' && ++pos < top) |
| c = vr.charAt(pos); |
| else { |
| if (c <= ' ') |
| return VersionParser.skipWhite(vr, pos); |
| if (breakChars != null && breakChars.indexOf(c) >= 0) |
| break; |
| } |
| sb.append(c); |
| if (++pos >= top) |
| break; |
| c = vr.charAt(pos); |
| } |
| return pos; |
| } |
| |
| /** |
| * Constructs a VersionRange with the specified minVersion and maxVersion. |
| * @param minVersion the minimum version of the range |
| * @param maxVersion the maximum version of the range |
| */ |
| public VersionRange(Version minVersion, boolean includeMin, Version maxVersion, boolean includeMax) { |
| if (minVersion == null) { |
| if (maxVersion == null) { |
| minVersion = Version.emptyVersion; |
| maxVersion = Version.MAX_VERSION; |
| } else |
| minVersion = Version.emptyVersion; |
| } else { |
| if (maxVersion == null) |
| maxVersion = Version.MAX_VERSION; |
| else { |
| if (minVersion != maxVersion && minVersion.equals(maxVersion)) |
| maxVersion = minVersion; |
| else if (!(minVersion.getFormat() == null ? maxVersion.getFormat() == null : minVersion.getFormat().equals(maxVersion.getFormat()))) { |
| // We always allow the MIN and MAX boundaries |
| if (!(minVersion.equals(Version.emptyVersion) || maxVersion.equals(Version.MAX_VERSION))) |
| throw new IllegalArgumentException(NLS.bind(Messages.range_boundaries_0_and_1_cannot_have_different_formats, minVersion, maxVersion)); |
| } |
| } |
| } |
| this.minVersion = minVersion; |
| this.includeMin = includeMin; |
| this.maxVersion = maxVersion; |
| this.includeMax = includeMax; |
| validateRange(); |
| } |
| |
| /** |
| * Constructs a VersionRange from the given versionRange String. |
| * @param versionRange a version range String that specifies a range of |
| * versions. |
| */ |
| public VersionRange(String versionRange) { |
| int top = 0; |
| int pos = 0; |
| if (versionRange != null) { |
| top = versionRange.length(); |
| pos = VersionParser.skipWhite(versionRange, 0); |
| top = VersionParser.skipTrailingWhite(versionRange, pos, top); |
| } |
| |
| if (pos >= top) { |
| minVersion = Version.emptyVersion; |
| includeMin = true; |
| maxVersion = Version.MAX_VERSION; |
| includeMax = true; |
| return; |
| } |
| |
| char c = versionRange.charAt(pos); |
| int[] position = new int[1]; |
| boolean rawPrefix = false; |
| IVersionFormat fmt = null; |
| if (VersionParser.isLetter(c)) { |
| if (versionRange.startsWith("raw:", pos)) { //$NON-NLS-1$ |
| rawPrefix = true; |
| pos += 4; |
| } else { |
| position[0] = pos; |
| fmt = parseFormat(versionRange, position); |
| pos = position[0]; |
| if (pos >= versionRange.length()) |
| throw new IllegalArgumentException(NLS.bind(Messages.format_must_be_delimited_by_colon_0, versionRange)); |
| |
| c = versionRange.charAt(pos); |
| if (c != ':') |
| throw new IllegalArgumentException(NLS.bind(Messages.format_must_be_delimited_by_colon_0, versionRange)); |
| ++pos; |
| } |
| pos = VersionParser.skipWhite(versionRange, pos); |
| if (pos >= top) |
| throw new IllegalArgumentException(NLS.bind(Messages.premature_EOS_0, versionRange)); |
| c = versionRange.charAt(pos); |
| } else |
| fmt = VersionFormat.OSGI_FORMAT; |
| |
| String minStr; |
| String maxStr; |
| StringBuffer sb = new StringBuffer(); |
| if (c == '[' || c == '(') { |
| includeMin = (c == '['); |
| pos = copyEscaped(versionRange, ++pos, ",)]", sb); //$NON-NLS-1$ |
| if (pos >= top) |
| throw new IllegalArgumentException(NLS.bind(Messages.premature_EOS_0, versionRange)); |
| c = versionRange.charAt(pos++); |
| if (c != ',') |
| throw new IllegalArgumentException(NLS.bind(Messages.missing_comma_in_range_0, versionRange)); |
| |
| minStr = sb.toString(); |
| sb.setLength(0); |
| pos = copyEscaped(versionRange, pos, ")]", sb); //$NON-NLS-1$ |
| if (pos >= top) |
| throw new IllegalArgumentException(); |
| maxStr = sb.toString(); |
| |
| c = versionRange.charAt(pos++); |
| includeMax = (c == ']'); |
| } else { |
| StringBuffer sbMin = new StringBuffer(); |
| pos = copyEscaped(versionRange, pos, rawPrefix ? "/" : null, sbMin); //$NON-NLS-1$ |
| includeMin = includeMax = true; |
| minStr = sbMin.toString(); |
| maxStr = null; |
| } |
| |
| if (rawPrefix) { |
| String origMin = null; |
| String origMax = null; |
| pos = VersionParser.skipWhite(versionRange, pos); |
| if (pos < top && versionRange.charAt(pos) == '/') { |
| if (++pos == top) |
| throw new IllegalArgumentException(NLS.bind(Messages.original_stated_but_missing_0, versionRange)); |
| position[0] = pos; |
| fmt = parseFormat(versionRange, position); |
| pos = VersionParser.skipWhite(versionRange, position[0]); |
| if (pos < top) { |
| boolean origUseIncDelims = false; |
| c = versionRange.charAt(pos); |
| if (c != ':') |
| throw new IllegalArgumentException(NLS.bind(Messages.original_must_start_with_colon_0, versionRange)); |
| |
| pos = VersionParser.skipWhite(versionRange, ++pos); |
| if (pos == top) |
| throw new IllegalArgumentException(NLS.bind(Messages.original_stated_but_missing_0, versionRange)); |
| |
| c = versionRange.charAt(pos); |
| if (c == '[' || c == '(') { |
| if (includeMin != (c == '[') || maxStr == null) |
| throw new IllegalArgumentException(NLS.bind(Messages.raw_and_original_must_use_same_range_inclusion_0, versionRange)); |
| pos = VersionParser.skipWhite(versionRange, ++pos); |
| origUseIncDelims = true; |
| } |
| |
| sb.setLength(0); |
| if (maxStr == null) { |
| copyEscaped(versionRange, pos, ",])", sb); //$NON-NLS-1$ |
| origMin = sb.toString(); |
| } else { |
| pos = copyEscaped(versionRange, pos, ",])", sb); //$NON-NLS-1$ |
| if (pos >= top) |
| throw new IllegalArgumentException(NLS.bind(Messages.premature_EOS_0, versionRange)); |
| c = versionRange.charAt(pos++); |
| if (c != ',') |
| throw new IllegalArgumentException(NLS.bind(Messages.missing_comma_in_range_0, versionRange)); |
| origMin = sb.toString(); |
| |
| sb.setLength(0); |
| pos = copyEscaped(versionRange, pos, "])", sb); //$NON-NLS-1$ |
| if (origUseIncDelims) { |
| if (pos >= top) |
| throw new IllegalArgumentException(NLS.bind(Messages.premature_EOS_0, versionRange)); |
| c = versionRange.charAt(pos++); |
| if (includeMax != (c == ']')) |
| throw new IllegalArgumentException(NLS.bind(Messages.raw_and_original_must_use_same_range_inclusion_0, versionRange)); |
| } |
| origMax = sb.toString(); |
| } |
| } |
| } |
| minVersion = VersionFormat.parseRaw(minStr, fmt, origMin); |
| if (maxStr != null) { |
| if (maxStr.equals(minStr)) |
| maxVersion = minVersion; |
| else |
| maxVersion = VersionFormat.parseRaw(maxStr, fmt, origMax); |
| } else |
| maxVersion = Version.MAX_VERSION; |
| } else { |
| if (fmt == null) |
| fmt = VersionFormat.OSGI_FORMAT; |
| minVersion = fmt.parse(minStr); |
| if (maxStr != null) { |
| if (maxStr.equals(minStr)) |
| maxVersion = minVersion; |
| else |
| maxVersion = fmt.parse(maxStr); |
| } else { |
| maxVersion = Version.MAX_VERSION; |
| } |
| } |
| validateRange(); |
| } |
| |
| private static IVersionFormat parseFormat(String versionRange, int[] position) { |
| int pos = VersionParser.skipWhite(versionRange, position[0]); |
| if (!versionRange.startsWith("format(", pos)) //$NON-NLS-1$ |
| return null; |
| |
| pos += 7; |
| int end = VersionParser.findEndOfFormat(versionRange, pos, versionRange.length()); |
| try { |
| position[0] = end + 1; |
| return VersionFormat.compile(versionRange, pos, end); |
| } catch (VersionFormatException e) { |
| throw new IllegalArgumentException(e.getMessage()); |
| } |
| } |
| |
| /** |
| * Returns the version format. |
| */ |
| public IVersionFormat getFormat() { |
| return minVersion.equals(Version.emptyVersion) ? maxVersion.getFormat() : minVersion.getFormat(); |
| } |
| |
| /** |
| * Returns the minimum Version of this VersionRange |
| * @return the minimum Version of this VersionRange |
| */ |
| public Version getMinimum() { |
| return minVersion; |
| } |
| |
| /** |
| * Indicates if the minimum version is included in the version range. |
| * @return true if the minimum version is included in the version range; |
| * otherwise false is returned |
| */ |
| public boolean getIncludeMinimum() { |
| return includeMin; |
| } |
| |
| /** |
| * Returns the maximum Version of this VersionRange |
| * @return the maximum Version of this VersionRange |
| */ |
| public Version getMaximum() { |
| return maxVersion; |
| } |
| |
| /** |
| * Indicates if the maximum version is included in the version range. |
| * @return true if the maximum version is included in the version range; |
| * otherwise false is returned |
| */ |
| public boolean getIncludeMaximum() { |
| return includeMax; |
| } |
| |
| public VersionRange intersect(VersionRange r2) { |
| int minCompare = minVersion.compareTo(r2.getMinimum()); |
| int maxCompare = maxVersion.compareTo(r2.getMaximum()); |
| |
| boolean resultMinIncluded; |
| Version resultMin; |
| if (minCompare == 0) { |
| if (maxCompare == 0 && includeMin == r2.getIncludeMinimum() && includeMax == r2.getIncludeMaximum()) |
| return this; |
| resultMin = minVersion; |
| resultMinIncluded = includeMin && r2.getIncludeMinimum(); |
| } else if (minCompare < 0) { |
| resultMin = r2.getMinimum(); |
| resultMinIncluded = r2.getIncludeMinimum(); |
| } else { // minCompare > 0) |
| resultMin = minVersion; |
| resultMinIncluded = includeMin; |
| } |
| |
| boolean resultMaxIncluded; |
| Version resultMax; |
| if (maxCompare > 0) { |
| resultMax = r2.getMaximum(); |
| resultMaxIncluded = r2.getIncludeMaximum(); |
| } else if (maxCompare < 0) { |
| resultMax = maxVersion; |
| resultMaxIncluded = includeMax; |
| } else {//maxCompare == 0 |
| resultMax = maxVersion; |
| resultMaxIncluded = includeMax && r2.getIncludeMaximum(); |
| } |
| |
| int minMaxCmp = resultMin.compareTo(resultMax); |
| if (minMaxCmp < 0 || (minMaxCmp == 0 && resultMinIncluded && resultMaxIncluded)) |
| return new VersionRange(resultMin, resultMinIncluded, resultMax, resultMaxIncluded); |
| |
| return null; |
| } |
| |
| /** |
| * Returns whether the given version is included in this VersionRange. |
| * This will depend on the minimum and maximum versions of this VersionRange |
| * and the given version. |
| * |
| * @param version a version to be tested for inclusion in this VersionRange. |
| * (may be <code>null</code>) |
| * @return <code>true</code> if the version is include, |
| * <code>false</code> otherwise |
| */ |
| public boolean isIncluded(Version version) { |
| if (version == null) |
| return false; |
| |
| if (minVersion == maxVersion) |
| // Can only happen when both includeMin and includeMax are true |
| return minVersion.equals(version); |
| |
| int minCheck = includeMin ? 0 : -1; |
| int maxCheck = includeMax ? 0 : 1; |
| return minVersion.compareTo(version) <= minCheck && maxVersion.compareTo(version) >= maxCheck; |
| } |
| |
| /** |
| * Checks if the versions of this range is in compliance with the OSGi version spec. |
| * @return A flag indicating whether the range is OSGi compatible or not. |
| */ |
| public boolean isOSGiCompatible() { |
| return minVersion.isOSGiCompatible() && maxVersion.isOSGiCompatible(); |
| } |
| |
| public boolean equals(Object object) { |
| if (!(object instanceof VersionRange)) |
| return false; |
| VersionRange vr = (VersionRange) object; |
| return includeMin == vr.includeMin && includeMax == vr.includeMax && minVersion.equals(vr.getMinimum()) && maxVersion.equals(vr.getMaximum()); |
| } |
| |
| public int hashCode() { |
| final int prime = 31; |
| int result = 1; |
| result = prime * result + maxVersion.hashCode(); |
| result = prime * result + minVersion.hashCode(); |
| result = prime * result + (includeMax ? 1231 : 1237); |
| result = prime * result + (includeMin ? 1231 : 1237); |
| return result; |
| } |
| |
| public String toString() { |
| StringBuffer result = new StringBuffer(); |
| toString(result); |
| return result.toString(); |
| } |
| |
| public void toString(StringBuffer result) { |
| boolean gtEqual = includeMin && includeMax && Version.MAX_VERSION.equals(maxVersion); |
| if (gtEqual && Version.emptyVersion.equals(minVersion)) { |
| minVersion.toString(result); |
| return; |
| } |
| |
| IVersionFormat fmt = getFormat(); |
| if (fmt == VersionFormat.OSGI_FORMAT) { |
| if (gtEqual) { |
| minVersion.toString(result); |
| } else { |
| result.append(includeMin ? '[' : '('); |
| minVersion.toString(result); |
| result.append(','); |
| maxVersion.toString(result); |
| result.append(includeMax ? ']' : ')'); |
| } |
| return; |
| } |
| |
| result.append("raw:"); //$NON-NLS-1$ |
| if (gtEqual) { |
| ((BasicVersion) minVersion).rawToString(result, true); |
| } else { |
| result.append(includeMin ? '[' : '('); |
| ((BasicVersion) minVersion).rawToString(result, true); |
| result.append(','); |
| ((BasicVersion) maxVersion).rawToString(result, true); |
| result.append(includeMax ? ']' : ')'); |
| } |
| boolean hasOriginal = (minVersion.getOriginal() != null || maxVersion.getOriginal() != null); |
| if (fmt != null || hasOriginal) { |
| result.append('/'); |
| if (fmt != null) |
| fmt.toString(result); |
| if (hasOriginal) { |
| result.append(':'); |
| if (gtEqual) { |
| ((BasicVersion) minVersion).originalToString(result, true); |
| } else { |
| if (Version.emptyVersion.equals(minVersion)) |
| ((BasicVersion) minVersion).rawToString(result, true); |
| else |
| ((BasicVersion) minVersion).originalToString(result, true); |
| result.append(','); |
| ((BasicVersion) maxVersion).originalToString(result, true); |
| } |
| } |
| } |
| } |
| |
| // Preserve singletons during deserialization |
| private Object readResolve() { |
| VersionRange vr = this; |
| if (equals(emptyRange)) |
| vr = emptyRange; |
| return vr; |
| } |
| |
| private void validateRange() { |
| int cmp = minVersion.compareTo(maxVersion); |
| if (!(cmp < 0 || (cmp == 0 && includeMin && includeMax))) |
| throw new IllegalArgumentException(NLS.bind(Messages.range_min_0_is_not_less_then_range_max_1, minVersion, maxVersion)); |
| } |
| |
| public static org.eclipse.osgi.service.resolver.VersionRange toOSGiVersionRange(VersionRange range) { |
| if (range.equals(emptyRange)) |
| return org.eclipse.osgi.service.resolver.VersionRange.emptyRange; |
| return new org.eclipse.osgi.service.resolver.VersionRange(Version.toOSGiVersion(range.getMinimum()), range.getIncludeMinimum(), Version.toOSGiVersion(range.getMaximum()), range.getIncludeMinimum()); |
| } |
| |
| public static VersionRange fromOSGiVersionRange(org.eclipse.osgi.service.resolver.VersionRange range) { |
| if (range.equals(org.eclipse.osgi.service.resolver.VersionRange.emptyRange)) |
| return emptyRange; |
| return new VersionRange(Version.fromOSGiVersion(range.getMinimum()), range.getIncludeMinimum(), Version.fromOSGiVersion(range.getMaximum()), range.getIncludeMaximum()); |
| } |
| } |