| /******************************************************************************* |
| * 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.util.List; |
| import org.eclipse.equinox.p2.metadata.IVersionFormat; |
| import org.eclipse.equinox.p2.metadata.Version; |
| import org.eclipse.osgi.util.NLS; |
| |
| /** |
| * @Immutable |
| * @noextend This class is not intended to be subclassed by clients. |
| */ |
| public class OSGiVersion extends BasicVersion { |
| |
| private static final long serialVersionUID = -4530178927569560877L; |
| |
| private static final boolean[] allowedOSGiChars; |
| |
| private final int major; |
| |
| private final int minor; |
| |
| private final int micro; |
| |
| private final Comparable<?> qualifier; |
| |
| static { |
| allowedOSGiChars = new boolean[128]; |
| for (int c = '0'; c <= '9'; ++c) |
| allowedOSGiChars[c] = true; |
| for (int c = 'A'; c <= 'Z'; ++c) |
| allowedOSGiChars[c] = true; |
| for (int c = 'a'; c <= 'z'; ++c) |
| allowedOSGiChars[c] = true; |
| allowedOSGiChars['_'] = true; |
| allowedOSGiChars['-'] = true; |
| } |
| |
| public static boolean isValidOSGiQualifier(Comparable<?> e) { |
| if (e == VersionVector.MAXS_VALUE) |
| return true; |
| |
| if (!(e instanceof String)) |
| return false; |
| |
| String s = (String) e; |
| int idx = s.length(); |
| boolean[] allowed = allowedOSGiChars; |
| while (--idx >= 0) { |
| int c = s.charAt(idx); |
| if (c < '-' || c > 'z' || !allowed[c]) |
| return false; |
| } |
| return true; |
| } |
| |
| static BasicVersion fromVector(List<Comparable<?>> vector) { |
| int vtop = vector.size() - 1; |
| Comparable<?> pad = vector.get(vtop); |
| if (vtop != 4) { |
| if (vtop == 0) { |
| if (pad == null) |
| return (BasicVersion) emptyVersion; |
| if (pad == VersionVector.MAX_VALUE) |
| return (BasicVersion) MAX_VERSION; |
| } |
| throw new IllegalArgumentException(); |
| } |
| int major = ((Integer) vector.get(0)).intValue(); |
| int minor = ((Integer) vector.get(1)).intValue(); |
| int micro = ((Integer) vector.get(2)).intValue(); |
| Comparable<?> qualifier = vector.get(3); |
| return (major == 0 && minor == 0 && micro == 0 && qualifier == VersionVector.MINS_VALUE) ? (BasicVersion) emptyVersion : new OSGiVersion(major, minor, micro, qualifier); |
| } |
| |
| public OSGiVersion(int major, int minor, int micro, Comparable<? extends Object> qualifier) { |
| this.major = major; |
| this.minor = minor; |
| this.micro = micro; |
| if (!isValidOSGiQualifier(qualifier)) |
| throw new IllegalArgumentException(NLS.bind(Messages._0_is_not_a_valid_qualifier_in_osgi_1, "qualifier", this)); //$NON-NLS-1$ |
| //intern the qualifier string to avoid duplication |
| if (qualifier instanceof String) |
| qualifier = ((String) qualifier).intern(); |
| this.qualifier = qualifier; |
| } |
| |
| @Override |
| public int compareTo(Version v) { |
| int result; |
| if (!(v instanceof OSGiVersion)) { |
| BasicVersion ov = (BasicVersion) v; |
| result = VersionVector.compare(getVector(), null, ov.getVector(), ov.getPad()); |
| } else { |
| OSGiVersion ov = (OSGiVersion) v; |
| result = major - ov.major; |
| if (result == 0) { |
| result = minor - ov.minor; |
| if (result == 0) { |
| result = micro - ov.micro; |
| if (result == 0) |
| result = VersionVector.compareSegments(qualifier, ov.qualifier); |
| } |
| } |
| } |
| return result; |
| } |
| |
| @Override |
| public boolean equals(Object object) { |
| if (object == this) |
| return true; |
| |
| if (!(object instanceof OSGiVersion)) { |
| if (object instanceof BasicVersion) { |
| BasicVersion ov = (BasicVersion) object; |
| return VersionVector.equals(getVector(), null, ov.getVector(), ov.getPad()); |
| } |
| return false; |
| } |
| |
| OSGiVersion other = (OSGiVersion) object; |
| return micro == other.micro && minor == other.minor && major == other.major && qualifier.equals(other.qualifier); |
| } |
| |
| @Override |
| public IVersionFormat getFormat() { |
| return VersionFormat.OSGI_FORMAT; |
| } |
| |
| @Override |
| public int getMajor() { |
| return major; |
| } |
| |
| @Override |
| public int getMicro() { |
| return micro; |
| } |
| |
| @Override |
| public int getMinor() { |
| return minor; |
| } |
| |
| @Override |
| public String getOriginal() { |
| return toString(); |
| } |
| |
| @Override |
| public String getQualifier() { |
| return qualifier == VersionVector.MAXS_VALUE ? IVersionFormat.DEFAULT_MAX_STRING_TRANSLATION : (String) qualifier; |
| } |
| |
| @Override |
| public int hashCode() { |
| return (major << 24) + (minor << 16) + (micro << 8) + qualifier.hashCode(); |
| } |
| |
| @Override |
| public boolean isOSGiCompatible() { |
| return true; |
| } |
| |
| @Override |
| public void originalToString(StringBuffer sb, boolean rangeSafe) { |
| toString(sb); |
| } |
| |
| @Override |
| public void rawToString(StringBuffer sb, boolean rangeSafe) { |
| sb.append(major); |
| sb.append('.'); |
| sb.append(minor); |
| sb.append('.'); |
| sb.append(micro); |
| sb.append('.'); |
| sb.append('\''); |
| sb.append(qualifier); |
| sb.append('\''); |
| } |
| |
| @Override |
| public void toString(StringBuffer sb) { |
| sb.append(major); |
| sb.append('.'); |
| sb.append(minor); |
| sb.append('.'); |
| sb.append(micro); |
| if (qualifier != VersionVector.MINS_VALUE) { |
| sb.append('.'); |
| sb.append(getQualifier()); |
| } |
| } |
| |
| @Override |
| public Comparable<?>[] getVector() { |
| return new Comparable[] {VersionParser.valueOf(major), VersionParser.valueOf(minor), VersionParser.valueOf(micro), qualifier}; |
| } |
| |
| @Override |
| public Comparable<?> getPad() { |
| return null; |
| } |
| |
| @Override |
| public Comparable<?> getSegment(int index) { |
| switch (index) { |
| case 0 : |
| return VersionParser.valueOf(major); |
| case 1 : |
| return VersionParser.valueOf(minor); |
| case 2 : |
| return VersionParser.valueOf(micro); |
| case 3 : |
| return qualifier; |
| } |
| throw new ArrayIndexOutOfBoundsException(index); // Not in the imaginary vector array |
| } |
| |
| @Override |
| public int getSegmentCount() { |
| return 4; |
| } |
| |
| private Object readResolve() { |
| OSGiVersion v = this; |
| // Preserve the empty string singleton. |
| if (qualifier.equals(VersionVector.MINS_VALUE)) |
| v = new OSGiVersion(major, minor, micro, VersionVector.MINS_VALUE); |
| return v; |
| } |
| } |