• 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

82.88
cli/src/main/java/com/devonfw/tools/ide/tool/repository/CustomToolsJsonMapper.java
1
package com.devonfw.tools.ide.tool.repository;
2

3
import java.io.BufferedReader;
4
import java.io.BufferedWriter;
5
import java.nio.file.Files;
6
import java.nio.file.Path;
7
import java.util.ArrayList;
8
import java.util.List;
9

10
import com.devonfw.tools.ide.context.IdeContext;
11
import com.devonfw.tools.ide.environment.VariableLine;
12
import com.devonfw.tools.ide.json.JsonMapping;
13
import com.devonfw.tools.ide.os.OperatingSystem;
14
import com.devonfw.tools.ide.os.SystemArchitecture;
15
import com.devonfw.tools.ide.os.SystemInfo;
16
import com.fasterxml.jackson.databind.ObjectMapper;
17

18
/**
19
 * Mapper of {@link CustomToolsJson} from/to JSON.
20
 */
21
public class CustomToolsJsonMapper {
×
22

23
  private static final ObjectMapper MAPPER = JsonMapping.create();
3✔
24

25
  /**
26
   * @param customTools the {@link CustomToolsJson} to save.
27
   * @param path the {@link Path} of the file where to save as JSON.
28
   */
29
  public static void saveJson(CustomToolsJson customTools, Path path) {
30

31
    try (BufferedWriter writer = Files.newBufferedWriter(path)) {
5✔
32
      MAPPER.writerWithDefaultPrettyPrinter().writeValue(writer, customTools);
5✔
33
    } catch (Exception e) {
×
34
      throw new IllegalStateException("Failed to save file " + path, e);
×
35
    }
1✔
36
  }
1✔
37

38
  /**
39
   * @param path the {@link Path} of the JSON file to load.
40
   * @return the parsed {@link CustomToolsJson} or {@code null} if the {@link Path} is not an existing file.
41
   */
42
  public static CustomToolsJson loadJson(Path path) {
43
    CustomToolsJson customToolsJson = null;
2✔
44
    if (Files.isRegularFile(path)) {
5!
45
      try (BufferedReader reader = Files.newBufferedReader(path)) {
3✔
46
        customToolsJson = MAPPER.readValue(reader, CustomToolsJson.class);
6✔
47
      } catch (Exception e) {
×
48
        throw new IllegalStateException("Failed to load " + path, e);
×
49
      }
1✔
50
    }
51
    return customToolsJson;
2✔
52
  }
53

54
  /**
55
   * @param customTools the {@link CustomToolsJson} to convert.
56
   * @param context the {@link IdeContext}.
57
   * @return the converted {@link List} of {@link CustomToolMetadata}.
58
   */
59
  public static List<CustomToolMetadata> convert(CustomToolsJson customTools, IdeContext context) {
60

61
    String repositoryUrl = customTools.url();
3✔
62
    List<CustomToolJson> tools = customTools.tools();
3✔
63
    List<CustomToolMetadata> result = new ArrayList<>(tools.size());
6✔
64
    for (CustomToolJson customTool : tools) {
10✔
65
      result.add(convert(customTool, repositoryUrl, context));
7✔
66
    }
1✔
67
    return result;
2✔
68
  }
69

70
  private static CustomToolMetadata convert(CustomToolJson customTool, String repositoryUrl, IdeContext context) {
71

72
    String tool = customTool.name();
3✔
73
    String version = customTool.version();
3✔
74
    SystemInfo systemInfo = context.getSystemInfo();
3✔
75
    String repoUrl = customTool.url();
3✔
76
    if ((repoUrl == null) || repoUrl.isEmpty()) {
5!
77
      repoUrl = repositoryUrl;
×
78
    }
79
    int capacity = repoUrl.length() + 2 * tool.length() + 2 * version.length() + 7;
15✔
80
    OperatingSystem os;
81
    if (customTool.osAgnostic()) {
3✔
82
      os = null;
3✔
83
    } else {
84
      os = systemInfo.getOs();
3✔
85
      capacity += os.toString().length() + 1;
8✔
86
    }
87
    SystemArchitecture arch;
88
    if (customTool.archAgnostic()) {
3✔
89
      arch = null;
3✔
90
    } else {
91
      arch = systemInfo.getArchitecture();
3✔
92
      capacity += arch.toString().length() + 1;
8✔
93
    }
94
    StringBuilder sb = new StringBuilder(capacity);
5✔
95
    sb.append(repoUrl);
4✔
96
    char last = repoUrl.charAt(repoUrl.length() - 1);
7✔
97
    if ((last != '/') && (last != '\\')) {
3!
98
      sb.append('/');
×
99
    }
100
    sb.append(tool);
4✔
101
    sb.append('/');
4✔
102
    sb.append(version);
4✔
103
    sb.append('/');
4✔
104
    sb.append(tool);
4✔
105
    sb.append('-');
4✔
106
    sb.append(version);
4✔
107
    if (os != null) {
2✔
108
      sb.append('-');
4✔
109
      sb.append(os);
4✔
110
    }
111
    if (arch != null) {
2✔
112
      sb.append('-');
4✔
113
      sb.append(arch);
4✔
114
    }
115
    sb.append(".tgz");
4✔
116
    String url = sb.toString();
3✔
117
    return new CustomToolMetadata(tool, version, os, arch, url, null, repoUrl);
11✔
118
  }
119

120
  /**
121
   * Retrieves custom tools from a devonfw-ide legacy config.
122
   *
123
   * @param customToolsContent String of custom tools
124
   * @param context the {@link IdeContext}.
125
   * @return {@link CustomToolsJson}.
126
   */
127
  public static CustomToolsJson parseCustomToolsFromLegacyConfig(String customToolsContent, IdeContext context) {
128
    List<String> customToolEntries = VariableLine.parseArray(customToolsContent);
3✔
129
    if (customToolEntries.isEmpty()) {
3✔
130
      return null;
2✔
131
    }
132
    List<CustomToolJson> customTools = new ArrayList<>(customToolEntries.size());
6✔
133
    String defaultUrl = null;
2✔
134
    for (String customToolConfig : customToolEntries) {
10✔
135
      CustomToolJson customToolJson = parseCustomToolFromLegacyConfig(customToolConfig);
3✔
136
      if (customToolJson == null) {
2!
137
        context.warning("Invalid custom tool entry: {}", customToolConfig);
×
138
      } else {
139
        String url = customToolJson.url();
3✔
140
        if (defaultUrl == null) {
2✔
141
          if ((url == null) || url.isEmpty()) {
5!
142
            context.warning("First custom tool entry has no URL specified: {}", customToolConfig);
10✔
143
          } else {
144
            defaultUrl = url;
2✔
145
            customToolJson = customToolJson.withoutUrl();
4✔
146
          }
147
        } else if (defaultUrl.equals(url)) {
4!
148
          customToolJson = customToolJson.withoutUrl();
×
149
        }
150
        customTools.add(customToolJson);
4✔
151
      }
152
    }
1✔
153
    if (customTools.isEmpty() || (defaultUrl == null)) {
5!
154
      return null;
2✔
155
    }
156
    return new CustomToolsJson(defaultUrl, customTools);
6✔
157
  }
158

159
  private static CustomToolJson parseCustomToolFromLegacyConfig(String customToolConfig) {
160
    int firstColon = customToolConfig.indexOf(":");
4✔
161
    if (firstColon < 0) {
2!
162
      return null;
×
163
    }
164
    String toolName = customToolConfig.substring(0, firstColon);
5✔
165
    int secondColon = customToolConfig.indexOf(":", firstColon + 1);
7✔
166
    if (secondColon < 0) {
2!
167
      return null;
×
168
    }
169
    String version = customToolConfig.substring(firstColon + 1, secondColon);
7✔
170
    int thirdColon = customToolConfig.indexOf(":", secondColon + 1);
7✔
171
    boolean osAgnostic = false;
2✔
172
    boolean archAgnostic = false;
2✔
173
    String url = null;
2✔
174
    if (thirdColon > 0) {
2✔
175
      if (customToolConfig.substring(secondColon + 1, thirdColon).equals("all")) {
9!
176
        osAgnostic = true;
2✔
177
        archAgnostic = true;
2✔
178
        url = customToolConfig.substring(thirdColon + 1);
7✔
179
      } else {
180
        url = customToolConfig.substring(secondColon + 1);
×
181
      }
182
    }
183
    return new CustomToolJson(toolName, version, osAgnostic, archAgnostic, url);
9✔
184
  }
185

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