| /******************************************************************************* |
| * Copyright (c) 2009, 2017 Cloudsmith Inc. and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * 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 |
| } |
| |
| @Override |
| public int compareTo(Object o) { |
| return o == this ? 0 : (o == MAX_VALUE || o instanceof Integer || o instanceof EnumDefinition.EnumSegment || o instanceof VersionVector ? -1 : 1); |
| } |
| |
| // For singleton deserialization |
| private Object readResolve() { |
| return MAXS_VALUE; |
| } |
| |
| @Override |
| public String toString() { |
| return "m"; //$NON-NLS-1$ |
| } |
| } |
| |
| private static final class MaxValue implements MinMaxComparable { |
| private static final long serialVersionUID = -5889641741635253589L; |
| |
| MaxValue() { |
| // Empty constructor |
| } |
| |
| @Override |
| public int compareTo(Object o) { |
| return o == this ? 0 : 1; |
| } |
| |
| // For singleton deserialization |
| private Object readResolve() { |
| return MAX_VALUE; |
| } |
| |
| @Override |
| public String toString() { |
| return "M"; //$NON-NLS-1$ |
| } |
| } |
| |
| private static class MinValue implements MinMaxComparable { |
| private static final long serialVersionUID = -1066323980049812226L; |
| |
| MinValue() { |
| // Empty constructor |
| } |
| |
| @Override |
| public int compareTo(Object o) { |
| return o == this ? 0 : -1; |
| } |
| |
| private Object readResolve() { |
| return MIN_VALUE; |
| } |
| |
| @Override |
| 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; |
| |
| if (a instanceof EnumDefinition.EnumSegment) |
| return (b instanceof EnumDefinition.EnumSegment) ? ((EnumDefinition.EnumSegment) a).compareTo((EnumDefinition.EnumSegment) b) : 1; |
| if (b instanceof EnumDefinition.EnumSegment) |
| 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; |
| } |
| |
| @Override |
| public int compareTo(VersionVector ov) { |
| if (ov == this) |
| return 0; |
| |
| return compare(vector, padValue, ov.vector, ov.padValue); |
| } |
| |
| @Override |
| 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; |
| } |
| |
| @Override |
| public int hashCode() { |
| return hashCode(vector, padValue); |
| } |
| |
| @Override |
| 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); |
| } |
| |
| private Object readResolve() { |
| VersionVector vv = this; |
| // Preserve the emptyString singleton |
| int idx = vector.length; |
| while (--idx >= 0) |
| if (MINS_VALUE.equals(vector[idx])) |
| vector[idx] = MINS_VALUE; |
| if (MINS_VALUE.equals(padValue)) |
| vv = new VersionVector(vector, MINS_VALUE); |
| return vv; |
| } |
| } |