| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * https://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| package org.apache.ivy.osgi.util; |
| |
| import java.text.ParseException; |
| |
| import static org.apache.ivy.util.StringUtils.isNullOrEmpty; |
| |
| /** |
| * Provides version range support. |
| */ |
| public class VersionRange { |
| |
| private boolean startExclusive; |
| |
| private Version startVersion; |
| |
| private boolean endExclusive; |
| |
| private Version endVersion; |
| |
| public VersionRange(String versionStr) throws ParseException { |
| if (isNullOrEmpty(versionStr)) { |
| startExclusive = false; |
| startVersion = new Version(0, 0, 0, null); |
| endExclusive = true; |
| endVersion = null; |
| } else { |
| new VersionRangeParser(versionStr).parse(); |
| } |
| } |
| |
| class VersionRangeParser { |
| |
| /** |
| * value to parse |
| */ |
| private final String version; |
| |
| /** |
| * the length of the source |
| */ |
| private int length; |
| |
| /** |
| * position in the source |
| */ |
| private int pos = 0; |
| |
| /** |
| * last read character |
| */ |
| private char c; |
| |
| /** |
| * Default constructor |
| * |
| * @param version |
| * the version to parse |
| */ |
| VersionRangeParser(String version) { |
| this.version = version; |
| this.length = version.length(); |
| } |
| |
| /** |
| * Do the parsing |
| * |
| * @throws ParseException if something goes wrong |
| */ |
| void parse() throws ParseException { |
| boolean range = parseStart(); |
| startVersion = parseVersion(); |
| if (startVersion == null) { |
| throw new ParseException("Expecting a number", pos); |
| } |
| if (parseVersionSeparator()) { |
| endVersion = parseVersion(); |
| parseEnd(); |
| } else if (range) { |
| throw new ParseException("Expecting ,", pos); |
| } else { |
| // simple number |
| endVersion = null; |
| startExclusive = false; |
| endExclusive = false; |
| } |
| } |
| |
| private char readNext() { |
| if (pos == length) { |
| c = '\0'; |
| } else { |
| c = version.charAt(pos++); |
| } |
| return c; |
| } |
| |
| private void unread() { |
| if (pos > 0) { |
| pos--; |
| } |
| } |
| |
| private boolean parseStart() { |
| skipWhiteSpace(); |
| switch (readNext()) { |
| case '[': |
| startExclusive = false; |
| return true; |
| case '(': |
| startExclusive = true; |
| return true; |
| default: |
| unread(); |
| return false; |
| } |
| } |
| |
| private void skipWhiteSpace() { |
| do { |
| switch (readNext()) { |
| case ' ': |
| continue; |
| default: |
| unread(); |
| return; |
| } |
| } while (pos < length); |
| } |
| |
| private Version parseVersion() { |
| Integer major = parseNumber(); |
| if (major == null) { |
| return null; |
| } |
| Integer minor = 0; |
| Integer patch = 0; |
| String qualifier = null; |
| if (parseNumberSeparator()) { |
| minor = parseNumber(); |
| if (minor == null) { |
| minor = 0; |
| } else if (parseNumberSeparator()) { |
| patch = parseNumber(); |
| if (patch == null) { |
| patch = 0; |
| } else if (parseNumberSeparator()) { |
| qualifier = parseQualifier(); |
| } |
| } |
| } |
| return new Version(major, minor, patch, qualifier); |
| } |
| |
| private Integer parseNumber() { |
| skipWhiteSpace(); |
| Integer n = null; |
| do { |
| switch (readNext()) { |
| case '\0': |
| return n; |
| case '0': |
| case '1': |
| case '2': |
| case '3': |
| case '4': |
| case '5': |
| case '6': |
| case '7': |
| case '8': |
| case '9': |
| n = (n == null ? 0 : n * 10) + c - '0'; |
| break; |
| default: |
| unread(); |
| return n; |
| } |
| } while (pos < length); |
| return n; |
| } |
| |
| private boolean parseNumberSeparator() { |
| switch (readNext()) { |
| case '.': |
| return true; |
| default: |
| unread(); |
| return false; |
| } |
| } |
| |
| private boolean parseVersionSeparator() { |
| skipWhiteSpace(); |
| switch (readNext()) { |
| case ',': |
| return true; |
| default: |
| unread(); |
| return false; |
| } |
| } |
| |
| private String parseQualifier() { |
| StringBuilder q = new StringBuilder(); |
| do { |
| readNext(); |
| if (c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0' && c <= '9' |
| || c == '-' || c == '_') { |
| q.append(c); |
| } else { |
| unread(); |
| break; |
| } |
| } while (pos < length); |
| if (q.length() == 0) { |
| return null; |
| } |
| return q.toString(); |
| } |
| |
| private void parseEnd() throws ParseException { |
| skipWhiteSpace(); |
| switch (readNext()) { |
| case ']': |
| endExclusive = false; |
| break; |
| case ')': |
| endExclusive = true; |
| break; |
| default: |
| unread(); |
| throw new ParseException("Expecting ] or )", pos); |
| } |
| } |
| } |
| |
| public VersionRange(boolean startExclusive, Version startVersion, boolean endExclusive, |
| Version endVersion) { |
| this.startExclusive = startExclusive; |
| this.startVersion = startVersion; |
| this.endExclusive = endExclusive; |
| this.endVersion = endVersion; |
| } |
| |
| public VersionRange(Version startVersion) { |
| this.startExclusive = false; |
| this.startVersion = startVersion; |
| this.endExclusive = true; |
| this.endVersion = null; |
| } |
| |
| public String toString() { |
| return (startExclusive ? "(" : "[") + startVersion.toString() + "," |
| + (endVersion == null ? "" : endVersion.toString()) + (endExclusive ? ")" : "]"); |
| } |
| |
| public String toIvyRevision() { |
| StringBuilder buffer = new StringBuilder(); |
| buffer.append(startExclusive ? "(" : "[").append(startVersion).append(","); |
| if (endVersion != null) { |
| if (!endExclusive || startVersion.equals(endVersion)) { |
| buffer.append(endVersion.withNudgedPatch()); |
| } else { |
| buffer.append(endVersion); |
| } |
| } |
| return buffer.append(")").toString(); |
| } |
| |
| public boolean isEndExclusive() { |
| return this.endExclusive; |
| } |
| |
| public Version getEndVersion() { |
| return this.endVersion; |
| } |
| |
| public boolean isStartExclusive() { |
| return this.startExclusive; |
| } |
| |
| public Version getStartVersion() { |
| return this.startVersion; |
| } |
| |
| public boolean isClosedRange() { |
| return startVersion.equals(endVersion); |
| } |
| |
| public boolean contains(String versionStr) { |
| return contains(new Version(versionStr)); |
| } |
| |
| public boolean contains(Version version) { |
| return (startExclusive ? version.compareUnqualified(startVersion) > 0 : version.compareUnqualified(startVersion) >= 0) |
| && (endVersion == null || (endExclusive ? version.compareUnqualified(endVersion) < 0 : version.compareUnqualified(endVersion) <= 0)); |
| } |
| |
| public int hashCode() { |
| final int prime = 31; |
| int result = 1; |
| result = prime * result + (endExclusive ? 1231 : 1237); |
| result = prime * result + ((endVersion == null) ? 0 : endVersion.hashCode()); |
| result = prime * result + (startExclusive ? 1231 : 1237); |
| result = prime * result + ((startVersion == null) ? 0 : startVersion.hashCode()); |
| return result; |
| } |
| |
| public boolean equals(Object obj) { |
| if (this == obj) { |
| return true; |
| } |
| if (obj == null || !(obj instanceof VersionRange)) { |
| return false; |
| } |
| VersionRange other = (VersionRange) obj; |
| if (endExclusive != other.endExclusive) { |
| return false; |
| } |
| if (endVersion == null) { |
| if (other.endVersion != null) { |
| return false; |
| } |
| } else if (!endVersion.equals(other.endVersion)) { |
| return false; |
| } |
| return startExclusive == other.startExclusive |
| && (startVersion == null ? other.startVersion == null : startVersion.equals(other.startVersion)); |
| } |
| |
| } |