/*=============================================================================# # Copyright (c) 2012-2016 Stephan Wahlbrink (WalWare.de) 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: # Stephan Wahlbrink - initial API and implementation #=============================================================================*/ package de.walware.rj.renv; import java.util.Arrays; import java.util.Iterator; /** * @since de.walware.rj.renv 2.0 */ public final class RNumVersion { // First int in numeric version is the operator public static final RNumVersion NONE = new RNumVersion(""); //$NON-NLS-1$ public static RNumVersion create(final String s) { if (s == null || s.isEmpty()) { return NONE; } return new RNumVersion(s); } private static final int OP_UNSUPPORTED = -2; private static final int OP_NONE = -1; private static final int OP_GE = 2; static { NONE.numeric = new int[] { OP_NONE }; } private static final int[] parseVersion(final String s) { final int[] v = new int[(3 + s.length())/2]; int idx = 1; int i = 0; if (s.startsWith(">=")) { //$NON-NLS-1$ v[0] = OP_GE; i = 2; } int start = -3; for (; i < s.length(); i++) { final char c = s.charAt(i); if (start == -3 && c == ' ') { continue; } if (c >= '0' && c <= '9') { if (start < 0) { start = i; } continue; } if (start >= 0) { v[idx++] = Integer.parseInt(s.substring(start, i)); start = -1; if (c == '.' || c == '-') { continue; } } break; } if (start >= 0) { v[idx++] = Integer.parseInt(s.substring(start, s.length())); } if (idx <= 2) { v[0] = OP_UNSUPPORTED; idx = 1; } return (v.length == idx) ? v : Arrays.copyOf(v, idx); } private final String string; /** use {@link #getNumericVersion()} */ private volatile int[] numeric; private RNumVersion(final String s) { this.string = s; } public boolean isGreaterEqualThan(final RNumVersion pkgVersion2) { return isGreaterEqualThan(getNumericVersion(), pkgVersion2.getNumericVersion()); } private boolean isGreaterEqualThan(final int[] v1, final int[] v2) { final int l = Math.max(v1.length, v2.length); for (int i = 1; i < l; i++) { final int diff = ((i < v1.length) ? v1[i] : 0) - ((i < v2.length) ? v2[i] : 0); if (diff > 0) { return true; } else if (diff < 0) { return false; } } return true; } public boolean isGreaterThan(final RNumVersion pkgVersion2) { return isGreaterThan(getNumericVersion(), pkgVersion2.getNumericVersion()); } private boolean isGreaterThan(final int[] v1, final int[] v2) { final int l = Math.max(v1.length, v2.length); for (int i = 1; i < l; i++) { final int diff = ((i < v1.length) ? v1[i] : 0) - ((i < v2.length) ? v2[i] : 0); if (diff > 0) { return true; } else if (diff < 0) { return false; } } return false; } public boolean isSmallerThan(final RNumVersion pkgVersion2) { return isSmallerThan(getNumericVersion(), pkgVersion2.getNumericVersion()); } private boolean isSmallerThan(final int[] v1, final int[] v2) { final int l = Math.max(v1.length, v2.length); for (int i = 1; i < l; i++) { final int diff = ((i < v1.length) ? v1[i] : 0) - ((i < v2.length) ? v2[i] : 0); if (diff < 0) { return true; } else if (diff > 0) { return false; } } return false; } public boolean isSatisfiedBy(final RNumVersion pkgVersion2) { final int[] v1 = getNumericVersion(); switch (v1[0]) { case OP_GE: return isGreaterEqualThan(pkgVersion2.getNumericVersion(), v1); default: return true; } } public boolean isSatisfiedByAny(final Iterator<RNumVersion> pkgVersion2) { final int[] v1 = getNumericVersion(); switch (v1[0]) { case OP_GE: while (pkgVersion2.hasNext()) { if (isGreaterEqualThan(pkgVersion2.next().getNumericVersion(), v1)) { return true; } } return false; default: return pkgVersion2.hasNext(); } } private int[] getNumericVersion() { if (this.numeric == null) { this.numeric = parseVersion(this.string); } return this.numeric; } @Override public int hashCode() { return this.string.hashCode(); } @Override public boolean equals(final Object obj) { return (this == obj || (obj instanceof RNumVersion && this.string.equals(((RNumVersion) obj).string) )); } @Override public String toString() { return this.string; } }