diff options
Diffstat (limited to 'bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/VersionVector.java')
-rw-r--r-- | bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/VersionVector.java | 325 |
1 files changed, 325 insertions, 0 deletions
diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/VersionVector.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/VersionVector.java new file mode 100644 index 000000000..c723c8e6b --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/internal/p2/metadata/VersionVector.java @@ -0,0 +1,325 @@ +/******************************************************************************* + * Copyright (c) 2009 Cloudsmith Inc. 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: + * Cloudsmith Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.equinox.internal.p2.metadata; + +import java.io.Serializable; + +/** + * The VersionVector represents an array of Comparable objects. The array can be + * nested since a VersionVector is Comparable in itself. + * + * @Immutable + */ +public class VersionVector implements Comparable<VersionVector>, Serializable { + + interface MinMaxComparable extends Comparable<Object>, Serializable { + // + } + + private static final class MaxStringValue implements MinMaxComparable { + private static final long serialVersionUID = -4936252230441132767L; + + MaxStringValue() { + // Empty constructor + } + + public int compareTo(Object o) { + return o == this ? 0 : (o == MAX_VALUE || o instanceof Integer || o instanceof VersionVector ? -1 : 1); + } + + // For singleton deserialization + private Object readResolve() { + return MAXS_VALUE; + } + + public String toString() { + return "m"; //$NON-NLS-1$ + } + } + + private static final class MaxValue implements MinMaxComparable { + private static final long serialVersionUID = -5889641741635253589L; + + MaxValue() { + // Empty constructor + } + + public int compareTo(Object o) { + return o == this ? 0 : 1; + } + + // For singleton deserialization + private Object readResolve() { + return MAX_VALUE; + } + + public String toString() { + return "M"; //$NON-NLS-1$ + } + } + + private static class MinValue implements MinMaxComparable { + private static final long serialVersionUID = -1066323980049812226L; + + MinValue() { + // Empty constructor + } + + public int compareTo(Object o) { + return o == this ? 0 : -1; + } + + private Object readResolve() { + return MIN_VALUE; + } + + public String toString() { + return "-M"; //$NON-NLS-1$ + } + } + + /** + * A value that is greater then any other value + */ + public static final Comparable<Object> MAX_VALUE = new MaxValue(); + + /** + * A value that is greater then any string but less then {@link #MAX_VALUE} and + * any Integer or VersionVector. + */ + public static final Comparable<Object> MAXS_VALUE = new MaxStringValue(); + + /** + * A value that is less then any other value + */ + public static final Comparable<Object> MIN_VALUE = new MinValue(); + + /** + * A value that is greater then {@link #MIN_VALUE} and less then any string, + * Integer, or VersionVector (a.k.a. empty_string) + */ + public static final String MINS_VALUE = ""; //$NON-NLS-1$ + + private static final long serialVersionUID = -8385373304298723744L; + + static int compare(Comparable<?>[] vectorA, Comparable<?> padA, Comparable<?>[] vectorB, Comparable<?> padB) { + int top = vectorA.length; + if (top > vectorB.length) + top = vectorB.length; + + for (int idx = 0; idx < top; ++idx) { + int cmp = compareSegments(vectorA[idx], vectorB[idx]); + if (cmp != 0) + return cmp; + } + + // All elements compared equal up to this point. Check + // pad values + if (top < vectorA.length) + return (padB == null) ? 1 : compareReminder(top, vectorA, padA, padB); + + if (top < vectorB.length) + return (padA == null) ? -1 : -compareReminder(top, vectorB, padB, padA); + + // Lengths are equal. Compare pad values + return padA == null ? (padB == null ? 0 : -1) : (padB == null ? 1 : compareSegments(padA, padB)); + } + + static boolean equals(Comparable<?>[] vectorA, Comparable<?> padValueA, Comparable<?>[] vectorB, Comparable<?> padValueB) { + // We compare pad first since it is impossible for versions with + // different pad to be equal (versions are padded to infinity) + if (padValueA == null) { + if (padValueB != null) + return false; + } else { + if (padValueB == null || !padValueA.equals(padValueB)) + return false; + } + + int idx = vectorA.length; + + // If the length of the vector differs, the versions cannot be equal + // since segments equal to pad are stripped by the parser + if (idx != vectorB.length) + return false; + + while (--idx >= 0) + if (!vectorA[idx].equals(vectorB[idx])) + return false; + + return true; + } + + static int hashCode(Comparable<?>[] vector, Comparable<?> padValue) { + int hashCode = padValue == null ? 31 : padValue.hashCode(); + int idx = vector.length; + while (--idx >= 0) { + Object elem = vector[idx]; + if (elem != null) + hashCode += elem.hashCode(); + hashCode = hashCode * 31; + } + return hashCode; + } + + static void toString(StringBuffer sb, Comparable<?>[] vector, Comparable<?> padValue, boolean rangeSafe) { + int top = vector.length; + if (top == 0) + // Write one pad value as explicit. It will be considered + // redundant and removed by the parser but the raw format + // does not allow zero elements + VersionFormat.rawToString(sb, rangeSafe, padValue == null ? MIN_VALUE : padValue); + else { + for (int idx = 0; idx < top; ++idx) { + if (idx > 0) + sb.append('.'); + VersionFormat.rawToString(sb, rangeSafe, vector[idx]); + } + } + if (padValue != null) { + sb.append('p'); + VersionFormat.rawToString(sb, rangeSafe, padValue); + } + } + + private static int compareReminder(int idx, Comparable<?>[] vector, Comparable<?> padValue, Comparable<?> othersPad) { + int cmp; + for (cmp = 0; idx < vector.length && cmp == 0; ++idx) + cmp = compareSegments(vector[idx], othersPad); + if (cmp == 0) + cmp = (padValue == null) ? -1 : compareSegments(padValue, othersPad); + return cmp; + } + + static int compareSegments(Comparable<?> a, Comparable<?> b) { + if (a == b) + return 0; + + if (a instanceof Integer && b instanceof Integer) { + int ai = ((Integer) a).intValue(); + int bi = ((Integer) b).intValue(); + return ai > bi ? 1 : (ai < bi ? -1 : 0); + } + + if (a instanceof String && b instanceof String) + return ((String) a).compareTo((String) b); + + if (a == MAX_VALUE || a == MIN_VALUE || a == MAXS_VALUE) + return ((MinMaxComparable) a).compareTo(b); + + if (b == MAX_VALUE || b == MIN_VALUE || b == MAXS_VALUE) + return -((MinMaxComparable) b).compareTo(a); + + if (a instanceof Integer) + return 1; + if (b instanceof Integer) + return -1; + if (a instanceof VersionVector) + return (b instanceof VersionVector) ? ((VersionVector) a).compareTo((VersionVector) b) : 1; + + if (b instanceof VersionVector) + return -1; + + throw new IllegalArgumentException(); + } + + private final Comparable<?> padValue; + + private final Comparable<?>[] vector; + + public VersionVector(Comparable<?>[] vector, Comparable<?> pad) { + this.vector = vector; + this.padValue = (pad == MIN_VALUE) ? null : pad; + } + + public int compareTo(VersionVector ov) { + if (ov == this) + return 0; + + return compare(vector, padValue, ov.vector, ov.padValue); + } + + public boolean equals(Object o) { + if (o == this) + return true; + + if (!(o instanceof VersionVector)) + return false; + + VersionVector ov = (VersionVector) o; + return equals(vector, padValue, ov.vector, ov.padValue); + } + + /** + * Returns the pad value used when comparing this versions to + * versions that has a raw vector with a larger number of elements + * @return The pad value or <code>null</code> if not set. + */ + public Comparable<?> getPad() { + return padValue; + } + + /** + * An element from the raw vector + * @param index The zero based index of the desired element + * @return An element from the raw vector + */ + public Comparable<?> getSegment(int index) { + return vector[index]; + } + + /** + * Returns the number of elements in the raw vector + * @return The element count + */ + public int getSegmentCount() { + return vector.length; + } + + /** + * This method is package protected since it violates the immutable + * contract. + * @return The raw vector. Must be treated as read-only + */ + Comparable<?>[] getVector() { + return vector; + } + + public int hashCode() { + return hashCode(vector, padValue); + } + + public String toString() { + StringBuffer sb = new StringBuffer(); + toString(sb); + return sb.toString(); + } + + /** + * Append the string representation of this instance to the + * <code>sb</code> buffer. + * @param sb The buffer to append to + */ + public void toString(StringBuffer sb) { + toString(sb, vector, padValue, false); + } + + /** + * Append the string representation of this instance to the + * <code>sb</code> buffer. + * @param sb The buffer to append to + * @param rangeSafe If <code>true</code>, the range delimiters will be escaped + * with backslash. + */ + void toString(StringBuffer sb, boolean rangeSafe) { + toString(sb, vector, padValue, rangeSafe); + } +} |