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

devonfw / IDEasy / 28790126080

06 Jul 2026 12:03PM UTC coverage: 71.844% (+0.01%) from 71.83%
28790126080

push

github

web-flow
#1178: Clean up and Solution for new qualifier snapshot (#2057)

Co-authored-by: Jörg Hohwiller <hohwille@users.noreply.github.com>

4785 of 7350 branches covered (65.1%)

Branch coverage included in aggregate %.

12257 of 16371 relevant lines covered (74.87%)

3.17 hits per line

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

81.76
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
  /** The snapshot qualifier string. */
23
  public static final String SNAPSHOT = "snapshot";
24

25
  private final String letters;
26

27
  private final String lettersLowerCase;
28

29
  private final VersionPhase phase;
30

31
  private final boolean prePhase;
32

33
  private final boolean snapshot;
34

35
  /**
36
   * The constructor.
37
   *
38
   * @param letters the {@link #getLetters() letters}.
39
   */
40
  private VersionLetters(String letters) {
41

42
    super();
2✔
43
    this.letters = letters;
3✔
44
    this.lettersLowerCase = letters.toLowerCase(Locale.ROOT);
5✔
45
    String phaseLetters = this.lettersLowerCase.replace('_', '-');
6✔
46
    if (phaseLetters.startsWith("pre")) {
4✔
47
      this.prePhase = true;
3✔
48
      int preLength = 3;
2✔
49
      if (phaseLetters.startsWith("pre-")) {
4✔
50
        preLength = 4;
2✔
51
      }
52
      phaseLetters = phaseLetters.substring(preLength);
4✔
53
    } else {
1✔
54
      this.prePhase = false;
3✔
55
    }
56
    String newPhaseLetters = removeSnapshotPart(phaseLetters);
3✔
57
    if (newPhaseLetters.equals(phaseLetters)) {
4✔
58
      this.snapshot = false;
4✔
59
    } else { // snapshot was found and removed
60
      phaseLetters = newPhaseLetters;
2✔
61
      this.snapshot = true;
3✔
62
    }
63

64
    this.phase = VersionPhase.of(phaseLetters);
4✔
65
  }
1✔
66

67
  private VersionLetters(VersionPhase phase) {
68

69
    super();
2✔
70
    this.letters = "";
3✔
71
    this.lettersLowerCase = "";
3✔
72
    this.prePhase = false;
3✔
73
    this.phase = phase;
3✔
74
    this.snapshot = false;
3✔
75
  }
1✔
76

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

89
    return this.letters;
3✔
90
  }
91

92
  /**
93
   * @return the {@link VersionPhase} for the {@link #getLetters() letters}. Will be {@link VersionPhase#UNDEFINED} if unknown and hence never {@code null}.
94
   * @see #isPrePhase()
95
   * @see #getLetters()
96
   */
97
  public VersionPhase getPhase() {
98

99
    return this.phase;
3✔
100
  }
101

102
  /**
103
   * @return {@code true} if this {@link VersionSegment} has a SNAPSHOT suffix, otherwise {@code false}.
104
   */
105
  public boolean isSnapshot() {
106
    return snapshot;
3✔
107
  }
108

109
  /**
110
   * @return {@code true} if the {@link #getLetters() letters} and a potential {@link #getPhase() phase} are prefixed with "pre" (e.g. in "pre-alpha"),
111
   *     {@code false} otherwise.
112
   */
113
  public boolean isPrePhase() {
114

115
    return this.prePhase;
3✔
116
  }
117

118
  @Override
119
  public boolean isDevelopmentPhase() {
120

121
    return this.phase.isDevelopmentPhase() || isPrePhase();
11✔
122
  }
123

124
  @Override
125
  public boolean isUnstable() {
126

127
    return !isStable();
7✔
128
  }
129

130
  @Override
131
  public boolean isStable() {
132

133
    return !this.prePhase && this.phase.isStable() && !this.snapshot;
14✔
134
  }
135

136
  /**
137
   * @return {@code true} if empty, {@code false} otherwise.
138
   */
139
  public boolean isEmpty() {
140

141
    return this.letters.isEmpty();
4✔
142
  }
143

144
  @Override
145
  public boolean isValid() {
146

147
    return true;
×
148
  }
149

150
  @Override
