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

devonfw / IDEasy / 11061200804

26 Sep 2024 10:27PM UTC coverage: 66.053% (-0.05%) from 66.107%
11061200804

push

github

web-flow
#593: #651: #564: #439: fixed bugs, refactored tool dependencies (#652)

2312 of 3848 branches covered (60.08%)

Branch coverage included in aggregate %.

6078 of 8854 relevant lines covered (68.65%)

3.03 hits per line

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

13.26
cli/src/main/java/com/devonfw/tools/ide/repo/CustomToolRepositoryImpl.java
1
package com.devonfw.tools.ide.repo;
2

3
import java.io.BufferedReader;
4
import java.io.InputStream;
5
import java.io.InputStreamReader;
6
import java.io.Reader;
7
import java.net.URLDecoder;
8
import java.nio.charset.StandardCharsets;
9
import java.nio.file.Files;
10
import java.nio.file.Path;
11
import java.util.ArrayList;
12
import java.util.Collection;
13
import java.util.Collections;
14
import java.util.HashMap;
15
import java.util.List;
16
import java.util.Map;
17
import javax.json.Json;
18
import javax.json.JsonArray;
19
import javax.json.JsonObject;
20
import javax.json.JsonReader;
21
import javax.json.JsonString;
22
import javax.json.JsonStructure;
23
import javax.json.JsonValue;
24
import javax.json.JsonValue.ValueType;
25

26
import com.devonfw.tools.ide.context.IdeContext;
27
import com.devonfw.tools.ide.url.model.file.UrlDownloadFileMetadata;
28
import com.devonfw.tools.ide.url.model.file.json.ToolDependency;
29
import com.devonfw.tools.ide.version.GenericVersionRange;
30
import com.devonfw.tools.ide.version.VersionIdentifier;
31

32
/**
33
 * Implementation of {@link CustomToolRepository}.
34
 */
