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

devonfw / IDEasy / 21013659204

14 Jan 2026 11:29PM UTC coverage: 70.365% (+0.5%) from 69.904%
21013659204

Pull #1675

github

web-flow
Merge 7a3aa598b into fcadaae82
Pull Request #1675: #1298: support ide-extra-tools.json #1658: prevent Jackson reflection

4015 of 6292 branches covered (63.81%)

Branch coverage included in aggregate %.

10440 of 14251 relevant lines covered (73.26%)

3.17 hits per line

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

70.97
cli/src/main/java/com/devonfw/tools/ide/tool/ToolInstallRequest.java
1
package com.devonfw.tools.ide.tool;
2

3
import java.nio.file.Path;
4

5
import com.devonfw.tools.ide.log.IdeLogger;
6
import com.devonfw.tools.ide.process.ProcessContext;
7
import com.devonfw.tools.ide.step.Step;
8
import com.devonfw.tools.ide.version.GenericVersionRange;
9
import com.devonfw.tools.ide.version.VersionIdentifier;
10
import com.devonfw.tools.ide.version.VersionRange;
11

12
/**
13
 * Container for data related to a tool installation.
14
 */
15
public final class ToolInstallRequest {
1✔
16

17
  private final ToolInstallRequest parent;
18

19
  private final boolean silent;
20

21
  private final boolean direct;
22

23
  private boolean cveCheckDone;
24

25
  private ToolEditionAndVersion requested;
26

27
  private ToolEditionAndVersion installed;
28

29
  private ProcessContext processContext;
30

31
  private Path toolPath;
32

33
  private boolean extraInstallation;
34

35
  private Step step;
36

37
  /**
38
   * The constructor.
39
   *
40
   * @param silent the {@link #isSilent() silent} flag.
41
   */
42
  public ToolInstallRequest(boolean silent) {
43
    this(null, silent, false);
5✔
44
  }
1✔
45

46
  /**
47
   * The constructor.
48
   *
49
   * @param parent the parent {@link ToolInstallRequest} (in case of a dependency).
50
   */
51
  public ToolInstallRequest(ToolInstallRequest parent) {
52
    this(parent, parent.silent, false);
6✔
53
  }
1✔
54

55
  /**
56
   * The constructor.
57
   *
58
   * @param silent the {@link #isSilent() silent} flag.
59
   * @param direct the {@link #isDirect() direct} flag.
60
   */
61
  private ToolInstallRequest(ToolInstallRequest parent, boolean silent, boolean direct) {
62
    super();
2✔
63
    this.parent = parent;
3✔
64
    this.silent = silent;
3✔
65
    this.direct = direct;
3✔
66
    if (parent != null) {
2✔
67
      this.processContext = parent.processContext;
4✔
68
    }
69
  }
1✔
70

71
  /**
72
   * @param logger the {@link IdeLogger} used to log an installation loop if found.
73
   * @return {@code true} if an installation loop was found and logged, {@code false} otherwise.
74
   */
75
  public boolean isInstallLoop(IdeLogger logger) {
76

77
    if ((this.requested == null) || (this.requested.getEdition() == null)) {
7!
78
      throw new IllegalStateException(); // this method was called too early
×
79
    }
80
    StringBuilder sb = new StringBuilder();
4✔
81
    boolean loopFound = detectInstallLoopRecursively(this.requested, sb);
6✔
82
    if (loopFound) {
2!
83
      logger.warning("Found installation loop:\n"
×
84
              + "{}\n"
85
              + "This typically indicates an internal bug in IDEasy.\n"
86
              + "Please report this bug, when you see this and include this entire warning message.\n"
87
              + "We are now trying to prevent an infinity loop and abort the recursive installation.",
88
          sb);
89
    }
90
    return loopFound;
2✔
91
  }
92

93
  private boolean detectInstallLoopRecursively(ToolEditionAndVersion toolEditionAndVersion, StringBuilder sb) {
94

95
    if (this.requested != toolEditionAndVersion) {
4✔
96
      if (this.requested.getEdition().equals(toolEditionAndVersion.getEdition())) {
7!
97
        if (this.requested.getResolvedVersion().equals(toolEditionAndVersion.getResolvedVersion())) {
×
98
          sb.append(this.requested);
×
99
          return true;
×
100
        }
101
      }
102
    }
103
    if (this.parent == null) {
3✔
104
      return false;
2✔
105
    }
106
    boolean loopFound = this.parent.detectInstallLoopRecursively(toolEditionAndVersion, sb);
6✔
107
    if (loopFound && (sb != null)) {
2!
108
      sb.append("-->");
×
109
      sb.append(this.requested);
×
110
    }
111
    return loopFound;
2✔
112
  }
113

114
  /**
115
   * @return {@code true} if this installation should be silent and log information like "tool already installed" only on debug level to avoid spam,
116
   *     {@code false} otherwise. A {@link #isDirect() direct} installation should never be silent.
117
   */
118
  public boolean isSilent() {
119

120
    return this.silent;
3✔
121
  }
122

123
  /**
124
   * @return {@code true} if the user directly triggered this tool installation (via "ide install tool"), {@code false} otherwise (indirect installation e.g. as
125
   *     dependency or via "ide create" or "ide update").
126
   */
127
  public boolean isDirect() {
128

129
    return this.direct;
×
130
  }
131

132
  /**
133
   * @return {@code true} if CVEs have already been checked, {@code false} otherwise.
134
   */
135
  public boolean isCveCheckDone() {
136

137
    return this.cveCheckDone;
3✔
138
  }
139

140
  void setCveCheckDone() {
141

142
    assert !this.cveCheckDone;
4!
143
    this.cveCheckDone = true;
3✔
144
  }
1✔
145

146
  /**
147
   * @return the {@link ToolEditionAndVersion} that is requested to be installed.
148
   */
149
  public ToolEditionAndVersion getRequested() {
150

151
    return this.requested;
3✔
152
  }
153

154
  /**
155
   * @param requested new value of {@link #getRequested()}.
156
   */
157
  public void setRequested(ToolEditionAndVersion requested) {
158
    if (this.requested != null) {
3!
159
      throw new IllegalStateException();
×
160
    }
161
    this.requested = requested;
3✔
162
  }
1✔
163

164
  /**
165
   * @return the {@link ToolEditionAndVersion} that is currently installed or {@code null} if the tool is not installed yet.
166
   */
167
  public ToolEditionAndVersion getInstalled() {
168

169
    return this.installed;
3✔
170
  }
171

172
  /**
173
   * @param installed new value of {@link #getInstalled()}.
174
   */
175
  public void setInstalled(ToolEditionAndVersion installed) {
176

177
    if (this.installed != null) {
3!
178
      throw new IllegalStateException();
×
179
    }
180
    this.installed = installed;
3✔
181
  }
1✔
182

183
  /**
184
   * @return the {@link ProcessContext} to use for executing the tool. Will also be configured during the installation (variables set, PATH extended).
185
   */
186
  public ProcessContext getProcessContext() {
187
    return this.processContext;
3✔
188
  }
189

190
  /**
191
   * @param processContext new value of {@link #getProcessContext()}.
192
   */
193
  public void setProcessContext(ProcessContext processContext) {
194

195
    if (this.processContext != null) {
3!
196
      throw new IllegalStateException();
×
197
    }
198
    this.processContext = processContext;
3✔
199
  }
1✔
200

201
  /**
202
   * @return the {@link Path} inside the project where the tool installation should be linked to.
203
   */
204
  public Path getToolPath() {
205

206
    return this.toolPath;
3✔
207
  }
208

209
  /**
210
   * @param toolPath new value of {@link #getToolPath()}.
211
   */
212
  public void setToolPath(Path toolPath) {
213

214
    if (this.toolPath != null) {
3!
215
      throw new IllegalStateException();
×
216
    }
217
    this.toolPath = toolPath;
3✔
218
  }
1✔
219

220
  /**
221
   * Called to trigger an extra installation with a custom tool path.
222
   *
223
   * @param toolPath new value of {@link #getToolPath()}.
224
   */
225
  public void setToolPathForExtraInstallation(Path toolPath) {
226

227
    setToolPath(toolPath);
3✔
228
    this.extraInstallation = true;
3✔
229
  }
1✔
230

231
  /**
232
   * @return {@code true} if this is an extra installation, {@code false} otherwise (standard installation).
233
   */
234
  public boolean isExtraInstallation() {
235

236
    return this.extraInstallation;
3✔
237
  }
238

239
  /**
240
   * @return the {@link Step} for the installation.
241
   */
242
  public Step getStep() {
243

244
    return this.step;
3✔
245
  }
246

247
  /**
248
   * @param step new value of {@link #getStep()}.
249
   */
250
  public void setStep(Step step) {
251

252
    if (this.step != null) {
×
253
      throw new IllegalStateException();
×
254
    }
255
    this.step = step;
×
256
  }
×
257

258
  /**
259
   * @return {@code true} if an additional installation is required not linked to the project's software folder (e.g. because of a transitive installation from
260
   *     a dependency that is incompatible with the project version), {@code false} otherwise.
261
   */
262
  public boolean isAdditionalInstallation() {
263
    if (this.requested != null) {
3!
264
      GenericVersionRange versionToInstall = this.requested.getVersion();
4✔
265
      return (versionToInstall instanceof VersionRange);
3✔
266
    }
267
    return false;
×
268
  }
269

270
  /**
271
   * @return {@code true} if the {@link #getRequested() requested edition and version} matches the {@link #getInstalled() installed edition and version}
272
   */
273
  public boolean isAlreadyInstalled() {
274

275
    if (this.installed == null) {
3✔
276
      return false;
2✔
277
    }
278
    VersionIdentifier installedVersion = this.installed.getResolvedVersion();
4✔
279
    if (installedVersion == null) {
2✔
280
      return false;
2✔
281
    }
282
    ToolEdition installedEdition = this.installed.getEdition();
4✔
283
    if (installedEdition == null) {
2!
284
      return false; // should actually never happen
×
285
    }
286
    if (!this.requested.getEdition().equals(installedEdition)) {
6✔
287
      return false;
2✔
288
    }
289
    return installedVersion.equals(this.requested.getResolvedVersion());
6✔
290
  }
291

292
  /**
293
   * @return a new {@link #isDirect() direct} {@link ToolInstallRequest}.
294
   */
295
  public static ToolInstallRequest ofDirect() {
296

297
    return new ToolInstallRequest(null, false, true);
7✔
298
  }
299
}
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