151
  public VersionComparisonResult compareVersion(VersionLetters other) {
152

153
    if (!this.lettersLowerCase.equals(other.lettersLowerCase)) {
6✔
154
      if ((this.phase == VersionPhase.UNDEFINED) || (other.phase == VersionPhase.UNDEFINED)) {
8!
155
        if (this.lettersLowerCase.compareTo(other.lettersLowerCase) < 0) {
6!
156
          return VersionComparisonResult.LESS_UNSAFE;
×
157
        } else {
158
          return VersionComparisonResult.GREATER_UNSAFE;
2✔
159
        }
160
      }
161

162
      int thisRank = getPhaseRank();
3✔
163
      int otherRank = other.getPhaseRank();
3✔
164
      if (thisRank < otherRank) {
3✔
165
        return VersionComparisonResult.LESS;
2✔
166
      } else if (thisRank > otherRank) {
3✔
167
        return VersionComparisonResult.GREATER;
2✔
168
      }
169

170
      if (this.prePhase != other.prePhase) {
5!
171
        if (this.prePhase) {
×
172
          return VersionComparisonResult.LESS;
×
173
        } else {
174
          return VersionComparisonResult.GREATER;
×
175
        }
176
      }
177
    }
178
    return VersionComparisonResult.EQUAL;
2✔
179
  }
180

181
  /**
182
   * @param other the other {@link VersionLetters} to match against.
183
   * @param pattern - {@code true} if the owning {@link VersionSegment} {@link VersionSegment#isPattern() is a pattern}, {@code false} otherwise.
184
   * @return the {@link VersionMatchResult}.
185
   * @see VersionSegment#matches(VersionSegment)
186
   */
187
  public VersionMatchResult matches(VersionLetters other, boolean pattern) {
188

189
    if (other == null) {
2!
190
      return VersionMatchResult.MISMATCH;
×
191
    }
192
    if (isEmpty() && other.isEmpty()) {
6✔
193
      return VersionMatchResult.EQUAL;
2✔
194
    }
195
    if (pattern) {
2✔
196
      if (this.phase != VersionPhase.NONE) {
4!
197
        if (this.phase != other.phase) {
×
198
          return VersionMatchResult.MISMATCH;
×
199
        }
200
      }
201
      if (!other.lettersLowerCase.startsWith(this.lettersLowerCase)) {
6!
202
        return VersionMatchResult.MISMATCH;
×
203
      }
204
      return VersionMatchResult.MATCH;
2✔
205
    } else {
206
      if ((this.phase != other.phase) || !this.lettersLowerCase.equals(other.lettersLowerCase)) {
11!
207
        return VersionMatchResult.MISMATCH;
×
208
      }
209
      return VersionMatchResult.EQUAL;
2✔
210
    }
211
  }
212

213
  @Override
214
  public int hashCode() {
215

216
    return this.letters.hashCode();
×
217
  }
218

219
  @Override
220
  public boolean equals(Object obj) {
221

222
    if (obj == this) {
3!
223
      return true;
×
224
    } else if (!(obj instanceof VersionLetters)) {
3!
225
      return false;
×
226
    }
227
    VersionLetters other = (VersionLetters) obj;
3✔
228
    return Objects.equals(this.letters, other.letters);
6✔
229
  }
230

231
  @Override
232
  public String toString() {
233

234
    return this.letters;
3✔
235
  }
236

237
  /**
238
   * @param letters the letters as {@link String}.
239
   * @return the parsed {@link VersionLetters}.
240
   */
241
  public static VersionLetters of(String letters) {
242

243
    if ((letters == null) || letters.isEmpty()) {
5!
244
      return EMPTY;
2✔
245
    }
246
    return new VersionLetters(letters);
5✔
247
  }
248

249
  private static String removeSnapshotPart(String phaseLetters) {
250
    int i = phaseLetters.indexOf(SNAPSHOT);
4✔
251
    if (i < 0) {
2✔
252
      return phaseLetters;
2✔
253
    }
254
    int start = i;
2✔
255
    int end = i + SNAPSHOT.length();
5✔
256
    if ((i > 0) && (phaseLetters.charAt(i - 1) == '-')) {
9!
257
      start--;
1✔
258
    }
259
    return phaseLetters.substring(0, start) + phaseLetters.substring(end);
9✔
260
  }
261

262

263
  private int getPhaseRank() {
264

265
    int rank = this.phase.ordinal() * 2;
6✔
266

267
    if (!this.snapshot) {
3✔
268
      rank++;
1✔
269
    }
270
    return rank;
2✔
271
  }
272
}
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