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

devonfw / IDEasy / 22317391561

23 Feb 2026 05:30PM UTC coverage: 70.257% (-0.2%) from 70.474%
22317391561

Pull #1714

github

web-flow
Merge 5be048514 into 379acdc9d
Pull Request #1714: #404: #1713: advanced logging

4066 of 6384 branches covered (63.69%)

Branch coverage included in aggregate %.

10598 of 14488 relevant lines covered (73.15%)

3.08 hits per line

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

86.58
cli/src/main/java/com/devonfw/tools/ide/cli/CliArgument.java
1
package com.devonfw.tools.ide.cli;
2

3
import java.util.ArrayList;
4
import java.util.List;
5
import java.util.Objects;
6

7
/**
8
 * Represents an argument for a command-line-interface (CLI) and a chain to all its {@link #getNext(boolean) successors}.
9
 *
10
 * @see #getNext(boolean)
11
 * @since 1.0.0
12
 */
13
public class CliArgument {
14

15
  /**
16
   * The {@link #get() argument} to indicate the end of the options. If this string is given as argument, any further arguments are treated as values. This
17
   * allows to provide values (e.g. a filename) starting with a hyphen ('-').
18
   */
19
  public static final String END_OPTIONS = "--";
20

21
  /** A {@link CliArgument} to represent the end of the CLI arguments. */
22
  public static final CliArgument END = new CliArgument();
5✔
23

24
  static final String NAME_START = "«start»";
25

26
  private final String arg;
27

28
  private String key;
29

30
  private String value;
31

32
  final CliArgument next;
33

34
  private final boolean completion;
35

36
  private CliArgument() {
37

38
    super();
2✔
39
    this.arg = "«end»";
3✔
40
    this.next = null;
3✔
41
    this.completion = false;
3✔
42
  }
1✔
43

44
  /**
45
   * The constructor.
46
   *
47
   * @param arg the {@link #get() argument}.
48
   * @param next the {@link #getNext() next}.
49
   */
50
  protected CliArgument(String arg, CliArgument next) {
51

52
    this(arg, next, false);
5✔
53
  }
1✔
54

55
  /**
56
   * The constructor.
57
   *
58
   * @param arg the {@link #get() argument}.
59
   * @param next the {@link #getNext() next}.
60
   * @param completion the {@link #isCompletion() completion flag}.
61
   */
62
  protected CliArgument(String arg, CliArgument next, boolean completion) {
63

64
    super();
2✔
65
    Objects.requireNonNull(arg);
3✔
66
    Objects.requireNonNull(next);
3✔
67
    this.arg = arg;
3✔
68
    this.next = next;
3✔
69
    this.completion = completion;
3✔
70
  }
1✔
71

72
  /**
73
   * @return {@code true} if this is the argument to complete (should be the last one), {@code false} otherwise.
74
   */
75
  public boolean isCompletion() {
76

77
    return this.completion;
3✔
78
  }
79

80
  /**
81
   * @return the argument text (e.g. "-h" for a short option, "--help" for a long option, or "foo" for a value).
82
   */
83
  public String get() {
84

85
    return this.arg;
3✔
86
  }
87

88
  /**
89
   * @return {@code true} if this is an option (e.g. "-h" or "--help"), {@code false} otherwise.
90
   */
91
  public boolean isOption() {
92

93
    return this.arg.startsWith("-");
5✔
94
  }
95

96
  /**
97
   * @return {@code true} if this is a long option (e.g. "--help"), {@code false} otherwise.
98
   */
99
  public boolean isLongOption() {
100

101
    return this.arg.startsWith("--");
5✔
102
  }
103

104
  /**
105
   * @return {@code true} if this is a short option (e.g. "-b"), {@code false} otherwise.
106
   */
107
  public boolean isShortOption() {
108

109
    return (this.arg.length() >= 2) && (this.arg.charAt(0) == '-') && (this.arg.charAt(1) != '-');
21✔
110
  }
111

112
  /**
113
   * @return {@code true} if this is a combined short option (e.g. "-bd"), {@code false} otherwise.
114
   */
115
  public boolean isCombinedShortOption() {
116

117
    return (this.arg.length() > 2) && (this.arg.charAt(0) == '-') && (this.arg.charAt(1) != '-');
21✔
118
  }
119

120
  /**
121
   * @return {@code true} if {@link #END_OPTIONS}, {@code false} otherwise.
122
   */
123
  public boolean isEndOptions() {
124

125
    return this.arg.equals(END_OPTIONS);
5✔
126
  }
127

128
  /**
129
   * @return {@code true} if this is the {@link #END} of the arguments, {@code false} otherwise.
130
   */
131
  public boolean isEnd() {
132

133
    return (this.next == null);
7✔
134
  }
135

136
  /**
137
   * @return {@code true} if this is the start of the arguments, {@code false} otherwise.
138
   */
139
  public boolean isStart() {
140

141
    return (this.arg == NAME_START); // not using equals on purpose
8✔
142
  }
143

144
  /**
145
   * @param successors the number of {@link #getNext() successors} expected.
146
   * @return {@code true} if at least the given number of {@link #getNext() successors} are available, {@code false} otherwise.
147
   */
148
  public boolean hasMoreSuccessorsThan(int successors) {
149

150
    if (successors <= 0) {
×
151
      return true;
×
152
    }
153
    CliArgument current = this;
×
154
    while (current != END) {
×
155
      successors--;
×
156
      if (successors == 0) {
×
157
        return true;
×
158
      }
159
      current = current.next;
×
160
    }
161
    return false;
×
162
  }
163

164
  /**
165
   * @return the next {@link CliArgument} or {@code null} if this is the {@link #isEnd() end}.
166
   * @see #getNext(boolean)
167
   */
168
  public CliArgument getNext() {
169

170
    return this.next;
3✔
171
  }
172

173
  /**
174
   * @param splitShortOpts - if {@code true} then combined short options will be split (so instead of "-fbd" you will get "-f", "-b", "-d").
175
   * @return the next {@link CliArgument} or {@code null} if this is the {@link #isEnd() end}.
176
   */
177
  public CliArgument getNext(boolean splitShortOpts) {
178

179
    if (splitShortOpts && (this.next != null) && !this.next.completion) {
9!
180
      String option = this.next.arg;
4✔
181
      int len = option.length();
3✔
182
      if ((len > 2) && (option.charAt(0) == '-') && (option.charAt(1) != '-')) {
13✔
183
        CliArgument current = this.next.next;
4✔
184
        for (int i = len - 1; i > 0; i--) {
8✔
185
          current = new CliArgument("-" + option.charAt(i), current);
9✔
186
        }
187
        return current;
2✔
188
      }
189
    }
190
    return this.next;
3✔
191
  }
192

193
  /**
194
   * @return the {@code «key»} part if the {@link #get() argument} has the has the form {@code «key»=«value»}. Otherwise the {@link #get() argument} itself.
195
   */
196
  public String getKey() {
197

198
    initKeyValue();
2✔
199
    return this.key;
3✔
200
  }
201

202
  /**
203
   * @return the {@code «value»} part if the {@link #get() argument} has the has the form {@code «key»=«value»}. Otherwise {@code null}.
204
   */
205
  public String getValue() {
206

207
    initKeyValue();
2✔
208
    return this.value;
3✔
209
  }
210

211
  private void initKeyValue() {
212

213
    if (this.key != null) {
3✔
214
      return;
1✔
215
    }
216
    int equalsIndex = this.arg.indexOf('=');
5✔
217
    if (equalsIndex < 0) {
2✔
218
      this.key = this.arg;
5✔
219
    } else {
220
      this.key = this.arg.substring(0, equalsIndex);
7✔
221
      this.value = this.arg.substring(equalsIndex + 1);
8✔
222
    }
223
  }
1✔
224

225
  /**
226
   * @return a {@link String} representing all arguments from this {@link CliArgument} recursively along is {@link #getNext(boolean) next} arguments to the
227
   *     {@link #isEnd() end}.
228
   */
229
  public String getArgs() {
230

231
    if (isEnd()) {
3!
232
      return "";
×
233
    }
234
    StringBuilder sb = new StringBuilder();
4✔
235
    CliArgument current = this;
2✔
236
    if (current.isStart()) {
3✔
237
      current = current.next;
3✔
238
    }
239
    String prefix = "\"";
2✔
240
    while (!current.isEnd()) {
3✔
241
      sb.append(prefix);
4✔
242
      sb.append(current.arg);
5✔
243
      sb.append("\"");
4✔
244
      current = current.next;
3✔
245
      prefix = " \"";
3✔
246
    }
247
    return sb.toString();
3✔
248
  }
249

250
  private CliArgument createStart() {
251

252
    assert (!isStart());
4!
253
    return new CliArgument(NAME_START, this);
6✔
254
  }
255

256
  /**
257
   * @return a {@link String} array with all arguments starting from this one.
258
   */
259
  public String[] asArray() {
260

261
    List<String> args = new ArrayList<>();
4✔
262
    CliArgument current = this;
2✔
263
    while (!current.isEnd()) {
3✔
264
      args.add(current.arg);
5✔
265
      current = current.next;
4✔
266
    }
267
    return args.toArray(size -> new String[size]);
8✔
268
  }
269

270
  @Override
271
  public String toString() {
272

273
    return this.arg;
×
274
  }
275

276
  /**
277
   * @param args the command-line arguments (e.g. from {@code main} method).
278
   * @return the first {@link CliArgument} of the parsed arguments or {@code null} if for empty arguments.
279
   */
280
  public static CliArgument of(String... args) {
281

282
    return of(false, args);
4✔
283
  }
284

285
  /**
286
   * @param args the command-line arguments (e.g. from {@code main} method).
287
   * @return the first {@link CliArgument} of the parsed arguments or {@code null} if for empty arguments.
288
   */
289
  public static CliArgument ofCompletion(String... args) {
290

291
    return of(true, args);
4✔
292
  }
293

294
  private static CliArgument of(boolean completion, String... args) {
295

296
    CliArgument current = CliArgument.END;
2✔
297
    int last = args.length - 1;
5✔
298
    for (int argsIndex = last; argsIndex >= 0; argsIndex--) {
6✔
299
      String arg = args[argsIndex];
4✔
300
      boolean completionArg = false;
2✔
301
      if (argsIndex == last) {
3✔
302
        completionArg = completion;
2✔
303
      }
304
      current = new CliArgument(arg, current, completionArg);
7✔
305
    }
306
    return current.createStart();
3✔
307
  }
308

309
  /**
310
   * @param currentArgs the current arguments to extend.
311
   * @param firstArgs the additional arguments to prepend before {@code currentArgs}.
312
   * @return a {@link String} array with the values from {@code firstArgs} followed by the values from {@code currentArgs}.
313
   */
314
  public static String[] prepend(String[] currentArgs, String... firstArgs) {
315

316
    return join(firstArgs, currentArgs);
4✔
317
  }
318

319
  /**
320
   * @param currentArgs the current arguments to extend.
321
   * @param lastArgs the additional arguments to append after {@code currentArgs}.
322
   * @return a {@link String} array with the values from {@code currentArgs} followed by the values from {@code lastArgs}.
323
   */
324
  public static String[] append(String[] currentArgs, String... lastArgs) {
325

326
    return join(currentArgs, lastArgs);
4✔
327
  }
328

329
  private static String[] join(String[] args, String... extraArgs) {
330

331
    String[] result = new String[args.length + extraArgs.length];
7✔
332
    System.arraycopy(args, 0, result, 0, args.length);
7✔
333
    System.arraycopy(extraArgs, 0, result, args.length, extraArgs.length);
8✔
334
    return result;
2✔
335
  }
336
}
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