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

devonfw / IDEasy / 30885837220

04 Aug 2026 06:56AM UTC coverage: 72.825% (+0.2%) from 72.61%
30885837220

Pull #2230

github

web-flow
Merge 7754a3faf into cd9770b9c
Pull Request #2230: #1953: Complete cleanup commandlet implementation and review handoverShow more lines

5080 of 7725 branches covered (65.76%)

Branch coverage included in aggregate %.

13242 of 17434 relevant lines covered (75.96%)

3.23 hits per line

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

91.94
cli/src/main/java/com/devonfw/tools/ide/commandlet/CommandletManagerImpl.java
1
package com.devonfw.tools.ide.commandlet;
2

3
import java.util.Collection;
4
import java.util.Collections;
5
import java.util.HashMap;
6
import java.util.Iterator;
7
import java.util.List;
8
import java.util.Map;
9
import java.util.NoSuchElementException;
10

11
import org.slf4j.Logger;
12
import org.slf4j.LoggerFactory;
13

14
import com.devonfw.tools.ide.cli.CliArgument;
15
import com.devonfw.tools.ide.cli.CliArguments;
16
import com.devonfw.tools.ide.commandlet.cleanup.CleanupCommandlet;
17
import com.devonfw.tools.ide.completion.CompletionCandidateCollector;
18
import com.devonfw.tools.ide.context.IdeContext;
19
import com.devonfw.tools.ide.git.repository.RepositoryCommandlet;
20
import com.devonfw.tools.ide.property.KeywordProperty;
21
import com.devonfw.tools.ide.property.Property;
22
import com.devonfw.tools.ide.tool.androidstudio.AndroidStudio;
23
import com.devonfw.tools.ide.tool.aws.Aws;
24
import com.devonfw.tools.ide.tool.az.Azure;
25
import com.devonfw.tools.ide.tool.cdk.Cdk;
26
import com.devonfw.tools.ide.tool.claude.Claude;
27
import com.devonfw.tools.ide.tool.copilot.Copilot;
28
import com.devonfw.tools.ide.tool.corepack.Corepack;
29
import com.devonfw.tools.ide.tool.docker.Docker;
30
import com.devonfw.tools.ide.tool.dotnet.DotNet;
31
import com.devonfw.tools.ide.tool.eclipse.Eclipse;
32
import com.devonfw.tools.ide.tool.gcviewer.GcViewer;
33
import com.devonfw.tools.ide.tool.gh.Gh;
34
import com.devonfw.tools.ide.tool.go.Go;
35
import com.devonfw.tools.ide.tool.graalvm.GraalVm;
36
import com.devonfw.tools.ide.tool.gradle.Gradle;
37
import com.devonfw.tools.ide.tool.gui.Gui;
38
import com.devonfw.tools.ide.tool.helm.Helm;
39
import com.devonfw.tools.ide.tool.inso.Inso;
40
import com.devonfw.tools.ide.tool.intellij.Intellij;
41
import com.devonfw.tools.ide.tool.jasypt.Jasypt;
42
import com.devonfw.tools.ide.tool.java.Java;
43
import com.devonfw.tools.ide.tool.jmc.Jmc;
44
import com.devonfw.tools.ide.tool.just.Just;
45
import com.devonfw.tools.ide.tool.kotlinc.Kotlinc;
46
import com.devonfw.tools.ide.tool.kotlinc.KotlincNative;
47
import com.devonfw.tools.ide.tool.kubectl.KubeCtl;
48
import com.devonfw.tools.ide.tool.lazydocker.LazyDocker;
49
import com.devonfw.tools.ide.tool.msvc.Msvc;
50
import com.devonfw.tools.ide.tool.mvn.Mvn;
51
import com.devonfw.tools.ide.tool.mvnd.Mvnd;
52
import com.devonfw.tools.ide.tool.nest.Nest;
53
import com.devonfw.tools.ide.tool.ng.Ng;
54
import com.devonfw.tools.ide.tool.node.Node;
55
import com.devonfw.tools.ide.tool.npm.Npm;
56
import com.devonfw.tools.ide.tool.oc.Oc;
57
import com.devonfw.tools.ide.tool.pgadmin.PgAdmin;
58
import com.devonfw.tools.ide.tool.pip.Pip;
59
import com.devonfw.tools.ide.tool.pycharm.Pycharm;
60
import com.devonfw.tools.ide.tool.python.Python;
61
import com.devonfw.tools.ide.tool.quarkus.Quarkus;
62
import com.devonfw.tools.ide.tool.ruff.Ruff;
63
import com.devonfw.tools.ide.tool.rust.Rust;
64
import com.devonfw.tools.ide.tool.soapui.SoapUi;
65
import com.devonfw.tools.ide.tool.sonar.Sonar;
66
import com.devonfw.tools.ide.tool.spring.Spring;
67
import com.devonfw.tools.ide.tool.spyder.Spyder;
68
import com.devonfw.tools.ide.tool.squirrelsql.SquirrelSql;
69
import com.devonfw.tools.ide.tool.task.Task;
70
import com.devonfw.tools.ide.tool.terraform.Terraform;
71
import com.devonfw.tools.ide.tool.tomcat.Tomcat;
72
import com.devonfw.tools.ide.tool.uv.Uv;
73
import com.devonfw.tools.ide.tool.vscode.Vscode;
74
import com.devonfw.tools.ide.tool.yarn.Yarn;
75

76
/**
77
 * Implementation of {@link CommandletManager}.
78
 */
79
public class CommandletManagerImpl implements CommandletManager {
80

81
  private static final Logger LOG = LoggerFactory.getLogger(CommandletManagerImpl.class);
4✔
82

83
  private final IdeContext context;
84

85
  private final Map<Class<? extends Commandlet>, Commandlet> commandletTypeMap;
86

87
  private final Map<String, Commandlet> commandletNameMap;
88

89
  private final Map<String, Commandlet> firstKeywordMap;
90

91
  private final Collection<Commandlet> commandlets;
92

93
  /**
94
   * The constructor.
95
   *
96
   * @param context the {@link IdeContext}.
97
   */
98
  public CommandletManagerImpl(IdeContext context) {
99

100
    super();
2✔
101
    this.context = context;
3✔
102
    this.commandletTypeMap = new HashMap<>();
5✔
103
    this.commandletNameMap = new HashMap<>();
5✔
104
    this.firstKeywordMap = new HashMap<>();
5✔
105
    this.commandlets = Collections.unmodifiableCollection(this.commandletTypeMap.values());
6✔
106
    add(new HelpCommandlet(context));
6✔
107
    add(new EnvironmentCommandlet(context));
6✔
108
    add(new CompleteCommandlet(context));
6✔
109
    add(new ShellCommandlet(context));
6✔
110
    add(new InstallCommandlet(context));
6✔
111
    add(new VersionSetCommandlet(context));
6✔
112
    add(new VersionGetCommandlet(context));
6✔
113
    add(new VersionListCommandlet(context));
6✔
114
    add(new EditionGetCommandlet(context));
6✔
115
    add(new EditionSetCommandlet(context));
6✔
116
    add(new EditionListCommandlet(context));
6✔
117
    add(new VersionCommandlet(context));
6✔
118
    add(new StatusCommandlet(context));
6✔
119
    add(new RepositoryCommandlet(context));
6✔
120
    add(new UninstallCommandlet(context));
6✔
121
    add(new LnCommandlet(context));
6✔
122
    add(new UpdateCommandlet(context));
6✔
123
    add(new CleanupCommandlet(context));
6✔
124
    add(new UpgradeSettingsCommandlet(context));
6✔
125
    add(new CreateCommandlet(context));
6✔
126
    add(new BuildCommandlet(context));
6✔
127
    add(new ReleaseCommandlet(context));
6✔
128
    add(new InstallPluginCommandlet(context));
6✔
129
    add(new UninstallPluginCommandlet(context));
6✔
130
    add(new UpgradeCommandlet(context));
6✔
131
    add(new TruststoreCommandlet(context));
6✔
132
    add(new Gh(context));
6✔
133
    add(new Helm(context));
6✔
134
    add(new Java(context));
6✔
135
    add(new Ng(context));
6✔
136
    add(new Node(context));
6✔
137
    add(new Npm(context));
6✔
138
    add(new Mvn(context));
6✔
139
    add(new Msvc(context));
6✔
140
    add(new GcViewer(context));
6✔
141
    add(new Gradle(context));
6✔
142
    add(new Eclipse(context));
6✔
143
    add(new Terraform(context));
6✔
144
    add(new Oc(context));
6✔
145
    add(new Quarkus(context));
6✔
146
    add(new Rust(context));
6✔
147
    add(new Kotlinc(context));
6✔
148
    add(new KotlincNative(context));
6✔
149
    add(new KubeCtl(context));
6✔
150
    add(new Tomcat(context));
6✔
151
    add(new Task(context));
6✔
152
    add(new Vscode(context));
6✔
153
    add(new Azure(context));
6✔
154
    add(new Aws(context));
6✔
155
    add(new Jmc(context));
6✔
156
    add(new DotNet(context));
6✔
157
    add(new Inso(context));
6✔
158
    add(new Intellij(context));
6✔
159
    add(new Jasypt(context));
6✔
160
    add(new Docker(context));
6✔
161
    add(new Sonar(context));
6✔
162
    add(new AndroidStudio(context));
6✔
163
    add(new GraalVm(context));
6✔
164
    add(new PgAdmin(context));
6✔
165
    add(new LazyDocker(context));
6✔
166
    add(new Python(context));
6✔
167
    add(new Pycharm(context));
6✔
168
    add(new Spring(context));
6✔
169
    add(new Uv(context));
6✔
170
    add(new Yarn(context));
6✔
171
    add(new Copilot(context));
6✔
172
    add(new Corepack(context));
6✔
173
    add(new Pip(context));
6✔
174
    add(new Go(context));
6✔
175
    add(new Gui(context));
6✔
176
    add(new SquirrelSql(context));
6✔
177
    add(new Spyder(context));
6✔
178
    add(new Nest(context));
6✔
179
    add(new Cdk(context));
6✔
180
    add(new Claude(context));
6✔
181
    add(new Mvnd(context));
6✔
182
    add(new Just(context));
6✔
183
    add(new SoapUi(context));
6✔
184
    add(new Ruff(context));
6✔
185
  }
1✔
186

187
  /**
188
   * @param commandlet the {@link Commandlet} to add.
189
   */
190
  protected void add(Commandlet commandlet) {
191

192
    boolean hasRequiredProperty = false;
2✔
193
    List<Property<?>> properties = commandlet.getProperties();
3✔
194
    int propertyCount = properties.size();
3✔
195
    KeywordProperty keyword = commandlet.getFirstKeyword();
3✔
196
    if (keyword != null) {
2!
197
      String name = keyword.getName();
3✔
198
      registerKeyword(name, commandlet);
4✔
199
      String optionName = keyword.getOptionName();
3✔
200
      if (!optionName.equals(name)) {
4✔
201
        registerKeyword(optionName, commandlet);
4✔
202
      }
203
      String alias = keyword.getAlias();
3✔
204
      if (alias != null) {
2✔
205
        registerKeyword(alias, commandlet);
4✔
206
      }
207
    }
208
    for (int i = 0; i < propertyCount; i++) {
5!
209
      Property<?> property = properties.get(i);
5✔
210
      if (property.isRequired()) {
3!
211
        hasRequiredProperty = true;
2✔
212
        break;
1✔
213
      }
214
    }
215
    if (!hasRequiredProperty) {
2!
216
      throw new IllegalStateException("Commandlet " + commandlet + " must have at least one mandatory property!");
×
217
    }
218
    this.commandletTypeMap.put(commandlet.getClass(), commandlet);
7✔
219
    Commandlet duplicate = this.commandletNameMap.put(commandlet.getName(), commandlet);
8✔
220
    if (duplicate != null) {
2!
221
      throw new IllegalStateException("Commandlet " + commandlet + " has the same name as " + duplicate);
×
222
    }
223
  }
1✔
224

225
  private void registerKeyword(String keyword, Commandlet commandlet) {
226

227
    Commandlet duplicate = this.firstKeywordMap.putIfAbsent(keyword, commandlet);
7✔
228
    if (duplicate != null) {
2!
229
      LOG.debug("Duplicate keyword {} already used by {} so it cannot be associated also with {}", keyword, duplicate, commandlet);
×
230
    }
231
  }
1✔
232

233
  @Override
234
  public Collection<Commandlet> getCommandlets() {
235

236
    return this.commandlets;
3✔
237
  }
238

239
  @Override
240
  public <C extends Commandlet> C getCommandlet(Class<C> commandletType) {
241

242
    Commandlet commandlet = this.commandletTypeMap.get(commandletType);
6✔
243
    if (commandlet == null) {
2!
244
      throw new IllegalStateException("Commandlet for type " + commandletType + " is not registered!");
×
245
    }
246
    return commandletType.cast(commandlet);
5✔
247
  }
248

249
  @Override
250
  public Commandlet getCommandlet(String name) {
251

252
    return this.commandletNameMap.get(name);
6✔
253
  }
254

255
  @Override
256
  public Commandlet getCommandletByFirstKeyword(String keyword) {
257

258
    return this.firstKeywordMap.get(keyword);
6✔
259
  }
260

261
  @Override
262
  public Iterator<Commandlet> findCommandlet(CliArguments arguments, CompletionCandidateCollector collector) {
263

264
    CliArgument current = arguments.current();
3✔
265
    if (current.isEnd()) {
3✔
266
      return Collections.emptyIterator();
2✔
267
    }
268
    String keyword = current.get();
3✔
269
    Commandlet commandlet = getCommandletByFirstKeyword(keyword);
4✔
270
    if ((commandlet == null) && (collector == null)) {
4✔
271
      return Collections.emptyIterator();
2✔
272
    }
273
    return new CommandletFinder(commandlet, arguments.copy(), collector);
9✔
274
  }
275

276
  private final class CommandletFinder implements Iterator<Commandlet> {
1✔
277

278
    private final Commandlet firstCandidate;
279

280
    private final Iterator<Commandlet> commandletIterator;
281

282
    private final CliArguments arguments;
283

284
    private final CompletionCandidateCollector collector;
285

286
    private Commandlet next;
287

288
    private CommandletFinder(Commandlet firstCandidate, CliArguments arguments, CompletionCandidateCollector collector) {
8✔
289

290
      this.firstCandidate = firstCandidate;
3✔
291
      this.commandletIterator = getCommandlets().iterator();
5✔
292
      this.arguments = arguments;
3✔
293
      this.collector = collector;
3✔
294
      if (isSuitable(firstCandidate)) {
4✔
295
        this.next = firstCandidate;
4✔
296
      } else {
297
        this.next = findNext();
4✔
298
      }
299
    }
1✔
300

301
    @Override
302
    public boolean hasNext() {
303

304
      return this.next != null;
7✔
305
    }
306

307
    @Override
308
    public Commandlet next() {
309

310
      if (this.next == null) {
3!
311
        throw new NoSuchElementException();
×
312
      }
313
      Commandlet result = this.next;
3✔
314
      this.next = findNext();
4✔
315
      return result;
2✔
316
    }
317

318
    private boolean isSuitable(Commandlet commandlet) {
319

320
      return (commandlet != null) && (!commandlet.isIdeHomeRequired() || (context.getIdeHome() != null));
14✔
321
    }
322

323
    private Commandlet findNext() {
324

325
      while (this.commandletIterator.hasNext()) {
4✔
326
        Commandlet cmd = this.commandletIterator.next();
5✔
327
        if ((cmd != this.firstCandidate) && isSuitable(cmd)) {
8✔
328
          List<Property<?>> properties = cmd.getProperties();
3✔
329
          // validation should already be done in add method and could be removed here...
330
          if (properties.isEmpty()) {
3!
331
            assert false : cmd.getClass().getSimpleName() + " has no properties!";
×
332
          } else {
333
            Property<?> property = properties.getFirst();
4✔
334
            if (property instanceof KeywordProperty) {
3!
335
              boolean matches = property.apply(arguments.copy(), context, cmd, this.collector);
12✔
336
              if (matches) {
2✔
337
                return cmd;
2✔
338
              }
339
            } else {
1✔
340
              assert false : cmd.getClass().getSimpleName() + " is invalid as first property must be keyword property but is " + property;
×
341
            }
342
          }
343
        }
344
      }
1✔
345
      return null;
2✔
346
    }
347
  }
348
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc