1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.aether.util.version;
20
21 import java.util.Objects;
22
23 import org.eclipse.aether.version.InvalidVersionSpecificationException;
24 import org.eclipse.aether.version.Version;
25 import org.eclipse.aether.version.VersionRange;
26 import org.eclipse.aether.version.VersionScheme;
27
28 import static java.util.Objects.requireNonNull;
29
30
31
32
33
34
35 final class GenericVersionRange implements VersionRange {
36 private final VersionScheme versionScheme;
37
38 private final Bound lowerBound;
39
40 private final Bound upperBound;
41
42
43
44
45
46
47
48 GenericVersionRange(VersionScheme versionScheme, String range) throws InvalidVersionSpecificationException {
49 this.versionScheme = requireNonNull(versionScheme, "versionScheme cannot be null");
50 String process = requireNonNull(range, "version range cannot be null");
51
52 boolean lowerBoundInclusive, upperBoundInclusive;
53 Version lowerBound, upperBound;
54
55 if (range.startsWith("[")) {
56 lowerBoundInclusive = true;
57 } else if (range.startsWith("(")) {
58 lowerBoundInclusive = false;
59 } else {
60 throw new InvalidVersionSpecificationException(
61 range, "Invalid version range " + range + ", a range must start with either [ or (");
62 }
63
64 if (range.endsWith("]")) {
65 upperBoundInclusive = true;
66 } else if (range.endsWith(")")) {
67 upperBoundInclusive = false;
68 } else {
69 throw new InvalidVersionSpecificationException(
70 range, "Invalid version range " + range + ", a range must end with either [ or (");
71 }
72
73 process = process.substring(1, process.length() - 1);
74
75 int index = process.indexOf(",");
76
77 if (index < 0) {
78 if (!lowerBoundInclusive || !upperBoundInclusive) {
79 throw new InvalidVersionSpecificationException(
80 range, "Invalid version range " + range + ", single version must be surrounded by []");
81 }
82
83 String version = process.trim();
84 if (version.endsWith(".*")) {
85 String prefix = version.substring(0, version.length() - 1);
86 lowerBound = parse(prefix + "min");
87 upperBound = parse(prefix + "max");
88 } else {
89 lowerBound = parse(version);
90 upperBound = lowerBound;
91 }
92 } else {
93 String parsedLowerBound = process.substring(0, index).trim();
94 String parsedUpperBound = process.substring(index + 1).trim();
95
96
97 if (parsedUpperBound.contains(",")) {
98 throw new InvalidVersionSpecificationException(
99 range, "Invalid version range " + range + ", bounds may not contain additional ','");
100 }
101
102 lowerBound = !parsedLowerBound.isEmpty() ? parse(parsedLowerBound) : null;
103 upperBound = !parsedUpperBound.isEmpty() ? parse(parsedUpperBound) : null;
104
105 if (upperBound != null && lowerBound != null) {
106 if (upperBound.compareTo(lowerBound) < 0) {
107 throw new InvalidVersionSpecificationException(
108 range,
109 "Invalid version range " + range + ", lower bound must not be greater than upper bound");
110 }
111 }
112 }
113
114 this.lowerBound = (lowerBound != null) ? new Bound(lowerBound, lowerBoundInclusive) : null;
115 this.upperBound = (upperBound != null) ? new Bound(upperBound, upperBoundInclusive) : null;
116 }
117
118 private Version parse(String version) throws InvalidVersionSpecificationException {
119 return versionScheme.parseVersion(version);
120 }
121
122 @Override
123 public Bound getLowerBound() {
124 return lowerBound;
125 }
126
127 @Override
128 public Bound getUpperBound() {
129 return upperBound;
130 }
131
132 @Override
133 public boolean containsVersion(Version version) {
134 if (lowerBound != null) {
135 int comparison = lowerBound.getVersion().compareTo(version);
136
137 if (comparison == 0 && !lowerBound.isInclusive()) {
138 return false;
139 }
140 if (comparison > 0) {
141 return false;
142 }
143 }
144
145 if (upperBound != null) {
146 int comparison = upperBound.getVersion().compareTo(version);
147
148 if (comparison == 0 && !upperBound.isInclusive()) {
149 return false;
150 }
151 if (comparison < 0) {
152 return false;
153 }
154 }
155
156 return true;
157 }
158
159 @Override
160 public boolean equals(Object obj) {
161 if (obj == this) {
162 return true;
163 } else if (obj == null || !getClass().equals(obj.getClass())) {
164 return false;
165 }
166
167 VersionRange that = (VersionRange) obj;
168
169 return Objects.equals(upperBound, that.getUpperBound()) && Objects.equals(lowerBound, that.getLowerBound());
170 }
171
172 @Override
173 public int hashCode() {
174 int hash = 17;
175 hash = hash * 31 + hash(upperBound);
176 hash = hash * 31 + hash(lowerBound);
177 return hash;
178 }
179
180 private static int hash(Object obj) {
181 return obj != null ? obj.hashCode() : 0;
182 }
183
184 @Override
185 public String toString() {
186 StringBuilder buffer = new StringBuilder(64);
187 if (lowerBound != null) {
188 buffer.append(lowerBound.isInclusive() ? '[' : '(');
189 buffer.append(lowerBound.getVersion());
190 } else {
191 buffer.append('(');
192 }
193 buffer.append(',');
194 if (upperBound != null) {
195 buffer.append(upperBound.getVersion());
196 buffer.append(upperBound.isInclusive() ? ']' : ')');
197 } else {
198 buffer.append(')');
199 }
200 return buffer.toString();
201 }
202 }