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

devonfw / IDEasy / 28107441918

24 Jun 2026 02:51PM UTC coverage: 71.421% (+0.04%) from 71.381%
28107441918

Pull #2057

github

web-flow
Merge c00e7f2c6 into 459b15501
Pull Request #2057: #1178: Clean up and Solution for new qualifier snapshot

4732 of 7320 branches covered (64.64%)

Branch coverage included in aggregate %.

12142 of 16306 relevant lines covered (74.46%)

3.15 hits per line

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

77.09
cli/src/main/java/com/devonfw/tools/ide/version/VersionLetters.java
1
package com.devonfw.tools.ide.version;
2

3
import java.util.Locale;
4
import java.util.Objects;
5

6
/**
7
 * Value type for the letters of a {@link VersionSegment}.
8
 *
9
 * @see VersionSegment#getLettersString()
10
 */
11
public final class VersionLetters implements AbstractVersionPhase, VersionObject<VersionLetters> {
12

13
  /** The empty {@link VersionLetters} instance. */
14
  public static final VersionLetters EMPTY = new VersionLetters("");
5✔
15

16
  /** The unstable {@link VersionLetters} instance - see {@link VersionSegment#PATTERN_MATCH_ANY_VERSION}. */
17
  public static final VersionLetters UNSTABLE = new VersionLetters(VersionPhase.UNSTABLE);
5✔
18

19
  /** The undefined {@link VersionLetters} instance. */
20
  public static final VersionLetters UNDEFINED = new VersionLetters("undefined");
6✔
21

22
  private final String letters;
23

24
  private final String lettersLowerCase;
25

26
  private final VersionPhase phase;
27

28
  private final boolean prePhase;
29

30
  private final boolean snapshot;
31

32
  /**
33
   * The constructor.
34
   *
35
   * @param letters the {@link #getLetters() letters}.
36
   */
37
  private VersionLetters(String letters) {
38

39
    super();
2✔
40
    this.letters = letters;
3✔
41
    this.lettersLowerCase = letters.toLowerCase(Locale.ROOT);
5✔
42
    String phaseLetters = this.lettersLowerCase.replace('_', '-');
6✔
43
    if (phaseLetters.startsWith("pre")) {
4✔
44
      this.prePhase = true;
3✔
45
      int preLength = 3;
2✔
46
      if (phaseLetters.startsWith("pre-")) {
4✔
47
        preLength = 4;
2✔
48
      }
49
      phaseLetters = phaseLetters.substring(preLength);
4✔
50
    } else {
1✔
51
      this.prePhase = false;
3✔
52
    }
53
    this.snapshot = containsSnapshotPart(phaseLetters);
4✔
54
    if (this.snapshot) {
3✔
55
      phaseLetters = removeSnapshotPart(phaseLetters);
3✔
56
    }
57

58
    this.phase = VersionPhase.of(phaseLetters);
4✔
59
  }
1✔
60

61
  private VersionLetters(VersionPhase phase) {
62

63
    super();
2✔
64
    this.letters = "";
3✔
65
    this.lettersLowerCase = "";
3✔
66
    this.prePhase = false;
3✔
67
    this.phase = phase;
3✔
68
    this.snapshot = false;
3✔
69
  }
1✔
70

71
  /**
72
   * @return the letters or the empty {@link String} ("") for none. In canonical {@link VersionIdentifier}s letters indicate the development phase (e.g. "pre",
73
   *     "rc", "alpha", "beta", "milestone", "test", "dev", "SNAPSHOT", etc.). However, letters are technically any
74
   *     {@link Character#isLetter(char) letter characters} and may also be something like a code-name (e.g. "Cupcake", "Donut", "Eclair", "Froyo",
75
   *     "Gingerbread", "Honeycomb", "Ice Cream Sandwich", "Jelly Bean" in case of Android internals). Please note that in such case it is impossible to
76
   *     properly decide which version is greater than another versions. To avoid mistakes, the comparison supports a strict mode that will let the comparison
77
   *     fail in such case. However, by default (e.g. for {@link Comparable#compareTo(Object)}) the default {@link String#compareTo(String) string comparison}
78
   *     (lexicographical) is used to ensure a natural order.
79
   * @see #getPhase()
80
   */
81
  public String getLetters() {
82

83
    return this.letters;
3✔
84
  }
85

86
  /**
87
   * @return the {@link VersionPhase} for the {@link #getLetters() letters}. Will be {@link VersionPhase#UNDEFINED} if unknown and hence never {@code null}.
88
   * @see #isPrePhase()
89
   * @see #getLetters()
90
   */
91
  public VersionPhase getPhase() {
92

93
    return this.phase;
3✔
94
  }
95

96
  /**
97
   * @return {@code true} if this {@link VersionSegment} has a SNAPSHOT suffix, otherwise {@code false}.
98
   */
99
  public boolean isSnapshot() {
100
    return snapshot;
3✔
101
  }
102

103
  /**
104
   * @return {@code true} if the {@link #getLetters() letters} and a potential {@link #getPhase() phase} are prefixed with "pre" (e.g. in "pre-alpha"),
105
   *     {@code false} otherwise.
106
   */
107
  public boolean isPrePhase() {
108

109
    return this.prePhase;
3✔
110
  }
111

112
  @Override
113
  public boolean isDevelopmentPhase() {
114

115
    return this.phase.isDevelopmentPhase() || isPrePhase();
11✔
116
  }
117

118
  @Override
119
  public boolean isUnstable() {
120

121
    return this.prePhase || this.phase.isUnstable() || this.isSnapshot();
14✔
122
  }
123

124
  @Override
125
  public boolean isStable() {
126

127
    return !this.prePhase && this.phase.isStable() && !this.snapshot;
14!
128
  }
129

130
  /**
131
   * @return {@code true} if empty, {@code false} otherwise.
132
   */
133
  public boolean isEmpty() {
134

135
    return this.letters.isEmpty();
4✔
136
  }
137

138
  @Override
139
  public boolean isValid() {
140

141
    return true;
×
142
  }
143

144
  @Override
145
  public VersionComparisonResult compareVersion(VersionLetters other) {
146

147
    if (!this.lettersLowerCase.equals(other.lettersLowerCase)) {
6✔
148
      if ((this.phase == VersionPhase.UNDEFINED) || (other.phase == VersionPhase.UNDEFINED)) {
8!
149
        if (this.lettersLowerCase.compareTo(other.lettersLowerCase) < 0) {
6!
150
          return VersionComparisonResult.LESS_UNSAFE;
×
151
        } else {
152
          return VersionComparisonResult.GREATER_UNSAFE;
2✔
153
        }
154
      }
155

156
      int thisRank = getPhaseRank();
3✔
157
      int otherRank = other.getPhaseRank();
3✔
158
      if (thisRank < otherRank) {
3✔
159
        return VersionComparisonResult.LESS;
2✔
160
      } else if (thisRank > otherRank) {
3✔
161
        return VersionComparisonResult.GREATER;
2✔
162
      }
163

164
      if (this.prePhase != other.prePhase) {
5!
165
        if (this.prePhase) {
×
166
          return VersionComparisonResult.LESS;
×
167
        } else {
168
          return VersionComparisonResult.GREATER;
×
169
        }
170
      }
171
    }
172
    return VersionComparisonResult.EQUAL;
2✔
173
  }
174

175
  /**
176
   * @param other the other {@link VersionLetters} to match against.
177
   * @param pattern - {@code true} if the owning {@link VersionSegment} {@link VersionSegment#isPattern() is a pattern}, {@code false} otherwise.
178
   * @return the {@link VersionMatchResult}.
179
   * @see VersionSegment#matches(VersionSegment)
180
   */
181
  public VersionMatchResult matches(VersionLetters other, boolean pattern) {
182

183
    if (other == null) {
2!
184
      return VersionMatchResult.MISMATCH;
×
185
    }
186
    if (isEmpty() && other.isEmpty()) {
6!
187
      return VersionMatchResult.EQUAL;
2✔
188
    }
189
    if (pattern) {
2!
190
      if (this.phase != VersionPhase.NONE) {
4!
191
        if (this.phase != other.phase) {
×
192
          return VersionMatchResult.MISMATCH;
×
193
        }
194
      }
195
      if (!other.lettersLowerCase.startsWith(this.lettersLowerCase)) {
6!
196
        return VersionMatchResult.MISMATCH;
×
197
      }
198
      return VersionMatchResult.MATCH;
2✔
199
    } else {
200
      if ((this.phase != other.phase) || !this.lettersLowerCase.equals(other.lettersLowerCase)) {
×
201
        return VersionMatchResult.MISMATCH;
×
202
      }
203
      return VersionMatchResult.EQUAL;
×
204
    }
205
  }
206

207
  @Override
208
  public int hashCode() {
209

210
    return this.letters.hashCode();
×
211
  }
212

213
  @Override
214
  public boolean equals(Object obj) {
215

216
    if (obj == this) {
3!
217
      return true;
×
218
    } else if (!(obj instanceof VersionLetters)) {
3!
219
      return false;
×
220
    }
221
    VersionLetters other = (VersionLetters) obj;
3✔
222
    return Objects.equals(this.letters, other.letters);
6✔
223
  }
224

225
  @Override
226
  public String toString() {
227

228
    return this.letters;
3✔
229
  }
230

231
  /**
232
   * @param letters the letters as {@link String}.
233
   * @return the parsed {@link VersionLetters}.
234
   */
235
  public static VersionLetters of(String letters) {
236

237
    if ((letters == null) || letters.isEmpty()) {
5!
238
      return EMPTY;
2✔
239
    }
240
    return new VersionLetters(letters);
5✔
241
  }
242

243
  private static boolean containsSnapshotPart(String phaseLetters) {
244
    String[] parts = phaseLetters.split("-");
4✔
245
    for (String part : parts) {
16✔
246
      if ("snapshot".equals(part)) {
4✔
247
        return true;
2✔
248
      }
249
    }
250
    return false;
2✔
251
  }
252

253
  private static String removeSnapshotPart(String phaseLetters) {
254
    String[] parts = phaseLetters.split("-");
4✔
255
    StringBuilder result = new StringBuilder();
4✔
256

257
    for (String part : parts) {
16✔
258

259
      if ("snapshot".equals(part)) {
4✔
260
        continue;
1✔
261
      }
262

263
      if (!part.isEmpty()) {
3!
264
        if (!result.isEmpty()) {
3!
265
          result.append("-");
×
266
        }
267
        result.append(part);
4✔
268
      }
269
    }
270
    return result.toString();
3✔
271
  }
272

273
  private int getPhaseRank() {
274
    int unstableRank = VersionPhase.UNSTABLE.ordinal();
3✔
275

276
    if (this.snapshot) {
3✔
277
      return unstableRank + 1;
4✔
278
    }
279
    int rank = this.phase.ordinal();
4✔
280

281
    if (rank > unstableRank) {
3!
282
      return rank + 1;
4✔
283
    }
284
    return rank;
×
285
  }
286
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc