| /******************************************************************************* |
| * Copyright (c) 2009, 2018 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.util.Collections; |
| import java.util.List; |
| import org.eclipse.equinox.p2.metadata.IVersionFormat; |
| import org.eclipse.equinox.p2.metadata.Version; |
| |
| /** |
| * <p>The Generic Omni Version is composed of a vector of Comparable objects and a pad value. The pad |
| * might be <code>null</code>. The vector can contain integers, strings, {@link VersionVector} |
| * instances, or one of the special objects {@link VersionVector#MAX_VALUE MAX_VALUE}, |
| * {@link VersionVector#MAXS_VALUE MAXS_VALUE}, or {@link VersionVector#MIN_VALUE MIN_VALUE}.</p> |
| * |
| * <p>When two versions are compared, they are always considered padded to infinity by their |
| * pad value or by {@link VersionVector#MIN_VALUE MIN_VALUE} in case the pad value is |
| * <code>null</code>. The comparison is type sensitive so that:</p><pre> |
| * MAX_VALUE > Integer > VersionVector > MAXS_VALUE > String > MIN_VALUE<br/> |
| * </pre> |
| * |
| * @Immutable |
| * @noextend This class is not intended to be subclassed by clients. |
| */ |
| public class OmniVersion extends BasicVersion { |
| private static final long serialVersionUID = 1996212688810048879L; |
| |
| private static OmniVersion minimumVersion; |
| |
| private static OmniVersion maximumVersion; |
| |
| private static final Comparable<?>[] emptyVector = new Comparable<?>[0]; |
| |
| private final Comparable<?>[] vector; |
| |
| private final Comparable<?> padValue; |
| |
| /** |
| * The optional format |
| */ |
| private final IVersionFormat format; |
| |
| /** |
| * The optional original string |
| */ |
| private final String original; |
| |
| static BasicVersion fromVector(List<Comparable<?>> vector, IVersionFormat format, String original) { |
| int vtop = vector.size() - 1; |
| Comparable<?> padValue = vector.get(vtop); |
| if (vtop == 0) { |
| if (padValue == null) |
| return (BasicVersion) emptyVersion; |
| if (padValue == VersionVector.MAX_VALUE) |
| return (BasicVersion) MAX_VERSION; |
| } |
| if (vtop == 3 && padValue == null && vector.get(0) == (Integer) 0 && vector.get(1) == (Integer) 0 && vector.get(2) == (Integer) 0) |
| return (BasicVersion) emptyVersion; |
| |
| return new OmniVersion(vector, format, original); |
| } |
| |
| public static Version createMinVersion() { |
| if (minimumVersion == null) |
| minimumVersion = new OmniVersion(Collections.singletonList(null), null, null); |
| return minimumVersion; |
| } |
| |
| public static Version createMaxVersion() { |
| if (maximumVersion == null) |
| maximumVersion = new OmniVersion(Collections.singletonList(VersionVector.MAX_VALUE), null, null); |
| return maximumVersion; |
| } |
| |
| private OmniVersion(List<Comparable<?>> vector, IVersionFormat format, String original) { |
| int vtop = vector.size() - 1; |
| if (vtop > 0) { |
| Comparable<?>[] v = new Comparable<?>[vtop]; |
| for (int idx = 0; idx < vtop; ++idx) |
| v[idx] = vector.get(idx); |
| this.vector = v; |
| } else |
| this.vector = emptyVector; |
| this.padValue = vector.get(vtop); |
| this.format = format; |
| this.original = original; |
| } |
| |
| @Override |
| public boolean equals(Object o) { |
| if (o == this) |
| return true; |
| |
| if (!(o instanceof BasicVersion)) |
| return false; |
| |
| BasicVersion ov = (BasicVersion) o; |
| return VersionVector.equals(vector, padValue, ov.getVector(), ov.getPad()); |
| } |
| |
| @Override |
| public IVersionFormat getFormat() { |
| return format; |
| } |
| |
| @Override |
| public int getMajor() { |
| return getIntElement(0); |
| } |
| |
| @Override |
| public int getMicro() { |
| return getIntElement(2); |
| } |
| |
| @Override |
| public int getMinor() { |
| return getIntElement(1); |
| } |
| |
| @Override |
| public String getOriginal() { |
| return original; |
| } |
| |
| @Override |
| public String getQualifier() { |
| if (vector.length == 3) |
| return VersionVector.MINS_VALUE; |
| |
| if (vector.length != 4) |
| throw new UnsupportedOperationException(); |
| |
| Comparable<?> qualifier = vector[3]; |
| if (qualifier == VersionVector.MAXS_VALUE) |
| return IVersionFormat.DEFAULT_MAX_STRING_TRANSLATION; |
| if (!(qualifier instanceof String)) |
| throw new UnsupportedOperationException(); |
| return (String) qualifier; |
| } |
| |
| @Override |
| public int hashCode() { |
| return VersionVector.hashCode(vector, padValue); |
| } |
| |
| /** |
| * Checks if this version is in compliance with the OSGi version spec. |
| * @return A flag indicating whether the version is OSGi compatible or not. |
| */ |
| @Override |
| public boolean isOSGiCompatible() { |
| if (vector.length < 3 || vector.length > 4) |
| return (this == emptyVersion || this == MAX_VERSION); |
| |
| if (getPad() != null) |
| return false; |
| |
| for (int i = 0; i < 3; ++i) { |
| Object e = vector[i]; |
| if (!(e instanceof Integer && ((Integer) e).intValue() >= 0)) |
| return false; |
| } |
| |
| if (vector.length == 3) |
| return true; // No qualifier. Still compatible |
| return OSGiVersion.isValidOSGiQualifier(vector[3]); |
| } |
| |
| /** |
| * Appends the original for this version onto the <code>sb</code> StringBuffer |
| * if present. |
| * @param sb The buffer that will receive the raw string format |
| * @param rangeSafe Set to <code>true</code> if range delimiters should be escaped |
| */ |
| @Override |
| public void originalToString(StringBuffer sb, boolean rangeSafe) { |
| if (original != null) { |
| if (rangeSafe) { |
| // Escape all range delimiters while appending |
| String s = original; |
| int end = s.length(); |
| for (int idx = 0; idx < end; ++idx) { |
| char c = s.charAt(idx); |
| if (c == '\\' || c == '[' || c == '(' || c == ']' || c == ')' || c == ',' || c <= ' ') |
| sb.append('\\'); |
| sb.append(c); |
| } |
| } else |
| sb.append(original); |
| } |
| } |
| |
| /** |
| * Appends the raw format for this version onto the <code>sb</code> StringBuffer. |
| * @param sb The buffer that will receive the raw string format |
| * @param rangeSafe Set to <code>true</code> if range delimiters should be escaped |
| */ |
| @Override |
| public void rawToString(StringBuffer sb, boolean rangeSafe) { |
| VersionVector.toString(sb, vector, padValue, rangeSafe); |
| } |
| |
| /** |
| * Appends the string representation of this version onto the |
| * <code>sb</code> StringBuffer. |
| * @param sb The buffer that will receive the version string |
| */ |
| @Override |
| public void toString(StringBuffer sb) { |
| if (this == emptyVersion) |
| sb.append("0.0.0"); //$NON-NLS-1$ |
| else { |
| sb.append(RAW_PREFIX); |
| VersionVector.toString(sb, vector, padValue, false); |
| if (format != null || original != null) { |
| sb.append('/'); |
| if (format != null) |
| format.toString(sb); |
| if (original != null) { |
| sb.append(':'); |
| originalToString(sb, false); |
| } |
| } |
| } |
| } |
| |
| private int getIntElement(int i) { |
| if (!(vector.length > i && vector[i] instanceof Integer)) |
| throw new UnsupportedOperationException(); |
| return ((Integer) vector[i]).intValue(); |
| } |
| |
| // Preserve singletons during deserialization |
| private Object readResolve() { |
| Version v = this; |
| if (equals(MAX_VERSION)) |
| v = MAX_VERSION; |
| else if (equals(emptyVersion)) |
| v = emptyVersion; |
| return v; |
| } |
| |
| @Override |
| public Comparable<?> getPad() { |
| return padValue; |
| } |
| |
| @Override |
| public Comparable<?> getSegment(int index) { |
| return vector[index]; |
| } |
| |
| @Override |
| public int getSegmentCount() { |
| return vector.length; |
| } |
| |
| @Override |
| Comparable<?>[] getVector() { |
| return vector; |
| } |
| |
| @Override |
| public int compareTo(Version v) { |
| BasicVersion ov = (BasicVersion) v; |
| return VersionVector.compare(vector, padValue, ov.getVector(), ov.getPad()); |
| } |
| } |