35
public class CustomToolRepositoryImpl extends AbstractToolRepository implements CustomToolRepository {
36

37
  private final String id;
38

39
  private final Map<String, CustomTool> toolsMap;
40

41
  private final Collection<CustomTool> tools;
42

43
  /**
44
   * The constructor.
45
   *
46
   * @param context the owning {@link IdeContext}.
47
   * @param tools the {@link CustomTool}s.
48
   */
49
  public CustomToolRepositoryImpl(IdeContext context, Collection<CustomTool> tools) {
50

51
    super(context);
3✔
52
    this.toolsMap = new HashMap<>(tools.size());
7✔
53
    String repoId = null;
2✔
54
    for (CustomTool tool : tools) {
6!
55
      String name = tool.getTool();
×
56
      CustomTool duplicate = this.toolsMap.put(name, tool);
×
57
      if (duplicate != null) {
×
58
        throw new IllegalStateException("Duplicate custom tool '" + name + "'!");
×
59
      }
60
      if (repoId == null) {
×
61
        repoId = computeId(tool.getRepositoryUrl());
×
62
      }
63
    }
×
64
    if (repoId == null) {
2!
65
      repoId = "custom";
2✔
66
    }
67
    this.id = repoId;
3✔
68
    this.tools = Collections.unmodifiableCollection(this.toolsMap.values());
6✔
69
  }
1✔
70

71
  private static String computeId(String url) {
72

73
    String id = url;
×
74
    int schemaIndex = id.indexOf("://");
×
75
    if (schemaIndex > 0) {
×
76
      id = id.substring(schemaIndex + 3); // remove schema like "https://"
×
77
      id = URLDecoder.decode(id, StandardCharsets.UTF_8);
×
78
    }
79
    id.replace('\\', '/').replace("//", "/"); // normalize slashes
×
80
    if (id.startsWith("/")) {
×
81
      id = id.substring(1);
×
82
    }
83
    StringBuilder sb = new StringBuilder(id.length());
×
84
    if (schemaIndex > 0) { // was a URL?
×
85
      int slashIndex = id.indexOf('/');
×
86
      if (slashIndex > 0) {
×
87
        sb.append(id.substring(0, slashIndex).replace(':', '_'));
×
88
        sb.append('/');
×
89
        id = id.substring(slashIndex + 1);
×
90
      }
91
    }
92
    int length = id.length();
×
93
    for (int i = 0; i < length; i++) {
×
94
      char c = id.charAt(i);
×
95
      if (((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')) || ((c >= '0') && (c <= '9')) || (c == '.')
×
96
          || (c == '-')) {
97
        sb.append(c);
×
98
      } else {
99
        sb.append('_');
×
100
      }
101
    }
102
    return sb.toString();
×
103
  }
104

105
  @Override
106
  public String getId() {
107

108
    return this.id;
×
109
  }
110

111
  @Override
112
  protected UrlDownloadFileMetadata getMetadata(String tool, String edition, VersionIdentifier version) {
113

114
    CustomTool customTool = getCustomTool(tool);
×
115
    if (!version.equals(customTool.getVersion())) {
×
116
      throw new IllegalArgumentException("Undefined version '" + version + "' for custom tool '" + tool
×
117
          + "' - expected version '" + customTool.getVersion() + "'!");
×
118
    }
119
    if (!edition.equals(customTool.getEdition())) {
×
120
      throw new IllegalArgumentException("Undefined edition '" + edition + "' for custom tool '" + tool + "'!");
×
121
    }
122
    return customTool;
×
123
  }
124

125
  private CustomTool getCustomTool(String tool) {
126
    CustomTool customTool = this.toolsMap.get(tool);
×
127
    if (customTool == null) {
×
128
      throw new IllegalArgumentException("Undefined custom tool '" + tool + "'!");
×
129
    }
130
    return customTool;
×
131
  }
132

133
  @Override
134
  public VersionIdentifier resolveVersion(String tool, String edition, GenericVersionRange version) {
135

136
    CustomTool customTool = getCustomTool(tool);
×
137
    VersionIdentifier customToolVerstion = customTool.getVersion();
×
138
    if (!version.contains(customToolVerstion)) {
×
139
      throw new IllegalStateException(customTool + " does not satisfy version to install " + version);
×
140
    }
141
    return customToolVerstion;
×
142
  }
143

144
  @Override
145
  public Collection<CustomTool> getTools() {
146

147
    return this.tools;
3✔
148
  }
149

150
  @Override
151
  public Collection<ToolDependency> findDependencies(String tool, String edition, VersionIdentifier version) {
152
    
153
    return Collections.emptyList();
×
154
  }
155

156
  /**
157
   * @param context the owning {@link IdeContext}.
158
   * @return the {@link CustomToolRepository}.
159
   */
160
  public static CustomToolRepository of(IdeContext context) {
161

162
    Path settingsPath = context.getSettingsPath();
3✔
163
    Path customToolsJson = null;
2✔
164
    if (settingsPath != null) {
2✔
165
      customToolsJson = settingsPath.resolve(FILE_CUSTOM_TOOLS);
4✔
166
    }
167
    List<CustomTool> tools = new ArrayList<>();
4✔
168
    if ((customToolsJson != null) && Files.exists(customToolsJson)) {
7!
169
      try (InputStream in = Files.newInputStream(customToolsJson);
×
170
          Reader reader = new InputStreamReader(in, StandardCharsets.UTF_8)) {
×
171

172
        JsonReader jsonReader = Json.createReader(new BufferedReader(reader));
×
173
        JsonStructure json = jsonReader.read();
×
174
        JsonObject jsonRoot = requireObject(json);
×
175
        String defaultUrl = getString(jsonRoot, "url", "");
×
176
        JsonArray jsonTools = requireArray(jsonRoot.get("tools"));
×
177
        for (JsonValue jsonTool : jsonTools) {
×
178
          JsonObject jsonToolObject = requireObject(jsonTool);
×
179
          String name = getString(jsonToolObject, "name");
×
180
          String version = getString(jsonToolObject, "version");
×
181
          String url = getString(jsonToolObject, "url", defaultUrl);
×
182
          boolean osAgnostic = getBoolean(jsonToolObject, "os-agnostic", Boolean.FALSE);
×
183
          boolean archAgnostic = getBoolean(jsonToolObject, "arch-agnostic", Boolean.TRUE);
×
184
          if (url.isEmpty()) {
×
185
            throw new IllegalStateException("Missing 'url' property for tool '" + name + "'!");
×
186
          }
187
          // TODO
188
          String checksum = null;
×
189
          CustomTool customTool = new CustomTool(name, VersionIdentifier.of(version), osAgnostic, archAgnostic, url,
×
190
              checksum, context.getSystemInfo());
×
191
          tools.add(customTool);
×
192
        }
×
193
      } catch (Exception e) {
×
194
        throw new IllegalStateException("Failed to read JSON from " + customToolsJson, e);
×
195
      }
×
196
    }
197
    return new CustomToolRepositoryImpl(context, tools);
6✔
198
  }
199

200
  private static boolean getBoolean(JsonObject json, String property, Boolean defaultValue) {
201

202
    JsonValue value = json.get(property);
×
203
    if (value == null) {
×
204
      if (defaultValue == null) {
×
205
        throw new IllegalArgumentException("Missing string property '" + property + "' in JSON: " + json);
×
206
      }
207
      return defaultValue.booleanValue();
×
208
    }
209
    ValueType valueType = value.getValueType();
×
210
    if (valueType == ValueType.TRUE) {
×
211
      return true;
×
212
    } else if (valueType == ValueType.FALSE) {
×
213
      return false;
×
214
    } else {
215
      throw new IllegalStateException("Expected value type boolean but found " + valueType + " for JSON: " + json);
×
216
    }
217
  }
218

219
  private static String getString(JsonObject json, String property) {
220

221
    return getString(json, property, null);
×
222
  }
223

224
  private static String getString(JsonObject json, String property, String defaultValue) {
225

226
    JsonValue value = json.get(property);
×
227
    if (value == null) {
×
228
      if (defaultValue == null) {
×
229
        throw new IllegalArgumentException("Missing string property '" + property + "' in JSON: " + json);
×
230
      }
231
      return defaultValue;
×
232
    }
233
    require(value, ValueType.STRING);
×
234
    return ((JsonString) value).getString();
×
235
  }
236

237
  /**
238
   * @param json the {@link JsonValue} to check.
239
   */
240
  private static JsonObject requireObject(JsonValue json) {
241

242
    require(json, ValueType.OBJECT);
×
243
    return (JsonObject) json;
×
244
  }
245

246
  /**
247
   * @param json the {@link JsonValue} to check.
248
   */
249
  private static JsonArray requireArray(JsonValue json) {
250

251
    require(json, ValueType.ARRAY);
×
252
    return (JsonArray) json;
×
253
  }
254

255
  /**
256
   * @param json the {@link JsonValue} to check.
257
   * @param type the expected {@link ValueType}.
258
   */
259
  private static void require(JsonValue json, ValueType type) {
260

261
    ValueType actualType = json.getValueType();
×
262
    if (actualType != type) {
×
263
      throw new IllegalStateException(
×
264
          "Expected value type " + type + " but found " + actualType + " for JSON: " + json);
265
    }
266
  }
×
267

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