diff --git a/cli/pom.xml b/cli/pom.xml index 2bab9462f..113a30ccb 100644 --- a/cli/pom.xml +++ b/cli/pom.xml @@ -90,6 +90,13 @@ jansi ${jansi.version} + + + net.bytebuddy + byte-buddy + 1.14.15 + test + diff --git a/cli/src/main/java/com/devonfw/tools/ide/cli/CliArgument.java b/cli/src/main/java/com/devonfw/tools/ide/cli/CliArgument.java index 9d94ec3bc..43789a27b 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/cli/CliArgument.java +++ b/cli/src/main/java/com/devonfw/tools/ide/cli/CliArgument.java @@ -5,17 +5,16 @@ import java.util.Objects; /** - * Represents an argument for a command-line-interface (CLI) and a chain to all its {@link #getNext(boolean) - * successors}. + * Represents an argument for a command-line-interface (CLI) and a chain to all its {@link #getNext(boolean) successors}. * - * @since 1.0.0 * @see #getNext(boolean) + * @since 1.0.0 */ public class CliArgument { /** - * The {@link #get() argument} to indicate the end of the options. If this string is given as argument, any further - * arguments are treated as values. This allows to provide values (e.g. a filename) starting with a hyphen ('-'). + * The {@link #get() argument} to indicate the end of the options. If this string is given as argument, any further arguments are treated as values. This + * allows to provide values (e.g. a filename) starting with a hyphen ('-'). */ public static final String END_OPTIONS = "--"; @@ -144,8 +143,7 @@ public boolean isStart() { /** * @param successors the number of {@link #getNext() successors} expected. - * @return {@code true} if at least the given number of {@link #getNext() successors} are available, {@code false} - * otherwise. + * @return {@code true} if at least the given number of {@link #getNext() successors} are available, {@code false} otherwise. */ public boolean hasMoreSuccessorsThan(int successors) { @@ -173,8 +171,7 @@ public CliArgument getNext() { } /** - * @param splitShortOpts - if {@code true} then combined short options will be split (so instead of "-fbd" you will - * get "-f", "-b", "-d"). + * @param splitShortOpts - if {@code true} then combined short options will be split (so instead of "-fbd" you will get "-f", "-b", "-d"). * @return the next {@link CliArgument} or {@code null} if this is the {@link #isEnd() end}. */ public CliArgument getNext(boolean splitShortOpts) { @@ -194,8 +191,7 @@ public CliArgument getNext(boolean splitShortOpts) { } /** - * @return the {@code «key»} part if the {@link #get() argument} has the has the form {@code «key»=«value»}. Otherwise - * the {@link #get() argument} itself. + * @return the {@code «key»} part if the {@link #get() argument} has the has the form {@code «key»=«value»}. Otherwise the {@link #get() argument} itself. */ public String getKey() { @@ -204,8 +200,7 @@ public String getKey() { } /** - * @return the {@code «value»} part if the {@link #get() argument} has the has the form {@code «key»=«value»}. - * Otherwise {@code null}. + * @return the {@code «value»} part if the {@link #get() argument} has the has the form {@code «key»=«value»}. Otherwise {@code null}. */ public String getValue() { @@ -228,8 +223,8 @@ private void initKeyValue() { } /** - * @return a {@link String} representing all arguments from this {@link CliArgument} recursively along is - * {@link #getNext(boolean) next} arguments to the {@link #isEnd() end}. + * @return a {@link String} representing all arguments from this {@link CliArgument} recursively along is {@link #getNext(boolean) next} arguments to the + * {@link #isEnd() end}. */ public String getArgs() { diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/Commandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/Commandlet.java index 877ee576f..1f32e6477 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/Commandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/Commandlet.java @@ -1,5 +1,11 @@ package com.devonfw.tools.ide.commandlet; +import com.devonfw.tools.ide.context.IdeContext; +import com.devonfw.tools.ide.property.KeywordProperty; +import com.devonfw.tools.ide.property.Property; +import com.devonfw.tools.ide.tool.ToolCommandlet; +import com.devonfw.tools.ide.version.VersionIdentifier; + import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -7,12 +13,6 @@ import java.util.Map; import java.util.Objects; -import com.devonfw.tools.ide.context.IdeContext; -import com.devonfw.tools.ide.property.KeywordProperty; -import com.devonfw.tools.ide.property.Property; -import com.devonfw.tools.ide.tool.ToolCommandlet; -import com.devonfw.tools.ide.version.VersionIdentifier; - /** * A {@link Commandlet} is a sub-command of the IDE CLI. */ @@ -68,8 +68,7 @@ public List> getValues() { } /** - * @param nameOrAlias the potential {@link Property#getName() name} or {@link Property#getAlias() alias} of the - * requested {@link Property}. + * @param nameOrAlias the potential {@link Property#getName() name} or {@link Property#getAlias() alias} of the requested {@link Property}. * @return the requested {@link Property property} or {@code null} if not found. */ public Property getOption(String nameOrAlias) { @@ -108,8 +107,7 @@ protected void addKeyword(Property property) { protected

> P add(P property) { if (this.multiValued != null) { - throw new IllegalStateException( - "The multi-valued property " + this.multiValued + " can not be followed by " + property); + throw new IllegalStateException("The multi-valued property " + this.multiValued + " can not be followed by " + property); } this.propertiesList.add(property); if (property.isOption()) { @@ -147,8 +145,7 @@ private void add(String name, Property property, boolean alias) { public abstract String getName(); /** - * @return the first keyword of this {@link Commandlet}. Typically the same as {@link #getName() name} but may also - * differ (e.g. "set" vs. "set-version"). + * @return the first keyword of this {@link Commandlet}. Typically the same as {@link #getName() name} but may also differ (e.g. "set" vs. "set-version"). */ public String getKeyword() { @@ -167,8 +164,7 @@ protected C getCommandlet(Class commandletType) { } /** - * @return {@code true} if {@link IdeContext#getIdeHome() IDE_HOME} is required for this commandlet, {@code false} - * otherwise. + * @return {@code true} if {@link IdeContext#getIdeHome() IDE_HOME} is required for this commandlet, {@code false} otherwise. */ public boolean isIdeHomeRequired() { @@ -176,8 +172,7 @@ public boolean isIdeHomeRequired() { } /** - * @return {@code true} to suppress the {@link com.devonfw.tools.ide.step.StepImpl#logSummary(boolean) step summary - * success message}. + * @return {@code true} to suppress the {@link com.devonfw.tools.ide.step.StepImpl#logSummary(boolean) step summary success message}. */ public boolean isSuppressStepSuccess() { @@ -190,8 +185,7 @@ public boolean isSuppressStepSuccess() { public abstract void run(); /** - * @return {@code true} if this {@link Commandlet} is the valid candidate to be {@link #run()}, {@code false} - * otherwise. + * @return {@code true} if this {@link Commandlet} is the valid candidate to be {@link #run()}, {@code false} otherwise. * @see Property#validate() */ public boolean validate() { @@ -217,8 +211,8 @@ public String toString() { } /** - * @return the {@link ToolCommandlet} set in a {@link Property} of this commandlet used for auto-completion of a - * {@link VersionIdentifier} or {@code null} if not exists or not configured. + * @return the {@link ToolCommandlet} set in a {@link Property} of this commandlet used for auto-completion of a {@link VersionIdentifier} or {@code null} if + * not exists or not configured. */ public ToolCommandlet getToolForVersionCompletion() { diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/CompleteCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/CompleteCommandlet.java index a9d856a98..10edb2faf 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/CompleteCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/CompleteCommandlet.java @@ -1,20 +1,20 @@ package com.devonfw.tools.ide.commandlet; -import java.util.List; - import com.devonfw.tools.ide.cli.CliArguments; import com.devonfw.tools.ide.completion.CompletionCandidate; import com.devonfw.tools.ide.context.AbstractIdeContext; import com.devonfw.tools.ide.context.IdeContext; -import com.devonfw.tools.ide.property.StringListProperty; +import com.devonfw.tools.ide.property.StringProperty; + +import java.util.List; /** * {@link Commandlet} for auto-completion. */ public final class CompleteCommandlet extends Commandlet { - /** {@link StringListProperty} with the current CLI arguments to complete. */ - public final StringListProperty args; + /** {@link StringProperty} with the current CLI arguments to complete. */ + public final StringProperty args; /** * The constructor. @@ -25,7 +25,7 @@ public CompleteCommandlet(IdeContext context) { super(context); addKeyword(getName()); - this.args = add(new StringListProperty("", false, "args")); + this.args = add(new StringProperty("", false, true, "args")); } @Override diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java index f216099a4..6781f70c7 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/RepositoryCommandlet.java @@ -1,18 +1,12 @@ package com.devonfw.tools.ide.commandlet; import com.devonfw.tools.ide.context.IdeContext; -import com.devonfw.tools.ide.property.PathProperty; import com.devonfw.tools.ide.property.RepositoryProperty; import com.devonfw.tools.ide.tool.ToolCommandlet; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; -import java.util.ArrayList; import java.util.List; -import java.util.Properties; import static com.devonfw.tools.ide.commandlet.RepositoryConfig.loadProperties; @@ -65,8 +59,7 @@ public void run() { return; } - List propertiesFiles = this.context.getFileAccess().listChildren(repositories, - path -> path.getFileName().toString().endsWith(".properties")); + List propertiesFiles = this.context.getFileAccess().listChildren(repositories, path -> path.getFileName().toString().endsWith(".properties")); boolean forceMode = this.context.isForceMode(); for (Path propertiesFile : propertiesFiles) { @@ -93,8 +86,7 @@ private void doImportRepository(Path repositoryFile, boolean forceMode) { String repository = repositoryConfig.path(); String gitUrl = repositoryConfig.gitUrl(); if (repository == null || "".equals(repository) || gitUrl == null || "".equals(gitUrl)) { - this.context.warning("Invalid repository configuration {} - both 'path' and 'git-url' have to be defined." - , repositoryFile.getFileName().toString()); + this.context.warning("Invalid repository configuration {} - both 'path' and 'git-url' have to be defined.", repositoryFile.getFileName().toString()); return; } @@ -112,11 +104,10 @@ private void doImportRepository(Path repositoryFile, boolean forceMode) { if (buildCmd != null && !buildCmd.isEmpty()) { String[] command = buildCmd.split("\\s+"); ToolCommandlet commandlet = this.context.getCommandletManager().getToolCommandlet(command[0]); - List args = new ArrayList<>(command.length - 1); + for (int i = 1; i < command.length; i++) { - args.add(command[i]); + commandlet.arguments.addValue(command[i]); } - commandlet.arguments.setValue(args); commandlet.run(); } else { this.context.info("Build command not set. Skipping build for repository."); diff --git a/cli/src/main/java/com/devonfw/tools/ide/commandlet/UninstallCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/commandlet/UninstallCommandlet.java index 00002b23c..0fb940066 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/commandlet/UninstallCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/commandlet/UninstallCommandlet.java @@ -1,11 +1,11 @@ package com.devonfw.tools.ide.commandlet; -import java.nio.file.Files; -import java.nio.file.Path; - import com.devonfw.tools.ide.context.IdeContext; -import com.devonfw.tools.ide.io.FileAccess; import com.devonfw.tools.ide.property.ToolProperty; +import com.devonfw.tools.ide.tool.ToolCommandlet; + +import java.nio.file.Files; +import java.nio.file.Path; /** * An internal {@link Commandlet} to uninstall a tool. @@ -13,7 +13,7 @@ public class UninstallCommandlet extends Commandlet { /** The tool to uninstall. */ - public final ToolProperty tool; + public final ToolProperty tools; /** * The constructor. @@ -24,7 +24,7 @@ public UninstallCommandlet(IdeContext context) { super(context); addKeyword(getName()); - this.tool = add(new ToolProperty("", true, "tool")); + this.tools = add(new ToolProperty("", true, true, "tool")); } @Override @@ -36,18 +36,25 @@ public String getName() { @Override public void run() { - String commandletName = this.tool.getValue().getName(); - Path softwarePath = context.getSoftwarePath().resolve(commandletName); - if (Files.exists(softwarePath)) { - FileAccess fileAccess = context.getFileAccess(); + for (int i = 0; i < this.tools.getValueCount(); i++) { + ToolCommandlet toolCommandlet = this.tools.getValue(i); + try { - fileAccess.delete(softwarePath); - this.context.success("Successfully uninstalled " + commandletName); + String commandletName = toolCommandlet.getName(); + Path softwarePath = context.getSoftwarePath().resolve(commandletName); + if (Files.exists(softwarePath)) { + try { + context.getFileAccess().delete(softwarePath); + this.context.success("Successfully uninstalled " + commandletName); + } catch (Exception e) { + this.context.error("Couldn't uninstall " + commandletName); + } + } else { + this.context.warning("An installed version of " + commandletName + " does not exist"); + } } catch (Exception e) { - throw new IllegalStateException("Couldn't uninstall " + commandletName, e); + this.context.error(e.getMessage()); } - } else { - this.context.info("An installed version of " + commandletName + " does not exist"); } } } diff --git a/cli/src/main/java/com/devonfw/tools/ide/property/BooleanProperty.java b/cli/src/main/java/com/devonfw/tools/ide/property/BooleanProperty.java index 3f9cf0de2..680b62b1f 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/property/BooleanProperty.java +++ b/cli/src/main/java/com/devonfw/tools/ide/property/BooleanProperty.java @@ -19,7 +19,7 @@ public class BooleanProperty extends Property { */ public BooleanProperty(String name, boolean required, String alias) { - super(name, required, alias, null); + super(name, required, alias); } @Override @@ -82,8 +82,8 @@ public void setValueAsString(String valueAsString, IdeContext context) { } @Override - protected boolean applyValue(String argValue, boolean lookahead, CliArguments args, IdeContext context, - Commandlet commandlet, CompletionCandidateCollector collector) { + protected boolean applyValue(String argValue, boolean lookahead, CliArguments args, IdeContext context, Commandlet commandlet, + CompletionCandidateCollector collector) { if (lookahead) { Boolean b = parse(argValue); diff --git a/cli/src/main/java/com/devonfw/tools/ide/property/CommandletProperty.java b/cli/src/main/java/com/devonfw/tools/ide/property/CommandletProperty.java index a0854d6bc..0baad0ca1 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/property/CommandletProperty.java +++ b/cli/src/main/java/com/devonfw/tools/ide/property/CommandletProperty.java @@ -1,11 +1,11 @@ package com.devonfw.tools.ide.property; -import java.util.function.Consumer; - import com.devonfw.tools.ide.commandlet.Commandlet; import com.devonfw.tools.ide.completion.CompletionCandidateCollector; import com.devonfw.tools.ide.context.IdeContext; +import java.util.function.Consumer; + /** * {@link Property} with {@link #getValueType() value type} {@link Commandlet}. */ @@ -33,7 +33,7 @@ public CommandletProperty(String name, boolean required, String alias) { */ public CommandletProperty(String name, boolean required, String alias, Consumer validator) { - super(name, required, alias, validator); + super(name, required, alias, false, validator); } @Override @@ -49,8 +49,7 @@ protected String format(Commandlet valueToFormat) { } @Override - protected void completeValue(String arg, IdeContext context, Commandlet commandlet, - CompletionCandidateCollector collector) { + protected void completeValue(String arg, IdeContext context, Commandlet commandlet, CompletionCandidateCollector collector) { for (Commandlet cmd : context.getCommandletManager().getCommandlets()) { String cmdName = cmd.getName(); diff --git a/cli/src/main/java/com/devonfw/tools/ide/property/EditionProperty.java b/cli/src/main/java/com/devonfw/tools/ide/property/EditionProperty.java index f2226254b..cf17227a3 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/property/EditionProperty.java +++ b/cli/src/main/java/com/devonfw/tools/ide/property/EditionProperty.java @@ -28,7 +28,7 @@ public EditionProperty(String name, boolean required, String alias) { */ public EditionProperty(String name, boolean required, String alias, Consumer validator) { - super(name, required, alias, validator); + super(name, required, alias, false, validator); } @Override diff --git a/cli/src/main/java/com/devonfw/tools/ide/property/EnumProperty.java b/cli/src/main/java/com/devonfw/tools/ide/property/EnumProperty.java index d1db549e0..86dfb0b68 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/property/EnumProperty.java +++ b/cli/src/main/java/com/devonfw/tools/ide/property/EnumProperty.java @@ -1,11 +1,11 @@ package com.devonfw.tools.ide.property; -import java.util.Locale; - import com.devonfw.tools.ide.commandlet.Commandlet; import com.devonfw.tools.ide.completion.CompletionCandidateCollector; import com.devonfw.tools.ide.context.IdeContext; +import java.util.Locale; + /** * {@link Property} with {@link #getValueType() value type} {@link Boolean}. */ @@ -22,7 +22,7 @@ public class EnumProperty> extends Property { */ public EnumProperty(String name, boolean required, String alias, Class valueType) { - super(name, required, alias, null); + super(name, required, alias); this.valueType = valueType; } @@ -46,8 +46,7 @@ public V parse(String valueAsString, IdeContext context) { } @Override - protected void completeValue(String arg, IdeContext context, Commandlet commandlet, - CompletionCandidateCollector collector) { + protected void completeValue(String arg, IdeContext context, Commandlet commandlet, CompletionCandidateCollector collector) { for (V enumConstant : this.valueType.getEnumConstants()) { String name = enumConstant.name().toLowerCase(Locale.ROOT); diff --git a/cli/src/main/java/com/devonfw/tools/ide/property/LocaleProperty.java b/cli/src/main/java/com/devonfw/tools/ide/property/LocaleProperty.java index 0db5e9750..b28a9ffc1 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/property/LocaleProperty.java +++ b/cli/src/main/java/com/devonfw/tools/ide/property/LocaleProperty.java @@ -1,13 +1,13 @@ package com.devonfw.tools.ide.property; -import java.util.Arrays; -import java.util.Locale; -import java.util.function.Consumer; - import com.devonfw.tools.ide.commandlet.Commandlet; import com.devonfw.tools.ide.completion.CompletionCandidateCollector; import com.devonfw.tools.ide.context.IdeContext; +import java.util.Arrays; +import java.util.Locale; +import java.util.function.Consumer; + /** * {@link Property} with {@link Locale} as {@link #getValueType() value type}. */ @@ -37,7 +37,7 @@ public LocaleProperty(String name, boolean required, String alias) { */ public LocaleProperty(String name, boolean required, String alias, Consumer validator) { - super(name, required, alias, validator); + super(name, required, alias, false, validator); } @Override @@ -53,8 +53,7 @@ public Locale parse(String valueAsString, IdeContext context) { } @Override - protected void completeValue(String arg, IdeContext context, Commandlet commandlet, - CompletionCandidateCollector collector) { + protected void completeValue(String arg, IdeContext context, Commandlet commandlet, CompletionCandidateCollector collector) { collector.addAllMatches(arg, getAvailableLocales(), this, commandlet); } diff --git a/cli/src/main/java/com/devonfw/tools/ide/property/NumberProperty.java b/cli/src/main/java/com/devonfw/tools/ide/property/NumberProperty.java index ccc6b7ddc..0b947f623 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/property/NumberProperty.java +++ b/cli/src/main/java/com/devonfw/tools/ide/property/NumberProperty.java @@ -1,9 +1,9 @@ package com.devonfw.tools.ide.property; -import java.util.function.Consumer; - import com.devonfw.tools.ide.context.IdeContext; +import java.util.function.Consumer; + /** * {@link Property} with {@link #getValueType() value type} {@link Long}. */ @@ -31,7 +31,7 @@ public NumberProperty(String name, boolean required, String alias) { */ public NumberProperty(String name, boolean required, String alias, Consumer validator) { - super(name, required, alias, validator); + super(name, required, alias, false, validator); } @Override diff --git a/cli/src/main/java/com/devonfw/tools/ide/property/PathProperty.java b/cli/src/main/java/com/devonfw/tools/ide/property/PathProperty.java index 92c86155a..2e7a56603 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/property/PathProperty.java +++ b/cli/src/main/java/com/devonfw/tools/ide/property/PathProperty.java @@ -1,15 +1,15 @@ package com.devonfw.tools.ide.property; +import com.devonfw.tools.ide.commandlet.Commandlet; +import com.devonfw.tools.ide.completion.CompletionCandidateCollector; +import com.devonfw.tools.ide.context.IdeContext; + import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.function.Consumer; import java.util.stream.Stream; -import com.devonfw.tools.ide.commandlet.Commandlet; -import com.devonfw.tools.ide.completion.CompletionCandidateCollector; -import com.devonfw.tools.ide.context.IdeContext; - /** * {@link Property} with {@link Path} as {@link #getValueType() value type}. */ @@ -41,7 +41,7 @@ public PathProperty(String name, boolean required, String alias, boolean mustExi */ public PathProperty(String name, boolean required, String alias, boolean mustExist, Consumer validator) { - super(name, required, alias, validator); + super(name, required, alias, false, validator); this.mustExist = mustExist; } @@ -61,10 +61,10 @@ public Path parse(String valueAsString, IdeContext context) { public boolean validate() { if (this.value != null) { - if (Files.exists(this.value)) { - if (isPathRequiredToBeFile() && !Files.isRegularFile(this.value)) { + if (Files.exists(getValue())) { + if (isPathRequiredToBeFile() && !Files.isRegularFile(getValue())) { throw new IllegalStateException("Path " + this.value + " is not a file."); - } else if (isPathRequiredToBeFolder() && !Files.isDirectory(this.value)) { + } else if (isPathRequiredToBeFolder() && !Files.isDirectory(getValue())) { throw new IllegalStateException("Path " + this.value + " is not a folder."); } } else if (isPathRequiredToExist()) { @@ -76,8 +76,8 @@ public boolean validate() { } /** - * @return {@code true} if the {@link Path} {@link #getValue() value} must - * {@link Files#exists(Path, java.nio.file.LinkOption...) exist} if set, {@code false} otherwise. + * @return {@code true} if the {@link Path} {@link #getValue() value} must {@link Files#exists(Path, java.nio.file.LinkOption...) exist} if set, {@code false} + * otherwise. */ protected boolean isPathRequiredToExist() { @@ -85,8 +85,8 @@ protected boolean isPathRequiredToExist() { } /** - * @return {@code true} if the {@link Path} {@link #getValue() value} must be a - * {@link Files#isDirectory(Path, java.nio.file.LinkOption...) folder} if it exists, {@code false} otherwise. + * @return {@code true} if the {@link Path} {@link #getValue() value} must be a {@link Files#isDirectory(Path, java.nio.file.LinkOption...) folder} if it + * exists, {@code false} otherwise. */ protected boolean isPathRequiredToBeFolder() { @@ -94,8 +94,8 @@ protected boolean isPathRequiredToBeFolder() { } /** - * @return {@code true} if the {@link Path} {@link #getValue() value} must be a - * {@link Files#isRegularFile(Path, java.nio.file.LinkOption...) file} if it exists, {@code false} otherwise. + * @return {@code true} if the {@link Path} {@link #getValue() value} must be a {@link Files#isRegularFile(Path, java.nio.file.LinkOption...) file} if it + * exists, {@code false} otherwise. */ protected boolean isPathRequiredToBeFile() { @@ -103,16 +103,14 @@ protected boolean isPathRequiredToBeFile() { } @Override - protected void completeValue(String arg, IdeContext context, Commandlet commandlet, - CompletionCandidateCollector collector) { + protected void completeValue(String arg, IdeContext context, Commandlet commandlet, CompletionCandidateCollector collector) { Path path = Path.of(arg); Path parent = path.getParent(); String filename = path.getFileName().toString(); if (Files.isDirectory(parent)) { try (Stream children = Files.list(parent)) { - children.filter(child -> isValidPath(path, filename)) - .forEach(child -> collector.add(child.toString(), null, this, commandlet)); + children.filter(child -> isValidPath(path, filename)).forEach(child -> collector.add(child.toString(), null, this, commandlet)); } catch (IOException e) { throw new IllegalStateException(e); } @@ -121,9 +119,9 @@ protected void completeValue(String arg, IdeContext context, Commandlet commandl private boolean isValidPath(Path path, String filename) { - if (isPathRequiredToBeFile() && !Files.isRegularFile(this.value)) { + if (isPathRequiredToBeFile() && !Files.isRegularFile(getValue())) { return false; - } else if (isPathRequiredToBeFolder() && !Files.isDirectory(this.value)) { + } else if (isPathRequiredToBeFolder() && !Files.isDirectory(getValue())) { return false; } return path.getFileName().toString().startsWith(filename); diff --git a/cli/src/main/java/com/devonfw/tools/ide/property/Property.java b/cli/src/main/java/com/devonfw/tools/ide/property/Property.java index 5f79211bc..a936f7b7f 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/property/Property.java +++ b/cli/src/main/java/com/devonfw/tools/ide/property/Property.java @@ -1,8 +1,5 @@ package com.devonfw.tools.ide.property; -import java.util.Objects; -import java.util.function.Consumer; - import com.devonfw.tools.ide.cli.CliArgument; import com.devonfw.tools.ide.cli.CliArguments; import com.devonfw.tools.ide.commandlet.Commandlet; @@ -10,14 +7,17 @@ import com.devonfw.tools.ide.completion.CompletionCandidateCollectorAdapter; import com.devonfw.tools.ide.context.IdeContext; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.function.Consumer; + /** - * A {@link Property} is a simple container for a {@link #getValue() value} with a fixed {@link #getName() name} and - * {@link #getValueType() type}. Further we use a {@link Property} as {@link CliArgument CLI argument} so it is either - * an {@link #isOption() option} or a {@link #isValue() value}.
- * In classic Java Beans a property only exists implicit as a combination of a getter and a setter. This class makes it - * an explicit construct that allows to {@link #getValue() get} and {@link #setValue(Object) set} the value of a - * property easily in a generic way including to retrieve its {@link #getName() name} and {@link #getValueType() type}. - * Besides simplification this also prevents the use of reflection for generic CLI parsing with assigning and validating + * A {@link Property} is a simple container for a {@link #getValue() value} with a fixed {@link #getName() name} and {@link #getValueType() type}. Further we + * use a {@link Property} as {@link CliArgument CLI argument} so it is either an {@link #isOption() option} or a {@link #isValue() value}.
In classic Java + * Beans a property only exists implicit as a combination of a getter and a setter. This class makes it an explicit construct that allows to + * {@link #getValue() get} and {@link #setValue(Object) set} the value of a property easily in a generic way including to retrieve its {@link #getName() name} + * and {@link #getValueType() type}. Besides simplification this also prevents the use of reflection for generic CLI parsing with assigning and validating * arguments what is beneficial for compiling the Java code to a native image using GraalVM. * * @param the {@link #getValueType() value type}. @@ -39,8 +39,11 @@ public abstract class Property { private final Consumer validator; + /** @see #isMultiValued() */ + private final boolean multivalued; + /** @see #getValue() */ - protected V value; + protected final List value = new ArrayList<>(); /** * The constructor. @@ -48,15 +51,34 @@ public abstract class Property { * @param name the {@link #getName() property name}. * @param required the {@link #isRequired() required flag}. * @param alias the {@link #getAlias() property alias}. + */ + public Property(String name, boolean required, String alias) { + + super(); + this.name = name; + this.required = required; + this.alias = alias; + this.multivalued = false; + this.validator = null; + } + + /** + * The constructor. + * + * @param name the {@link #getName() property name}. + * @param required the {@link #isRequired() required flag}. + * @param alias the {@link #getAlias() property alias}. + * @param multivalued the boolean flag about multiple arguments * @param validator the {@link Consumer} used to {@link #validate() validate} the {@link #getValue() value}. */ - public Property(String name, boolean required, String alias, Consumer validator) { + public Property(String name, boolean required, String alias, boolean multivalued, Consumer validator) { super(); this.name = name; this.required = required; this.alias = alias; this.validator = validator; + this.multivalued = multivalued; } /** @@ -77,8 +99,7 @@ public String getAlias() { } /** - * @return the {@link #getName() name} or the {@link #getAlias() alias} if {@link #getName() name} is - * {@link String#isEmpty() empty}. + * @return the {@link #getName() name} or the {@link #getAlias() alias} if {@link #getName() name} is {@link String#isEmpty() empty}. */ public String getNameOrAlias() { @@ -89,8 +110,8 @@ public String getNameOrAlias() { } /** - * @return {@code true} if this property is required (if argument is not present the {@link Commandlet} cannot be - * invoked), {@code false} otherwise (if optional). + * @return {@code true} if this property is required (if argument is not present the {@link Commandlet} cannot be invoked), {@code false} otherwise (if + * optional). */ public boolean isRequired() { @@ -106,8 +127,8 @@ public boolean isExpectValue() { } /** - * Determines if this {@link Property} is an option. Canonical options have a long-option {@link #getName() name} - * (e.g. "--force") and a short-option {@link #getAlias() alias} (e.g. "-f"). + * Determines if this {@link Property} is an option. Canonical options have a long-option {@link #getName() name} (e.g. "--force") and a short-option + * {@link #getAlias() alias} (e.g. "-f"). * * @return {@code true} if this {@link Property} is an option, {@code false} otherwise (if a positional argument). */ @@ -117,8 +138,8 @@ public boolean isOption() { } /** - * @return {@code true} if this {@link Property} forces an implicit {@link CliArgument#isEndOptions() end-options} as - * if "--" was provided before its first {@link CliArgument argument}. + * @return {@code true} if this {@link Property} forces an implicit {@link CliArgument#isEndOptions() end-options} as if "--" was provided before its first + * {@link CliArgument argument}. */ public boolean isEndOptions() { @@ -126,21 +147,20 @@ public boolean isEndOptions() { } /** - * Determines if this {@link Property} is multi-valued and accepts any number of values. A multi-valued - * {@link Property} needs to be the last {@link Property} of a {@link Commandlet}. + * Determines if this {@link Property} is multi-valued and accepts any number of values. A multi-valued {@link Property} needs to be the last {@link Property} + * of a {@link Commandlet}. * * @return {@code true} if multi-valued, {@code false} otherwise. */ public boolean isMultiValued() { - return false; + return multivalued; } /** - * Determines if this a value {@link Property}. Such value is either a {@link KeywordProperty} with the keyword as - * {@link #getName() name} or a raw indexed value argument. In the latter case the command-line argument at this index - * will be the immediate value of the {@link Property}, the {@link #getName() name} is {@link String#isEmpty() empty} - * and the {@link #getAlias() alias} is a logical name of the value to display to users. + * Determines if this a value {@link Property}. Such value is either a {@link KeywordProperty} with the keyword as {@link #getName() name} or a raw indexed + * value argument. In the latter case the command-line argument at this index will be the immediate value of the {@link Property}, the {@link #getName() name} + * is {@link String#isEmpty() empty} and the {@link #getAlias() alias} is a logical name of the value to display to users. * * @return {@code true} if value, {@code false} otherwise. */ @@ -160,7 +180,28 @@ public boolean isValue() { */ public V getValue() { - return this.value; + if (this.value.isEmpty()) { + return null; + } else { + return this.value.get(0); + } + } + + /** + * @param i the position to get. + * @return the value of this property. + */ + public V getValue(int i) { + + return this.value.get(i); + } + + /** + * @return amount of values. + */ + public int getValueCount() { + + return this.value.size(); } /** @@ -169,10 +210,10 @@ public V getValue() { */ public String getValueAsString() { - if (this.value == null) { + if (getValue() == null) { return null; } - return format(this.value); + return format(getValue()); } /** @@ -190,7 +231,27 @@ protected String format(V valueToFormat) { */ public void setValue(V value) { - this.value = value; + if (!this.multivalued) { + this.value.clear(); + } + this.value.add(value); + } + + public void addValue(V value) { + + if (!this.multivalued) { + throw new IllegalStateException("not multivalued"); + } + this.value.add(value); + } + + /** + * @param value the new {@link #getValue() value} to set. + * @param i the position to set. + */ + public void setValue(V value, int i) { + + this.value.set(i, value); } /** @@ -201,9 +262,9 @@ public void setValue(V value) { public void setValueAsString(String valueAsString, IdeContext context) { if (valueAsString == null) { - this.value = getNullValue(); + setValue(getNullValue()); } else { - this.value = parse(valueAsString, context); + setValue(parse(valueAsString, context)); } } @@ -246,15 +307,13 @@ protected V getNullValue() { public abstract V parse(String valueAsString, IdeContext context); /** - * @param args the {@link CliArguments} already {@link CliArguments#current() pointing} the {@link CliArgument} to - * apply. + * @param args the {@link CliArguments} already {@link CliArguments#current() pointing} the {@link CliArgument} to apply. * @param context the {@link IdeContext}. * @param commandlet the {@link Commandlet} owning this property. * @param collector the {@link CompletionCandidateCollector}. * @return {@code true} if it matches, {@code false} otherwise. */ - public boolean apply(CliArguments args, IdeContext context, Commandlet commandlet, - CompletionCandidateCollector collector) { + public boolean apply(CliArguments args, IdeContext context, Commandlet commandlet, CompletionCandidateCollector collector) { CliArgument argument = args.current(); if (argument.isCompletion()) { @@ -295,19 +354,26 @@ public boolean apply(CliArguments args, IdeContext context, Commandlet commandle /** * @param argValue the value to set as {@link String}. - * @param lookahead - {@code true} if the given {@code argValue} is taken as lookahead from the next value, - * {@code false} otherwise. + * @param lookahead - {@code true} if the given {@code argValue} is taken as lookahead from the next value, {@code false} otherwise. * @param args the {@link CliArguments}. * @param context the {@link IdeContext}. * @param commandlet the {@link Commandlet} owning this {@link Property}. * @param collector the {@link CompletionCandidateCollector}. * @return {@code true} if it matches, {@code false} otherwise. */ - protected boolean applyValue(String argValue, boolean lookahead, CliArguments args, IdeContext context, - Commandlet commandlet, CompletionCandidateCollector collector) { + protected boolean applyValue(String argValue, boolean lookahead, CliArguments args, IdeContext context, Commandlet commandlet, + CompletionCandidateCollector collector) { boolean success = assignValueAsString(argValue, context, commandlet); + if (success) { + if (multivalued) { + + while (success && args.hasNext()) { + CliArgument arg = args.next(); + success = assignValueAsString(arg.get(), context, commandlet); + } + } args.next(); } return success; @@ -322,8 +388,7 @@ protected boolean applyValue(String argValue, boolean lookahead, CliArguments ar * @param commandlet the {@link Commandlet} owning this {@link Property}. * @param collector the {@link CompletionCandidateCollector}. */ - protected void complete(CliArgument argument, CliArguments args, IdeContext context, Commandlet commandlet, - CompletionCandidateCollector collector) { + protected void complete(CliArgument argument, CliArguments args, IdeContext context, Commandlet commandlet, CompletionCandidateCollector collector) { String arg = argument.get(); if (this.name.isEmpty()) { @@ -351,7 +416,7 @@ protected void complete(CliArgument argument, CliArguments args, IdeContext cont if (value != null) { String key = argument.getKey(); if (this.name.equals(key) || Objects.equals(this.alias, key)) { - completeValue(value, context, commandlet, new CompletionCandidateCollectorAdapter(key+"=", collector)); + completeValue(value, context, commandlet, new CompletionCandidateCollectorAdapter(key + "=", collector)); } } } @@ -364,15 +429,13 @@ protected void complete(CliArgument argument, CliArguments args, IdeContext cont * @param commandlet the {@link Commandlet} owning this {@link Property}. * @param collector the {@link CompletionCandidateCollector}. */ - protected void completeValue(String arg, IdeContext context, Commandlet commandlet, - CompletionCandidateCollector collector) { + protected void completeValue(String arg, IdeContext context, Commandlet commandlet, CompletionCandidateCollector collector) { } /** * @param nameOrAlias the potential {@link #getName() name} or {@link #getAlias() alias} to match. - * @return {@code true} if the given {@code nameOrAlias} is equal to {@link #getName() name} or {@link #getAlias() - * alias}, {@code false} otherwise. + * @return {@code true} if the given {@code nameOrAlias} is equal to {@link #getName() name} or {@link #getAlias() alias}, {@code false} otherwise. */ public boolean matches(String nameOrAlias) { @@ -380,18 +443,18 @@ public boolean matches(String nameOrAlias) { } /** - * @return {@code true} if this {@link Property} is valid, {@code false} if it is {@link #isRequired() required} but - * no {@link #getValue() value} has been set. - * @throws RuntimeException if the {@link #getValue() value} is violating given constraints. This is checked by the - * optional {@link Consumer} function given at construction time. + * @return {@code true} if this {@link Property} is valid, {@code false} if it is {@link #isRequired() required} but no {@link #getValue() value} has been + * set. + * @throws RuntimeException if the {@link #getValue() value} is violating given constraints. This is checked by the optional {@link Consumer} function given + * at construction time. */ public boolean validate() { - if (this.required && (this.value == null)) { + if (this.required && (getValue() == null)) { return false; } if (this.validator != null) { - this.validator.accept(this.value); + this.validator.accept(getValue()); } return true; } diff --git a/cli/src/main/java/com/devonfw/tools/ide/property/StringListProperty.java b/cli/src/main/java/com/devonfw/tools/ide/property/StringListProperty.java deleted file mode 100644 index 1a393d077..000000000 --- a/cli/src/main/java/com/devonfw/tools/ide/property/StringListProperty.java +++ /dev/null @@ -1,107 +0,0 @@ -package com.devonfw.tools.ide.property; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Objects; -import java.util.function.Consumer; - -import com.devonfw.tools.ide.cli.CliArgument; -import com.devonfw.tools.ide.cli.CliArguments; -import com.devonfw.tools.ide.commandlet.Commandlet; -import com.devonfw.tools.ide.completion.CompletionCandidateCollector; -import com.devonfw.tools.ide.context.IdeContext; - -/** - * {@link Property} with {@link #getValueType() value type} {@link String}. - */ -public class StringListProperty extends Property> { - - private static final String[] NO_ARGS = new String[0]; - - /** - * The constructor. - * - * @param name the {@link #getName() property name}. - * @param required the {@link #isRequired() required flag}. - * @param alias the {@link #getAlias() property alias}. - */ - public StringListProperty(String name, boolean required, String alias) { - - this(name, required, alias, null); - } - - /** - * The constructor. - * - * @param name the {@link #getName() property name}. - * @param required the {@link #isRequired() required flag}. - * @param alias the {@link #getAlias() property alias}. - * @param validator the {@link Consumer} used to {@link #validate() validate} the {@link #getValue() value}. - */ - public StringListProperty(String name, boolean required, String alias, Consumer> validator) { - - super(name, required, alias, validator); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - @Override - public Class> getValueType() { - - return (Class) List.class; - } - - @Override - public boolean isMultiValued() { - - return true; - } - - @Override - public void setValueAsString(String valueAsString, IdeContext context) { - - Objects.requireNonNull(valueAsString); - // pragmatic solution this implementation does not set the list value to the given string - // instead it adds the given value to the list - List list = getValue(); - if (list == null) { - list = new ArrayList<>(); - setValue(list); - } - list.add(valueAsString); - } - - @Override - public List parse(String valueAsString, IdeContext context) { - - String[] items = valueAsString.split(" "); - return Arrays.asList(items); - } - - /** - * @return the {@link #getValue() value} as null-safe {@link String} array. - */ - public String[] asArray() { - - List list = getValue(); - if ((list == null) || list.isEmpty()) { - return NO_ARGS; - } - return list.toArray(new String[list.size()]); - } - - @Override - protected boolean applyValue(String argValue, boolean lookahead, CliArguments args, IdeContext context, - Commandlet commandlet, CompletionCandidateCollector collector) { - - this.value = new ArrayList<>(); - this.value.add(argValue); - while (args.hasNext()) { - CliArgument arg = args.next(); - this.value.add(arg.get()); - } - args.next(); - return true; - } - -} diff --git a/cli/src/main/java/com/devonfw/tools/ide/property/StringProperty.java b/cli/src/main/java/com/devonfw/tools/ide/property/StringProperty.java index 1ae5cff13..55a46c9dc 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/property/StringProperty.java +++ b/cli/src/main/java/com/devonfw/tools/ide/property/StringProperty.java @@ -1,9 +1,9 @@ package com.devonfw.tools.ide.property; -import java.util.function.Consumer; - import com.devonfw.tools.ide.context.IdeContext; +import java.util.function.Consumer; + /** * {@link Property} with {@link #getValueType() value type} {@link String}. */ @@ -18,7 +18,20 @@ public class StringProperty extends Property { */ public StringProperty(String name, boolean required, String alias) { - this(name, required, alias, null); + this(name, required, alias, false, null); + } + + /** + * The constructor. + * + * @param name the {@link #getName() property name}. + * @param required the {@link #isRequired() required flag}. + * @param alias the {@link #getAlias() property alias}. + * @param multivalued the boolean flag about multiple arguments. + */ + public StringProperty(String name, boolean required, boolean multivalued, String alias) { + + this(name, required, alias, multivalued, null); } /** @@ -28,10 +41,11 @@ public StringProperty(String name, boolean required, String alias) { * @param required the {@link #isRequired() required flag}. * @param alias the {@link #getAlias() property alias}. * @param validator the {@link Consumer} used to {@link #validate() validate} the {@link #getValue() value}. + * @param multivalued the boolean flag about multiple arguments */ - public StringProperty(String name, boolean required, String alias, Consumer validator) { + public StringProperty(String name, boolean required, String alias, boolean multivalued, Consumer validator) { - super(name, required, alias, validator); + super(name, required, alias, multivalued, validator); } @Override @@ -46,4 +60,12 @@ public String parse(String valueAsString, IdeContext context) { return valueAsString; } + /** + * @return the {@link #getValue() value} as null-safe {@link String} array. + */ + public String[] asArray() { + + return this.value.toArray(new String[this.value.size()]); + } + } diff --git a/cli/src/main/java/com/devonfw/tools/ide/property/ToolProperty.java b/cli/src/main/java/com/devonfw/tools/ide/property/ToolProperty.java index 6b3d71e5c..31e1c7233 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/property/ToolProperty.java +++ b/cli/src/main/java/com/devonfw/tools/ide/property/ToolProperty.java @@ -1,12 +1,12 @@ package com.devonfw.tools.ide.property; -import java.util.function.Consumer; - import com.devonfw.tools.ide.commandlet.Commandlet; import com.devonfw.tools.ide.completion.CompletionCandidateCollector; import com.devonfw.tools.ide.context.IdeContext; import com.devonfw.tools.ide.tool.ToolCommandlet; +import java.util.function.Consumer; + /** * {@link Property} with {@link #getValueType() value type} {@link ToolCommandlet}. */ @@ -21,7 +21,20 @@ public class ToolProperty extends Property { */ public ToolProperty(String name, boolean required, String alias) { - this(name, required, alias, null); + this(name, required, alias, false, null); + } + + /** + * The constructor. + * + * @param name the {@link #getName() property name}. + * @param required the {@link #isRequired() required flag}. + * @param multivalued the boolean flag about multiple arguments + * @param alias the {@link #getAlias() property alias}. + */ + public ToolProperty(String name, boolean required, boolean multivalued, String alias) { + + this(name, required, alias, multivalued, null); } /** @@ -30,11 +43,12 @@ public ToolProperty(String name, boolean required, String alias) { * @param name the {@link #getName() property name}. * @param required the {@link #isRequired() required flag}. * @param alias the {@link #getAlias() property alias}. + * @param multivalued the boolean flag about multiple arguments * @param validator the {@link Consumer} used to {@link #validate() validate} the {@link #getValue() value}. */ - public ToolProperty(String name, boolean required, String alias, Consumer validator) { + public ToolProperty(String name, boolean required, String alias, boolean multivalued, Consumer validator) { - super(name, required, alias, validator); + super(name, required, alias, multivalued, validator); } @Override @@ -56,8 +70,7 @@ public ToolCommandlet parse(String valueAsString, IdeContext context) { } @Override - protected void completeValue(String arg, IdeContext context, Commandlet commandlet, - CompletionCandidateCollector collector) { + protected void completeValue(String arg, IdeContext context, Commandlet commandlet, CompletionCandidateCollector collector) { for (Commandlet cmd : context.getCommandletManager().getCommandlets()) { if (cmd instanceof ToolCommandlet) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/property/VersionProperty.java b/cli/src/main/java/com/devonfw/tools/ide/property/VersionProperty.java index 72364ee31..7db0b535a 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/property/VersionProperty.java +++ b/cli/src/main/java/com/devonfw/tools/ide/property/VersionProperty.java @@ -1,10 +1,5 @@ package com.devonfw.tools.ide.property; -import java.util.Collections; -import java.util.List; -import java.util.function.Consumer; -import java.util.stream.IntStream; - import com.devonfw.tools.ide.commandlet.Commandlet; import com.devonfw.tools.ide.completion.CompletionCandidate; import com.devonfw.tools.ide.completion.CompletionCandidateCollector; @@ -13,6 +8,11 @@ import com.devonfw.tools.ide.version.VersionIdentifier; import com.devonfw.tools.ide.version.VersionSegment; +import java.util.Collections; +import java.util.List; +import java.util.function.Consumer; +import java.util.stream.IntStream; + /** * {@link Property} for {@link VersionIdentifier} as {@link #getValueType() value type}. */ @@ -40,7 +40,7 @@ public VersionProperty(String name, boolean required, String alias) { */ public VersionProperty(String name, boolean required, String alias, Consumer validator) { - super(name, required, alias, validator); + super(name, required, alias, false, validator); } @Override @@ -56,15 +56,16 @@ public VersionIdentifier parse(String valueAsString, IdeContext context) { } @Override - protected void completeValue(String arg, IdeContext context, Commandlet commandlet, - CompletionCandidateCollector collector) { + protected void completeValue(String arg, IdeContext context, Commandlet commandlet, CompletionCandidateCollector collector) { ToolCommandlet tool = commandlet.getToolForVersionCompletion(); if (tool != null) { completeVersion(VersionIdentifier.of(arg), tool, context, commandlet, collector); } } - private void completeVersion(VersionIdentifier version2complete, ToolCommandlet tool, IdeContext context, Commandlet commandlet, CompletionCandidateCollector collector) { + + private void completeVersion(VersionIdentifier version2complete, ToolCommandlet tool, IdeContext context, Commandlet commandlet, + CompletionCandidateCollector collector) { collector.disableSorting(); if (tool != null) { @@ -80,13 +81,12 @@ private void completeVersion(VersionIdentifier version2complete, ToolCommandlet } List versions = context.getUrls().getSortedVersions(tool.getName(), tool.getEdition()); int size = versions.size(); - String[] sortedCandidates = IntStream.rangeClosed(1, size).mapToObj(i -> versions.get(size - i).toString()) - .toArray(String[]::new); + String[] sortedCandidates = IntStream.rangeClosed(1, size).mapToObj(i -> versions.get(size - i).toString()).toArray(String[]::new); collector.addAllMatches(text, sortedCandidates, this, commandlet); List candidates = collector.getCandidates(); Collections.reverse(candidates); - CompletionCandidate latest = collector.createCandidate(text + VersionSegment.PATTERN_MATCH_ANY_STABLE_VERSION, - "Latest stable matching version", this, commandlet); + CompletionCandidate latest = collector.createCandidate(text + VersionSegment.PATTERN_MATCH_ANY_STABLE_VERSION, "Latest stable matching version", this, + commandlet); if (candidates.isEmpty()) { candidates.add(latest); } else { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/GlobalToolCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/tool/GlobalToolCommandlet.java index f433093df..0787bd2bb 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/GlobalToolCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/GlobalToolCommandlet.java @@ -25,8 +25,7 @@ public abstract class GlobalToolCommandlet extends ToolCommandlet { * * @param context the {@link IdeContext}. * @param tool the {@link #getName() tool name}. - * @param tags the {@link #getTags() tags} classifying the tool. Should be created via {@link Set#of(Object) Set.of} - * method. + * @param tags the {@link #getTags() tags} classifying the tool. Should be created via {@link Set#of(Object) Set.of} method. */ public GlobalToolCommandlet(IdeContext context, String tool, Set tags) { diff --git a/cli/src/main/java/com/devonfw/tools/ide/tool/ToolCommandlet.java b/cli/src/main/java/com/devonfw/tools/ide/tool/ToolCommandlet.java index 9260188d0..8a4916054 100644 --- a/cli/src/main/java/com/devonfw/tools/ide/tool/ToolCommandlet.java +++ b/cli/src/main/java/com/devonfw/tools/ide/tool/ToolCommandlet.java @@ -10,7 +10,7 @@ import com.devonfw.tools.ide.process.ProcessContext; import com.devonfw.tools.ide.process.ProcessErrorHandling; import com.devonfw.tools.ide.process.ProcessMode; -import com.devonfw.tools.ide.property.StringListProperty; +import com.devonfw.tools.ide.property.StringProperty; import com.devonfw.tools.ide.version.VersionIdentifier; import java.io.IOException; @@ -30,7 +30,7 @@ public abstract class ToolCommandlet extends Commandlet implements Tags { private final Set tags; /** The commandline arguments to pass to the tool. */ - public final StringListProperty arguments; + public final StringProperty arguments; private MacOsHelper macOsHelper; @@ -47,7 +47,7 @@ public ToolCommandlet(IdeContext context, String tool, Set tags) { this.tool = tool; this.tags = tags; addKeyword(tool); - this.arguments = new StringListProperty("", false, "args"); + this.arguments = new StringProperty("", false, true, "args"); initProperties(); } diff --git a/cli/src/test/java/com/devonfw/tools/ide/commandlet/UninstallCommandletTest.java b/cli/src/test/java/com/devonfw/tools/ide/commandlet/UninstallCommandletTest.java index bd86e2b0c..1aeece501 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/commandlet/UninstallCommandletTest.java +++ b/cli/src/test/java/com/devonfw/tools/ide/commandlet/UninstallCommandletTest.java @@ -1,18 +1,23 @@ package com.devonfw.tools.ide.commandlet; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; +import com.devonfw.tools.ide.context.AbstractIdeContextTest; +import com.devonfw.tools.ide.context.IdeTestContext; +import com.devonfw.tools.ide.io.FileAccessImpl; +import com.devonfw.tools.ide.log.IdeLogLevel; +import com.devonfw.tools.ide.property.ToolProperty; +import com.devonfw.tools.ide.tool.dotnet.DotNet; +import com.devonfw.tools.ide.tool.eclipse.Eclipse; +import com.devonfw.tools.ide.tool.npm.Npm; +import org.junit.jupiter.api.Test; import java.nio.file.Files; import java.nio.file.Path; -import org.junit.jupiter.api.Test; - -import com.devonfw.tools.ide.context.AbstractIdeContextTest; -import com.devonfw.tools.ide.context.IdeTestContext; -import com.devonfw.tools.ide.io.FileAccess; -import com.devonfw.tools.ide.log.IdeLogLevel; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; /** * Integration test of {@link UninstallCommandlet}. @@ -21,57 +26,77 @@ public class UninstallCommandletTest extends AbstractIdeContextTest { /** * Test of {@link UninstallCommandlet} run. - * */ @Test public void testUninstallCommandletRun_WithExistingCommandlet() { // arrange - String toolName = "npm"; + String npm = "npm"; + String dotnet = "dotnet"; IdeTestContext context = newContext(PROJECT_BASIC); - UninstallCommandlet uninstallCommandlet = context.getCommandletManager().getCommandlet(UninstallCommandlet.class); - uninstallCommandlet.tool.setValueAsString(toolName, context); + CommandletManager commandletManager = getCommandletManager(context); + UninstallCommandlet uninstallCommandlet = commandletManager.getCommandlet(UninstallCommandlet.class); + Npm npmCommandlet = commandletManager.getCommandlet(Npm.class); + DotNet dotnetCommandlet = commandletManager.getCommandlet(DotNet.class); + + ToolProperty tools = uninstallCommandlet.tools; + tools.addValue(npmCommandlet); + tools.addValue(dotnetCommandlet); + // act uninstallCommandlet.run(); // assert - assertLogMessage(context, IdeLogLevel.SUCCESS, "Successfully uninstalled " + toolName); - assertThat(Files.notExists(context.getSoftwarePath().resolve(toolName))); + assertLogMessage(context, IdeLogLevel.SUCCESS, "Successfully uninstalled " + npm); + assertLogMessage(context, IdeLogLevel.WARNING, "An installed version of " + dotnet + " does not exist"); + assertThat(Files.notExists(context.getSoftwarePath().resolve(npm))); } @Test public void testUninstallCommandletRun_WithNonExistingCommandlet() { // arrange - String toolName = "eclipse"; + String eclipse = "eclipse"; IdeTestContext context = newContext(PROJECT_BASIC); - UninstallCommandlet uninstallCommandlet = context.getCommandletManager().getCommandlet(UninstallCommandlet.class); - uninstallCommandlet.tool.setValueAsString(toolName, context); + CommandletManager commandletManager = getCommandletManager(context); + UninstallCommandlet uninstallCommandlet = commandletManager.getCommandlet(UninstallCommandlet.class); + Eclipse eclipseCommandlet = commandletManager.getCommandlet(Eclipse.class); + uninstallCommandlet.tools.addValue(eclipseCommandlet); // act uninstallCommandlet.run(); // assert - assertLogMessage(context, IdeLogLevel.INFO, "An installed version of " + toolName + " does not exist"); - assertThat(Files.notExists(context.getSoftwarePath().resolve(toolName))); + assertLogMessage(context, IdeLogLevel.WARNING, "An installed version of " + eclipse + " does not exist"); + assertThat(Files.notExists(context.getSoftwarePath().resolve(eclipse))); } @Test public void testUninstallCommandletRun_ThrowsException() { // arrange - String toolName = "npm"; + FileAccessImpl mockFileAccess = mock(FileAccessImpl.class); + IdeTestContext mockContext = mock(IdeTestContext.class); IdeTestContext context = newContext(PROJECT_BASIC); + Path softwarePath = context.getSoftwarePath(); + + when(mockContext.getFileAccess()).thenReturn(mockFileAccess); - FileAccess mockFileAccess = mock(FileAccess.class); - doThrow(new IllegalStateException()).when(mockFileAccess).delete(any(Path.class)); - context.setMockFileAccess(mockFileAccess); + when(mockContext.getCommandletManager()).thenReturn(new CommandletManagerImpl(mockContext)); + when(mockContext.getSoftwarePath()).thenReturn(softwarePath); + + doThrow(new IllegalStateException("Couldn't uninstall")).when(mockFileAccess).delete(any()); + CommandletManager commandletManager = getCommandletManager(mockContext); + UninstallCommandlet uninstallCommandlet = commandletManager.getCommandlet(UninstallCommandlet.class); + Npm npmCommandlet = commandletManager.getCommandlet(Npm.class); + uninstallCommandlet.tools.addValue(npmCommandlet); - UninstallCommandlet uninstallCommandlet = context.getCommandletManager().getCommandlet(UninstallCommandlet.class); - uninstallCommandlet.tool.setValueAsString(toolName, context); // act - try { - uninstallCommandlet.run(); - } catch (IllegalStateException e) { - // assert - assertThat(e).hasMessageContaining("Couldn't uninstall " + toolName); - } + uninstallCommandlet.run(); + + //assert + verify(mockContext).error("Couldn't uninstall npm"); + } + + private CommandletManager getCommandletManager(IdeTestContext context) { + + return context.getCommandletManager(); } -} +} \ No newline at end of file diff --git a/cli/src/test/java/com/devonfw/tools/ide/tool/jmc/JmcTest.java b/cli/src/test/java/com/devonfw/tools/ide/tool/jmc/JmcTest.java index 5de3c5b93..58cf5df13 100644 --- a/cli/src/test/java/com/devonfw/tools/ide/tool/jmc/JmcTest.java +++ b/cli/src/test/java/com/devonfw/tools/ide/tool/jmc/JmcTest.java @@ -1,18 +1,16 @@ package com.devonfw.tools.ide.tool.jmc; -import java.io.IOException; -import java.util.List; - -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.ValueSource; - import com.devonfw.tools.ide.commandlet.InstallCommandlet; import com.devonfw.tools.ide.context.AbstractIdeContextTest; import com.devonfw.tools.ide.context.IdeTestContext; import com.devonfw.tools.ide.log.IdeLogLevel; import com.devonfw.tools.ide.os.SystemInfo; import com.devonfw.tools.ide.os.SystemInfoMock; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.io.IOException; /** * Integration test of {@link Jmc}. @@ -59,7 +57,9 @@ public void testJmcRun(String os) { SystemInfo systemInfo = SystemInfoMock.of(os); context.setSystemInfo(systemInfo); Jmc commandlet = new Jmc(context); - commandlet.arguments.setValue(List.of("foo", "bar")); + + commandlet.arguments.addValue("foo"); + commandlet.arguments.addValue("bar"); // act commandlet.run();