• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

devonfw / IDEasy / 21013659204

14 Jan 2026 11:29PM UTC coverage: 70.365% (+0.5%) from 69.904%
21013659204

Pull #1675

github

web-flow
Merge 7a3aa598b into fcadaae82
Pull Request #1675: #1298: support ide-extra-tools.json #1658: prevent Jackson reflection

4015 of 6292 branches covered (63.81%)

Branch coverage included in aggregate %.

10440 of 14251 relevant lines covered (73.26%)

3.17 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.12
cli/src/main/java/com/devonfw/tools/ide/version/VersionRange.java
1
package com.devonfw.tools.ide.version;
2

3
import java.util.Objects;
4

5
/**
6
 * Container for a range of versions. The lower and upper bounds can be exclusive or inclusive. If a bound is null, it means that this direction is unbounded.
7
 * The boolean defining whether this bound is inclusive or exclusive is ignored in this case.
8
 */
9
public final class VersionRange implements Comparable<VersionRange>, GenericVersionRange {
10

11
  /** The unbounded {@link VersionRange} instance. */
12
  public static final VersionRange UNBOUNDED = new VersionRange(null, null, BoundaryType.OPEN);
8✔
13

14
  private static final String VERSION_SEPARATOR = ",";
15

16
  final VersionIdentifier min;
17

18
  final VersionIdentifier max;
19

20
  final BoundaryType boundaryType;
21

22
  /**
23
   * The constructor.
24
   *
25
   * @param min the {@link #getMin() minimum}.
26
   * @param max the {@link #getMax() maximum}.
27
   * @param boundaryType the {@link BoundaryType} defining whether the boundaries of the range are inclusive or exclusive.
28
   */
29
  private VersionRange(VersionIdentifier min, VersionIdentifier max, BoundaryType boundaryType) {
30

31
    super();
2✔
32
    Objects.requireNonNull(boundaryType);
3✔
33
    this.min = min;
3✔
34
    this.max = max;
3✔
35
    this.boundaryType = boundaryType;
3✔
36
    if ((min != null) && (max != null) && min.isGreater(max)) {
8✔
37
      throw new IllegalArgumentException(toString());
6✔
38
    } else if ((min == null) && !boundaryType.isLeftExclusive()) {
5✔
39
      throw new IllegalArgumentException(toString());
6✔
40
    } else if ((max == null) && !boundaryType.isRightExclusive()) {
5✔
41
      throw new IllegalArgumentException(toString());
6✔
42
    }
43

44
  }
1✔
45

46
  @Override
47
  public VersionIdentifier getMin() {
48

49
    return this.min;
3✔
50
  }
51

52
  @Override
53
  public VersionIdentifier getMax() {
54

55
    return this.max;
3✔
56
  }
57

58
  @Override
59
  public BoundaryType getBoundaryType() {
60

61
    return this.boundaryType;
3✔
62
  }
63

64
  @Override
65
  public boolean isPattern() {
66

67
    return true;
2✔
68
  }
69

70
  @Override
71
  public boolean contains(VersionIdentifier version) {
72

73
    VersionSegment start = version.getStart();
3✔
74
    if ((start.getNumber() == -1) && start.isPattern()) {
7!
75
      return true; // * and *! are always contained
2✔
76
    }
77
    if (this.min != null) {
3!
78
      VersionComparisonResult compareMin = version.compareVersion(this.min);
5✔
79
      if (compareMin.isLess()) {
3✔
80
        return false;
2✔
81
      } else if (compareMin.isEqual() && this.boundaryType.isLeftExclusive() && !version.isPattern()) {
10✔
82
        return false;
2✔
83
      }
84
    }
85
    if (this.max != null) {
3✔
86
      VersionComparisonResult compareMax = version.compareVersion(this.max);
5✔
87
      if (compareMax.isGreater()) {
3✔
88
        return false;
2✔
89
      } else if (compareMax.isEqual() && this.boundaryType.isRightExclusive()) {
7✔
90
        return false;
2✔
91
      }
92
    }
93
    return true;
2✔
94
  }
95

96
  @Override
97
  public int compareTo(VersionRange o) {
98

99
    if (this.min == null) {
3!
100
      if (o == null) {
×
101
        return 1; // should never happen
×
102
      } else if (o.min == null) {
×
103
        return 0;
×
104
      }
105
      return -1;
×
106
    }
107
    int compareMins = this.min.compareTo(o.min);
6✔
108
    if (compareMins == 0) {
2✔
109
      return this.boundaryType.isLeftExclusive() == o.boundaryType.isLeftExclusive() ? 0
10✔
110
          : this.boundaryType.isLeftExclusive() ? 1 : -1;
7✔
111
    } else {
112
      return compareMins;
2✔
113
    }
114
  }
115

116
  /**
117
   * @param other the {@link VersionRange} to unite with.
118
   * @return the union of this with the given {@link VersionRange} or {@code null} if not {@link VersionRangeRelation#CONNECTED connected} or
119
   *     {@link VersionRangeRelation#OVERLAPPING overlapping}.
120
   */
121
  public VersionRange union(VersionRange other) {
122

123
    return union(other, VersionRangeRelation.CONNECTED);
5✔
124
  }
125

126
  /**
127
   * @param other the {@link VersionRange} to unite with.
128
   * @param minRelation the minimum {@link VersionRangeRelation} required to allow building the union instead of returning {@code null}. So if you want to
129
   *     build a strict union, you can pass {@link VersionRangeRelation#OVERLAPPING} so you only get a union that contains exactly what is contained in at least
130
   *     one of the two {@link VersionRange}s. However, you can pass {@link VersionRangeRelation#CONNECTED_LOOSELY} in order to get "[2.0,5.0]" as the union of
131
   *     "[2.0,2.2]" and "[2.3,5.0]".
132
   * @return the union of this with the given {@link VersionRange} or {@code null} if the actual {@link VersionRangeRelation} of the {@link VersionRange}s is
133
   *     lower than the given {@code minRelation}.
134
   */
135
  public VersionRange union(VersionRange other, VersionRangeRelation minRelation) {
136

137
    if (other == null) {
2!
138
      return this;
×
139
    }
140
    return new VersionRangeCombination(this, other).union(minRelation);
8✔
141
  }
142

143
  /**
144
   * @param other the {@link VersionRange} to intersect with.
145
   * @return the intersection of this with the given {@link VersionRange} or {@code null} for empty intersection.
146
   */
147
  public VersionRange intersect(VersionRange other) {
148

149
    if (other == null) {
2!
150
      return this;
×
151
    }
152
    return new VersionRangeCombination(this, other).intersection();
7✔
153
  }
154

155
  @Override
156
  public boolean equals(Object obj) {
157

158
    if (this == obj) {
3✔
159
      return true;
2✔
160
    } else if ((obj == null) || (getClass() != obj.getClass())) {
7!
161
      return false;
2✔
162
    }
163
    VersionRange o = (VersionRange) obj;
3✔
164
    if (this.boundaryType != o.boundaryType) {
5✔
165
      return false;
2✔
166
    } else if (!Objects.equals(this.min, o.min)) {
6✔
167
      return false;
2✔
168
    } else if (!Objects.equals(this.max, o.max)) {
6✔
169
      return false;
2✔
170
    }
171
    return true;
2✔
172
  }
173

174
  @Override
175
  public String toString() {
176

177
    StringBuilder sb = new StringBuilder();
4✔
178
    sb.append(this.boundaryType.getPrefix());
6✔
179
    if (this.min != null) {
3✔
180
      sb.append(this.min);
5✔
181
    }
182
    if (this.max == null || !this.max.equals(this.min)) { // [1.0] instead of [1.0,1.0]
9✔
183
      sb.append(VERSION_SEPARATOR);
4✔
184
      if (this.max != null) {
3✔
185
        sb.append(this.max);
5✔
186
      }
187
    }
188
    sb.append(this.boundaryType.getSuffix());
6✔
189
    return sb.toString();
3✔
190
  }
191

192
  /**
193
   * @param value the {@link #toString() string representation} of a {@link VersionRange} to parse.
194
   * @return the parsed {@link VersionRange}.
195
   */
196
  public static VersionRange of(String value) {
197

198
    return of(value, false);
4✔
199
  }
200

201
  /**
202
   * @param value the {@link #toString() string representation} of a {@link VersionRange} to parse.
203
   * @param tolerance {@code true} to enable tolerant parsing so we can read garbage (e.g. form JSON) without failing.
204
   * @return the parsed {@link VersionRange}.
205
   */
206
  public static VersionRange of(String value, boolean tolerance) {
207

208
    Boolean isleftExclusive = null;
2✔
209
    Boolean isRightExclusive = null;
2✔
210
    if (value.startsWith(BoundaryType.START_EXCLUDING_PREFIX)) {
4✔
211
      isleftExclusive = Boolean.TRUE;
2✔
212
      value = value.substring(BoundaryType.START_EXCLUDING_PREFIX.length());
6✔
213
    } else if (value.startsWith(BoundaryType.START_INCLUDING_PREFIX)) {
4✔
214
      isleftExclusive = Boolean.FALSE;
2✔
215
      value = value.substring(BoundaryType.START_INCLUDING_PREFIX.length());
5✔
216
    }
217
    if (value.endsWith(BoundaryType.END_EXCLUDING_SUFFIX)) {
4✔
218
      isRightExclusive = Boolean.TRUE;
2✔
219
      value = value.substring(0, value.length() - BoundaryType.END_EXCLUDING_SUFFIX.length());
10✔
220
    } else if (value.endsWith(BoundaryType.END_INCLUDING_SUFFIX)) {
4✔
221
      isRightExclusive = Boolean.FALSE;
2✔
222
      value = value.substring(0, value.length() - BoundaryType.END_INCLUDING_SUFFIX.length());
9✔
223
    }
224
    VersionIdentifier min = null;
2✔
225
    VersionIdentifier max = null;
2✔
226
    int index = value.indexOf(VERSION_SEPARATOR);
4✔
227
    if (index < 0) {
2✔
228
      min = VersionIdentifier.of(value);
3✔
229
      max = min;
3✔
230
    } else {
231
      String minString = value.substring(0, index).trim();
6✔
232
      if (!minString.isBlank()) {
3✔
233
        min = VersionIdentifier.of(minString);
3✔
234
        if (min == VersionIdentifier.LATEST) {
3✔
235
          min = null;
2✔
236
        }
237
      }
238
      String maxString = value.substring(index + 1).trim();
7✔
239
      if (!maxString.isBlank()) {
3✔
240
        max = VersionIdentifier.of(maxString);
3✔
241
        if (max == VersionIdentifier.LATEST) {
3✔
242
          max = null;
2✔
243
        }
244
      }
245
    }
246
    if ((isleftExclusive == null) || (tolerance && (min == null))) {
6✔
247
      isleftExclusive = min == null;
7✔
248
    }
249
    if ((isRightExclusive == null) || (tolerance && (max == null))) {
6✔
250
      isRightExclusive = max == null;
7✔
251
    }
252

253
    if ((min == null) && (max == null) && isleftExclusive && isRightExclusive) {
10✔
254
      return UNBOUNDED;
2✔
255
    }
256
    return new VersionRange(min, max, BoundaryType.of(isleftExclusive, isRightExclusive));
11✔
257
  }
258

259
  /**
260
   * @param min the {@link #getMin() minimum}.
261
   * @param max the {@link #getMax() maximum}.
262
   * @param type the {@link BoundaryType} defining whether the boundaries of the range are inclusive or exclusive.
263
   * @return the {@link VersionRange} created from the given values.
264
   */
265
  public static VersionRange of(VersionIdentifier min, VersionIdentifier max, BoundaryType type) {
266

267
    if (type == null) {
2!
268
      type = BoundaryType.of(min == null, max == null);
×
269
    }
270
    if ((min == null) && (max == null)) {
4✔
271
      assert (type == BoundaryType.OPEN);
4!
272
      return UNBOUNDED;
2✔
273
    }
274
    return new VersionRange(min, max, type);
7✔
275
  }
276

277
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc