Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 7967e8b4fb3aa2dfe56735b0b1f7077115dad51b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
/*******************************************************************************
 * Copyright (c) 2009, 2017 Cloudsmith 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.p2.metadata;

import java.io.Serializable;
import java.lang.ref.SoftReference;
import java.util.WeakHashMap;
import org.eclipse.equinox.internal.p2.metadata.*;

/**
 * A class that represents a Version in the Omni Version format. A Version can be thought of as an
 * array of comparable elements and an optional pad value. The pad value is used when comparing
 * two versions with a different number of segments.
 *
 * The Omni Version can convert almost any version into a raw format that it uses for comparisons.
 * This enables a unified order of all such versions and solves problems that arise when the
 * version semantics are different. A good example is the OSGi version versus the version used in Maven.
 * The lack of qualifier in the OSGi version implies that the qualifier is an empty string. So a version
 * without a qualifier is the smallest of all other versions with the same major,minor,micro number.
 * With Maven semantics, it's the opposite. If the qualifier is removed, the resulting version is
 * considered higher then all other versions with the same major, minor, and micro number. The
 * Omni version solves this by using different raw representations of the OSGi and Maven versions.
 *
 * The Omni version addresses a lot of other issues as well, such as reordering of the elements
 * or treating some parts of a version as irrelevant when comparing.
 * 
 * The class is signature compatible with {@link org.osgi.framework.Version} but attempts
 * to use it as such might render a {@link UnsupportedOperationException} in case the
 * raw vector holds incompatible values. The method {@link #isOSGiCompatible()} can be used
 * to test.
 * @noextend This class is not intended to be subclassed by clients.
 * @since 2.0
 */
public abstract class Version implements Comparable<Version>, Serializable {
	public static final String RAW_PREFIX = "raw:"; //$NON-NLS-1$
	private static WeakHashMap<String, SoftReference<Version>> POOL = new WeakHashMap<>();

	/**
	 * The version that is semantically greater then all other versions.
	 */
	public static final Version MAX_VERSION = OmniVersion.createMaxVersion();

	/**
	 * The version that is semantically less then all other versions.
	 */
	public static final Version emptyVersion = OmniVersion.createMinVersion();

	private static final long serialVersionUID = 6218979149720923857L;

	/**
	 * Compile a version format string into a compiled format..
	 *
	 * @param format The format to compile.
	 * @return The compiled format
	 * @throws VersionFormatException If the format could not be compiled
	 */
	public static IVersionFormat compile(String format) throws VersionFormatException {
		return VersionFormat.compile(format, 0, format.length());
	}

	/**
	 * Parses a version identifier from the specified string.
	 * <p>
	 * Note that this method performs a non thread-safe object pooling. Instances are
	 * stored in a weak cache, i.e. for multiple calls with the same input it is likely
	 * but not guaranteed that the same instance is retrieved. Same holds for concurrent
	 * access on this method. Clients must not assume to get the same instance for 
	 * subsequent calls.
	 * 
	 * @param version String representation of the version identifier. Leading
	 *        and trailing whitespace will be ignored.
	 * @return A <code>Version</code> object representing the version identifier
	 *         or <code>null</code> if <code>version</code> is <code>null</code> or
	 *         an empty string.
	 * @throws IllegalArgumentException If <code>version</code> is improperly
	 *         formatted.
	 */
	public static Version create(String version) {
		Version v = null;
		if (version != null && version.length() > 0) {
			SoftReference<Version> vRef = POOL.get(version);
			v = vRef != null ? vRef.get() : null;
			if (v == null) {
				v = VersionParser.parse(version, 0, version.length());
				synchronized (POOL) {
					POOL.put(version, new SoftReference<>(v));
				}
			}
		}
		return v;
	}

	/**
	 * Creates an OSGi version identifier from the specified numerical components.
	 * 
	 * <p>
	 * The qualifier is set to the empty string.
	 * 
	 * @param major Major component of the version identifier.
	 * @param minor Minor component of the version identifier.
	 * @param micro Micro component of the version identifier.
	 * @throws IllegalArgumentException If the numerical components are
	 *         negative.
	 */
	public static Version createOSGi(int major, int minor, int micro) {
		return createOSGi(major, minor, micro, null);
	}

	/**
	 * Creates an OSGi version identifier from the specified components.
	 * 
	 * @param major Major component of the version identifier.
	 * @param minor Minor component of the version identifier.
	 * @param micro Micro component of the version identifier.
	 * @param qualifier Qualifier component of the version identifier. If
	 *        <code>null</code> is specified, then the qualifier will be set to
	 *        the empty string.
	 * @throws IllegalArgumentException If the numerical components are negative
	 *         or the qualifier string is invalid.
	 */
	public static Version createOSGi(int major, int minor, int micro, String qualifier) {
		Comparable<?> logicQualifier;
		if (qualifier == null || qualifier.length() == 0) {
			if (major == 0 && minor == 0 && micro == 0)
				return emptyVersion;
			logicQualifier = VersionVector.MINS_VALUE; // So that we can do identity compare
		} else if (qualifier.equals(IVersionFormat.DEFAULT_MAX_STRING_TRANSLATION))
			logicQualifier = VersionVector.MAXS_VALUE;
		else
			logicQualifier = qualifier;
		return new OSGiVersion(major, minor, micro, logicQualifier);
	}

	/**
	 * Parses a version identifier from the specified string. This method is for backward
	 * compatibility with OSGi and will return the OSGi &quot;0.0.0&quot; version when
	 * the provided string is empty or <code>null</code>.
	 * 
	 * @param version String representation of the version identifier. Leading
	 *        and trailing whitespace will be ignored.
	 * @return A <code>Version</code> object representing the version
	 *         identifier. If <code>version</code> is <code>null</code> or
	 *         the empty string then the OSGi <code>emptyVersion</code> will be
	 *         returned.
	 * @throws IllegalArgumentException If <code>version</code> is improperly
	 *         formatted.
	 * @see #create(String)
	 */
	public static Version parseVersion(String version) {
		if (version == null || version.length() == 0)
			return Version.emptyVersion;
		Version v = create(version);
		return v == null ? Version.emptyVersion : v;
	}

	/**
	 * Returns the optional format.
	 */
	public abstract IVersionFormat getFormat();

	/**
	 * Returns the <code>original</code> part of the string for this version
	 * or <code>null</code> if no such part was provided when the version was
	 * created. An OSGi type version will always return the OSGi string representation.
	 *
	 * @return The <code>original</code> part of the version string or
	 * <code>null</code> if that part was missing.
	 */
	public abstract String getOriginal();

	/**
	 * Returns the pad value used when comparing this versions to
	 * versions that has a larger number of segments
	 * @return The pad value or <code>null</code> if not set.
	 */
	public abstract Comparable<?> getPad();

	/**
	 * An element from the raw vector representation of this version.
	 * @param index The zero based index of the desired element
	 * @return An element from the raw vector
	 */
	public abstract Comparable<?> getSegment(int index);

	/**
	 * Returns the number of elements in the raw vector representation of this version.
	 * @return The number of elements in the raw vector.
	 */
	public abstract int getSegmentCount();

	/**
	 * Checks if this version is in compliance with the OSGi version spec.
	 * @return A flag indicating whether the version is OSGi compatible or not.
	 */
	public abstract boolean isOSGiCompatible();

	@Override
	public String toString() {
		StringBuffer buf = new StringBuffer(20);
		toString(buf);
		return buf.toString();
	}

	/**
	 * Appends the string representation of this version onto the
	 * <code>sb</code> StringBuffer.
	 * @param sb The buffer that will receive the version string
	 */
	public abstract void toString(StringBuffer sb);
}

Back to the top