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

devonfw / IDEasy / 19462251046

18 Nov 2025 10:11AM UTC coverage: 68.846% (-0.09%) from 68.94%
19462251046

push

github

web-flow
#1584: improve commandlet error handling and bash array syntax warning (#1589)

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

3528 of 5623 branches covered (62.74%)

Branch coverage included in aggregate %.

9214 of 12885 relevant lines covered (71.51%)

3.14 hits per line

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

92.0
cli/src/main/java/com/devonfw/tools/ide/environment/VariableLine.java
1
package com.devonfw.tools.ide.environment;
2

3
import java.util.Arrays;
4
import java.util.List;
5

6
import com.devonfw.tools.ide.context.IdeContext;
7
import com.devonfw.tools.ide.log.IdeLogger;
8

9
/**
10
 * Container that represents a line from a properties (ide.properties) file. We do not use {@link java.util.Properties} as we need support for exported
11
 * variables, lists/arrays, and saving changes without loosing comments, etc.
12
 */
13
public abstract class VariableLine {
3✔
14

15
  /**
16
   * @return {@code true} if the variable is exported (e.g. "export MAVEN_OPTS=-Xmx20248m"), {@code false} otherwise.
17
   */
18
  public boolean isExport() {
19

20
    return false;
2✔
21
  }
22

23
  /**
24
   * @return the name of the variable. Will be {@code null} if not a regular variable line (e.g. a comment or empty line).
25
   */
26
  public String getName() {
27

28
    return null;
2✔
29
  }
30

31
  /**
32
   * @return the value of the variable. Will be {@code null} if not a regular variable line (e.g. a comment or empty line).
33
   */
34
  public String getValue() {
35

36
    return null;
2✔
37
  }
38

39
  /**
40
   * @return the comment line (including the '#' character). Will be {@code null} if not a comment line.
41
   */
42
  public String getComment() {
43

44
    return null;
2✔
45
  }
46

47
  /**
48
   * @return the {@link VariableSource} of the variable.
49
   */
50
  public VariableSource getSource() {
51

52
    return null;
×
53
  }
54

55
  /**
56
   * @param newName the new variable {@link #getName() name}.
57
   * @return the new {@link VariableLine} with the modified {@link #getName() name}.
58
   */
59
  public VariableLine withName(String newName) {
60

61
    throw new UnsupportedOperationException();
×
62
  }
63

64
  /**
65
   * @param newValue the new variable {@link #getValue() value}.
66
   * @return the new {@link VariableLine} with the modified {@link #getValue() value}.
67
   */
68
  public VariableLine withValue(String newValue) {
69

70
    throw new UnsupportedOperationException();
×
71
  }
72

73
  /**
74
   * @param newExport the new {@link #isExport() export} flag.
75
   * @return the new {@link VariableLine} with the modified {@link #isExport() export} flag.
76
   */
77
  public VariableLine withExport(boolean newExport) {
78

79
    throw new UnsupportedOperationException();
×
80
  }
81

82
  static final class Variable extends VariableLine {
83

84
    private boolean export;
85

86
    private final String name;
87

88
    private final String value;
89

90
    private final String line;
91

92
    private final VariableSource source;
93

94
    Variable(boolean export, String name, String value) {
95

96
      this(export, name, value, null, null);
7✔
97
    }
1✔
98

99
    private Variable(boolean export, String name, String value, String line, VariableSource source) {
100

101
      super();
2✔
102
      this.export = export;
3✔
103
      this.name = name;
3✔
104
      this.value = value;
3✔
105
      if (line == null) {
2✔
106
        StringBuilder sb = new StringBuilder();
4✔
107
        if (export) {
2✔
108
          sb.append("export ");
4✔
109
        }
110
        sb.append(this.name);
5✔
111
        sb.append('=');
4✔
112
        sb.append(this.value);
5✔
113
        this.line = sb.toString();
4✔
114
      } else {
1✔
115
        this.line = line;
3✔
116
      }
117
      this.source = source;
3✔
118
    }
1✔
119

120
    @Override
121
    public boolean isExport() {
122

123
      return this.export;
3✔
124
    }
125

126
    @Override
127
    public String getName() {
128

129
      return this.name;
3✔
130
    }
131

132
    @Override
133
    public String getValue() {
134

135
      return this.value;
3✔
136
    }
137

138
    @Override
139
    public VariableSource getSource() {
140
      return source;
3✔
141
    }
142

143
    @Override
144
    public VariableLine withName(String newName) {
145

146
      if (newName.equals(this.name)) {
5!
147
        return this;
×
148
      }
149
      return new Variable(this.export, newName, this.value);
9✔
150
    }
151

152
    @Override
153
    public VariableLine withValue(String newValue) {
154

155
      if (newValue.equals(this.value)) {
5✔
156
        return this;
2✔
157
      }
158
      return new Variable(this.export, this.name, newValue);
9✔
159
    }
160

161
    @Override
162
    public VariableLine withExport(boolean newExport) {
163

164
      if (newExport == this.export) {
4!
165
        return this;
×
166
      }
167
      return new Variable(newExport, this.name, this.value);
9✔
168
    }
169

170
    @Override
171
    public String toString() {
172

173
      return this.line;
3✔
174
    }
175

176
  }
177

178
  static final class Comment extends VariableLine {
179

180
    private final String line;
181

182
    private Comment(String line) {
183

184
      super();
2✔
185
      this.line = line;
3✔
186
    }
1✔
187

188
    @Override
189
    public String getComment() {
190

191
      return this.line;
3✔
192
    }
193

194
    @Override
195
    public String toString() {
196

197
      return this.line;
3✔
198
    }
199

200
  }
201

202
  static final class Garbage extends VariableLine {
203

204
    private final String line;
205

206
    Garbage(String line) {
207

208
      super();
2✔
209
      this.line = line;
3✔
210
    }
1✔
211

212
    @Override
213
    public String toString() {
214

215
      return this.line;
3✔
216
    }
217

218
  }
219

220
  static final class Empty extends VariableLine {
221

222
    private final String line;
223

224
    Empty(String line) {
225

226
      super();
2✔
227
      this.line = line;
3✔
228
    }
1✔
229

230
    @Override
231
    public String toString() {
232

233
      return this.line;
3✔
234
    }
235

236
  }
237

238
  /**
239
   * Parses a {@link VariableLine} from {@link String}.
240
   *
241
   * @param line the {@link VariableLine} as {@link String} to parse.
242
   * @param logger the {@link IdeLogger}.
243
   * @param source the source where the given {@link String} to parse is from (e.g. the file path).
244
   * @return the parsed {@link VariableLine}.
245
   */
246
  public static VariableLine of(String line, IdeLogger logger, VariableSource source) {
247

248
    int len = line.length();
3✔
249
    int start = 0;
2✔
250
    while (start < len) {
3✔
251
      char c = line.charAt(start);
4✔
252
      if (c == ' ') {
3✔
253
        start++; // ignore leading spaces
2✔
254
      } else if (c == '#') {
3✔
255
        return new Comment(line);
5✔
256
      } else {
257
        break;
258
      }
259
    }
1✔
260
    if (start >= len) {
3✔
261
      return new Empty(line);
5✔
262
    }
263
    boolean export = false;
2✔
264
    int end = start;
2✔
265
    int space = -1;
2✔
266
    while (end < len) {
3✔
267
      char c = line.charAt(end);
4✔
268
      if (c == ' ') {
3✔
269
        if (space == -1) {
3✔
270
          space = end;
3✔
271
        }
272
      } else if (c == '=') {
3✔
273
        // .0123456789
274
        // "export ...="
275
        String name;
276
        if ((space == start + 6) && (end > start + 7) && line.substring(start, space).equals("export")) {
17!
277
          name = line.substring(space + 1, end).trim();
8✔
278
          if (name.isEmpty()) {
3✔
279
            name = line.substring(start, end).trim();
7✔
280
          } else {
281
            export = true;
3✔
282
          }
283
        } else {
284
          name = line.substring(start, end).trim();
6✔
285
        }
286
        String value = line.substring(end + 1).trim();
7✔
287
        if (value.startsWith("\"") && value.endsWith("\"")) {
8!
288
          value = value.substring(1, value.length() - 1);
8✔
289
        }
290
        return new Variable(export, name, value, line, source);
9✔
291
      }
292
      end++;
1✔
293
    }
1✔
294
    logger.warning("Ignoring corrupted line '{}' in {}", line, source);
13✔
295
    return new Garbage(line);
5✔
296
  }
297

298
  /**
299
   * @param export the {@link #isExport() export flag}.
300
   * @param name the {@link #getName() name}.
301
   * @param value the {@link #getValue() value}.
302
   * @return the {@link VariableLine} for the given values.
303
   */
304
  public static VariableLine of(boolean export, String name, String value) {
305

306
    return new Variable(export, name, value);
7✔
307
  }
308

309
  /**
310
   * @param export the {@link #isExport() export flag}.
311
   * @param name the {@link #getName() name}.
312
   * @param value the {@link #getValue() value}.
313
   * @param source the {@link #getSource() source} of the variable.
314
   * @return the {@link VariableLine} for the given values.
315
   */
316
  public static VariableLine of(boolean export, String name, String value, VariableSource source) {
317

318
    return new Variable(export, name, value, null, source);
9✔
319
  }
320

321
  /**
322
   * @param value the {@link String} value to check.
323
   * @return {@code true} if the value is a bash array (starts with "(" and ends with ")"), {@code false} otherwise.
324
   */
325
  public static boolean isBashArray(String value) {
326

327
    return value.startsWith("(") && value.endsWith(")");
12!
328
  }
329

330
  /**
331
   * Returns a list of String Variables.
332
   *
333
   * @param value String to parse
334
   * @param context the {@link IdeContext} for logging warnings (may be {@code null}).
335
   * @return List of variables.
336
   */
337
  public static List<String> parseArray(String value) {
338
    String csv = value;
2✔
339
    String separator = ",";
2✔
340
    if (isBashArray(value)) {
3✔
341
      csv = value.substring(1, value.length() - 1);
8✔
342
      separator = " ";
2✔
343
      // Support comma as separator in bash array syntax for convenience
344
      if (csv.contains(",")) {
4✔
345
        separator = ",";
2✔
346
      }
347
    }
348
    return Arrays.stream(csv.split(separator))
6✔
349
      .map(String::trim)
2✔
350
      .filter(s -> !s.isEmpty())
8✔
351
      .toList();
1✔
352
  }
353

354
}
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