From 9cb519c26f04577a3336651b9a591c35b2aebd41 Mon Sep 17 00:00:00 2001
From: DarviL82
Date: Mon, 25 Sep 2023 21:19:52 +0200
Subject: [PATCH 01/15] fix: slightly optimized tokenizer
---
src/main/java/lanat/parsing/Parser.java | 6 +++++-
src/main/java/lanat/parsing/Tokenizer.java | 8 +++++++-
2 files changed, 12 insertions(+), 2 deletions(-)
diff --git a/src/main/java/lanat/parsing/Parser.java b/src/main/java/lanat/parsing/Parser.java
index d7a0a7f8..ea05bc96 100644
--- a/src/main/java/lanat/parsing/Parser.java
+++ b/src/main/java/lanat/parsing/Parser.java
@@ -81,8 +81,12 @@ public void setTokens(@NotNull List<@NotNull Token> tokens) {
this.tokens = tokens;
}
+ /**
+ * Parses the tokens that have been set. Delegates parsing of argument values to the {@link ArgumentType} of the
+ * argument that is being parsed.
+ */
public void parseTokens() {
- assert this.tokens != null : "Tokens have not been set yet";
+ assert this.tokens != null : "Tokens have not been set yet.";
assert !this.hasFinished : "This parser has already finished parsing.";
// number of positional arguments that have been parsed.
diff --git a/src/main/java/lanat/parsing/Tokenizer.java b/src/main/java/lanat/parsing/Tokenizer.java
index 0e4726e8..0397ac40 100644
--- a/src/main/java/lanat/parsing/Tokenizer.java
+++ b/src/main/java/lanat/parsing/Tokenizer.java
@@ -54,10 +54,16 @@ private void setInputString(@NotNull String inputString) {
* {@link Tokenizer#getFinalTokens()}
*/
public void tokenize(@NotNull String input) {
- assert !this.hasFinished : "Tokenizer has already finished tokenizing";
+ assert !this.hasFinished : "Tokenizer has already finished tokenizing.";
this.setInputString(input);
+ // nothing to tokenize. Just finish
+ if (input.isEmpty()) {
+ this.hasFinished = true;
+ return;
+ }
+
char currentStringChar = 0; // the character that opened the string
TokenizeError.TokenizeErrorType errorType = null;
From 0459e940ff8c1f57b6d3e283a6dbf015bacdf26c Mon Sep 17 00:00:00 2001
From: DarviL82
Date: Mon, 25 Sep 2023 21:38:58 +0200
Subject: [PATCH 02/15] fix: slight changes to access modifiers
---
src/main/java/lanat/ArgumentParser.java | 2 +-
src/main/java/lanat/Command.java | 6 +++---
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/main/java/lanat/ArgumentParser.java b/src/main/java/lanat/ArgumentParser.java
index 14d0368c..21d4073f 100644
--- a/src/main/java/lanat/ArgumentParser.java
+++ b/src/main/java/lanat/ArgumentParser.java
@@ -191,7 +191,7 @@ public static ArgumentParser from(@NotNull Class extends CommandTemplate> temp
return new ParsedArgumentsRoot(
this,
this.getParser().getParsedArgumentsHashMap(),
- this.subCommands.stream().map(Command::getParsedArguments).toList(),
+ this.getCommands().stream().map(Command::getParsedArguments).toList(),
this.getForwardValue()
);
}
diff --git a/src/main/java/lanat/Command.java b/src/main/java/lanat/Command.java
index da9611e4..c6665ad9 100644
--- a/src/main/java/lanat/Command.java
+++ b/src/main/java/lanat/Command.java
@@ -43,10 +43,10 @@ public class Command
{
private final @NotNull List<@NotNull String> names = new ArrayList<>();
private @Nullable String description;
- final @NotNull ArrayList<@NotNull Argument, ?>> arguments = new ArrayList<>();
- final @NotNull ArrayList<@NotNull Command> subCommands = new ArrayList<>();
+ private final @NotNull ArrayList<@NotNull Argument, ?>> arguments = new ArrayList<>();
+ private final @NotNull ArrayList<@NotNull Command> subCommands = new ArrayList<>();
private Command parentCommand;
- final @NotNull ArrayList<@NotNull ArgumentGroup> argumentGroups = new ArrayList<>();
+ private final @NotNull ArrayList<@NotNull ArgumentGroup> argumentGroups = new ArrayList<>();
private final @NotNull ModifyRecord<@NotNull TupleCharacter> tupleChars = ModifyRecord.of(TupleCharacter.SQUARE_BRACKETS);
private final @NotNull ModifyRecord<@NotNull Integer> errorCode = ModifyRecord.of(1);
From 9b24df3a8f78e77a5e50930674e9f5509b678c69 Mon Sep 17 00:00:00 2001
From: David L <48654552+DarviL82@users.noreply.github.com>
Date: Tue, 26 Sep 2023 13:28:54 +0200
Subject: [PATCH 03/15] Update README.md
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 1d470a1a..1a83a354 100644
--- a/README.md
+++ b/README.md
@@ -82,7 +82,7 @@ The package is currently only available on GitHub Packages.
implementation("darvil:lanat")
```
- Note that you may need to explicitly specify the version of the package you want to use. (e.g. `darvil:lanat:0.0.1`)
+ Note that you may need to explicitly specify the version of the package you want to use. (e.g. `darvil:lanat:x.x.x`)
This information is available at the [GitHub Packages documentation](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-gradle-registry#using-a-published-package).
From d1b688f2b47381322905088bfb8f5fbdd94fd65b Mon Sep 17 00:00:00 2001
From: DarviL82
Date: Fri, 6 Oct 2023 20:39:13 +0200
Subject: [PATCH 04/15] fix: slighty optimized .equals methods
---
src/main/java/lanat/Argument.java | 1 +
src/main/java/lanat/ArgumentGroup.java | 5 +++--
src/main/java/lanat/Command.java | 1 +
3 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/src/main/java/lanat/Argument.java b/src/main/java/lanat/Argument.java
index ded72661..a01876c0 100644
--- a/src/main/java/lanat/Argument.java
+++ b/src/main/java/lanat/Argument.java
@@ -597,6 +597,7 @@ void invokeCallbacks(@Nullable Object okValue) {
*/
@Override
public boolean equals(@NotNull Object obj) {
+ if (obj == this) return true;
if (obj instanceof Argument, ?> arg)
return UtlMisc.equalsByNamesAndParentCmd(this, arg);
return false;
diff --git a/src/main/java/lanat/ArgumentGroup.java b/src/main/java/lanat/ArgumentGroup.java
index fcc230a8..9053157e 100644
--- a/src/main/java/lanat/ArgumentGroup.java
+++ b/src/main/java/lanat/ArgumentGroup.java
@@ -75,8 +75,8 @@ public class ArgumentGroup
* each one added to this group is because at parsing, we might need to know which arguments were used in this
* group.
*
- * Sure, we could just use {@link Command#arguments}, but that would mean that we would have to iterate through all
- * the arguments in there for filtering ours, which is probably worse.
+ * Sure, we could just use {@link Command#getArguments()}, but that would mean that we would have
+ * to iterate through all the arguments in there for filtering ours, which is probably worse.
*/
private final @NotNull List<@NotNull Argument, ?>> arguments = new ArrayList<>();
@@ -274,6 +274,7 @@ public ArgumentGroup getParent() {
@Override
public boolean equals(@NotNull Object obj) {
+ if (obj == this) return true;
if (obj instanceof ArgumentGroup group)
return this.parentCommand == group.parentCommand && this.name.equals(group.name);
return false;
diff --git a/src/main/java/lanat/Command.java b/src/main/java/lanat/Command.java
index c6665ad9..6c752dcf 100644
--- a/src/main/java/lanat/Command.java
+++ b/src/main/java/lanat/Command.java
@@ -412,6 +412,7 @@ void passPropertiesToChildren() {
*/
@Override
public boolean equals(@NotNull Object obj) {
+ if (obj == this) return true;
if (obj instanceof Command cmd)
return UtlMisc.equalsByNamesAndParentCmd(this, cmd);
return false;
From a60268be003e90698641ff7629d25312f1517404 Mon Sep 17 00:00:00 2001
From: DarviL82
Date: Tue, 17 Oct 2023 21:27:45 +0200
Subject: [PATCH 05/15] update version number to 0.0.3 feat: add `needsToExist`
option to FileArgumentType docs: add more javadocs fix: FileArgumentType not
behaving as expected. fix: incorrect tests for FileArgumentType
---
build.gradle.kts | 2 +-
src/main/java/lanat/ArgumentType.java | 2 +-
.../lanat/argumentTypes/EnumArgumentType.java | 4 ++++
.../lanat/argumentTypes/FileArgumentType.java | 22 ++++++++++++++-----
.../NumberRangeArgumentType.java | 5 +++++
src/test/java/lanat/test/TestingParser.java | 2 +-
.../lanat/test/units/TestArgumentTypes.java | 5 ++---
7 files changed, 30 insertions(+), 12 deletions(-)
diff --git a/build.gradle.kts b/build.gradle.kts
index b631a142..15dcd597 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -4,7 +4,7 @@ plugins {
}
group = "darvil"
-version = "0.0.2"
+version = "0.0.3"
description = "Command line argument parser"
dependencies {
diff --git a/src/main/java/lanat/ArgumentType.java b/src/main/java/lanat/ArgumentType.java
index 2f05f4d6..c5bfcb04 100644
--- a/src/main/java/lanat/ArgumentType.java
+++ b/src/main/java/lanat/ArgumentType.java
@@ -92,7 +92,7 @@ public ArgumentType(@NotNull T initialValue) {
}
/**
- * Constructs a new argument type with no initial value.
+ * Constructs a new argument type.
*/
public ArgumentType() {
if (this.getRequiredUsageCount().start() == 0) {
diff --git a/src/main/java/lanat/argumentTypes/EnumArgumentType.java b/src/main/java/lanat/argumentTypes/EnumArgumentType.java
index 31e6d793..cb90d941 100644
--- a/src/main/java/lanat/argumentTypes/EnumArgumentType.java
+++ b/src/main/java/lanat/argumentTypes/EnumArgumentType.java
@@ -20,6 +20,10 @@
public class EnumArgumentType> extends ArgumentType {
private final @NotNull T @NotNull [] values;
+ /**
+ * Creates a new enum argument type.
+ * @param defaultValue The default value of the enum type. This is also used to infer the type of the enum.
+ */
public EnumArgumentType(@NotNull T defaultValue) {
super(defaultValue);
this.values = defaultValue.getDeclaringClass().getEnumConstants();
diff --git a/src/main/java/lanat/argumentTypes/FileArgumentType.java b/src/main/java/lanat/argumentTypes/FileArgumentType.java
index afcab7c2..28179b8a 100644
--- a/src/main/java/lanat/argumentTypes/FileArgumentType.java
+++ b/src/main/java/lanat/argumentTypes/FileArgumentType.java
@@ -7,18 +7,28 @@
import java.io.File;
/**
- * An argument type that takes a file path, and returns a {@link File} instance.
- * If the file could not be found, an error is added.
+ * An argument type that takes a file path and returns a {@link File} instance representing it.
*/
public class FileArgumentType extends ArgumentType {
+ private final boolean needsToExist;
+
+ /**
+ * Creates a new file argument type.
+ * @param needsToExist whether the file needs to exist or not. If set to false, the file instance will be created
+ * even it doesn't exist.
+ */
+ public FileArgumentType(boolean needsToExist) {
+ this.needsToExist = needsToExist;
+ }
+
@Override
public File parseValues(@NotNull String @NotNull [] args) {
- try {
- return new File(args[0]);
- } catch (Exception e) {
- this.addError("File not found: '" + args[0] + "'.");
+ File file = new File(args[0]);
+ if (this.needsToExist && !file.exists()) {
+ this.addError("File does not exist.");
return null;
}
+ return file;
}
@Override
diff --git a/src/main/java/lanat/argumentTypes/NumberRangeArgumentType.java b/src/main/java/lanat/argumentTypes/NumberRangeArgumentType.java
index 78cca4ee..0cac8054 100644
--- a/src/main/java/lanat/argumentTypes/NumberRangeArgumentType.java
+++ b/src/main/java/lanat/argumentTypes/NumberRangeArgumentType.java
@@ -17,6 +17,11 @@ public class NumberRangeArgumentType> extends A
private final ArgumentType argumentType;
private final T min, max;
+ /**
+ * Creates a new number range argument type.
+ * @param min The minimum value.
+ * @param max The maximum value.
+ */
@SuppressWarnings("unchecked")
public NumberRangeArgumentType(@NotNull T min, @NotNull T max) {
if (min.compareTo(max) > 0) {
diff --git a/src/test/java/lanat/test/TestingParser.java b/src/test/java/lanat/test/TestingParser.java
index c0f70263..eb5a3cfb 100644
--- a/src/test/java/lanat/test/TestingParser.java
+++ b/src/test/java/lanat/test/TestingParser.java
@@ -28,7 +28,7 @@ public List parseGetErrors(String args) {
}
public @NotNull ParsedArgumentsRoot parseGetValues(@NotNull String args) {
- var res = this.parse(CLInput.from(args)).getParsedArguments();
+ var res = this.parse(CLInput.from(args)).printErrors().getParsedArguments();
assertNotNull(res, "The result of the parsing was null (Arguments have failed)");
return res;
}
diff --git a/src/test/java/lanat/test/units/TestArgumentTypes.java b/src/test/java/lanat/test/units/TestArgumentTypes.java
index 5846d49f..7b4a1873 100644
--- a/src/test/java/lanat/test/units/TestArgumentTypes.java
+++ b/src/test/java/lanat/test/units/TestArgumentTypes.java
@@ -25,7 +25,7 @@ protected TestingParser setParser() {
this.addArgument(Argument.create(new FloatArgumentType(), "float"));
this.addArgument(Argument.create(new StringArgumentType(), "string"));
this.addArgument(Argument.create(new MultipleStringsArgumentType(), "multiple-strings"));
- this.addArgument(Argument.create(new FileArgumentType(), "file"));
+ this.addArgument(Argument.create(new FileArgumentType(true), "file"));
this.addArgument(Argument.create(new EnumArgumentType<>(TestEnum.TWO), "enum"));
this.addArgument(Argument.create(new KeyValuesArgumentType<>(new IntegerArgumentType()), "key-value"));
this.addArgument(Argument.create(new NumberRangeArgumentType<>(3, 10), "int-range"));
@@ -77,8 +77,7 @@ public void testStrings() {
@Test
public void testFile() {
- assertEquals("hello.txt", this.parseArg("file", "hello.txt").getName());
- this.assertNotPresent("file");
+ assertNull(this.parseArg("file", "hello.txt"));
}
@Test
From 8194d6a25ab50e94a1a45ef8e16d055d3d0c93cd Mon Sep 17 00:00:00 2001
From: DarviL82
Date: Wed, 18 Oct 2023 03:04:09 +0200
Subject: [PATCH 06/15] feat: add File and Strin[] to the types that can be
inferred. feat: registering argument types for inferring now requires
specifying a supplier lambda instead of just the class.
---
src/main/java/lanat/ArgumentBuilder.java | 7 +--
src/main/java/lanat/ArgumentType.java | 39 --------------
src/main/java/lanat/ArgumentTypeInfer.java | 52 +++++++++++++++++++
src/main/java/lanat/CommandTemplate.java | 2 +-
.../NumberRangeArgumentType.java | 6 +--
5 files changed, 58 insertions(+), 48 deletions(-)
create mode 100644 src/main/java/lanat/ArgumentTypeInfer.java
diff --git a/src/main/java/lanat/ArgumentBuilder.java b/src/main/java/lanat/ArgumentBuilder.java
index 09ee1f15..a2839410 100644
--- a/src/main/java/lanat/ArgumentBuilder.java
+++ b/src/main/java/lanat/ArgumentBuilder.java
@@ -47,11 +47,8 @@ public class ArgumentBuilder, TInner> {
if (annotation.argType() != DummyArgumentType.class)
return UtlReflection.instantiate(annotation.argType());
- // try to infer the type from the field type
- var argTypeMap = ArgumentType.getTypeInfer(field.getType());
-
- // if the type was not found, return null
- return argTypeMap == null ? null : UtlReflection.instantiate(argTypeMap);
+ // try to infer the type from the field type. If it can't be inferred, return null
+ return ArgumentTypeInfer.get(field.getType());
}
/**
diff --git a/src/main/java/lanat/ArgumentType.java b/src/main/java/lanat/ArgumentType.java
index c5bfcb04..49e9c910 100644
--- a/src/main/java/lanat/ArgumentType.java
+++ b/src/main/java/lanat/ArgumentType.java
@@ -9,7 +9,6 @@
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.function.Consumer;
/**
@@ -78,9 +77,6 @@ public abstract class ArgumentType
private @Nullable ArgumentType> parentArgType;
private final @NotNull ArrayList<@NotNull ArgumentType>> subTypes = new ArrayList<>();
- /** Mapping of types to their corresponding argument types. Used for inferring. */
- private static final HashMap, Class extends ArgumentType>>> INFER_ARGUMENT_TYPES_MAP = new HashMap<>();
-
/**
* Constructs a new argument type with the specified initial value.
@@ -298,39 +294,4 @@ public void resetState() {
public @Nullable ArgumentType> getParent() {
return this.parentArgType;
}
-
-
- /**
- * Registers an argument type to be inferred for the specified type/s.
- * @param type The argument type to infer.
- * @param infer The types to infer the argument type for.
- */
- public static void registerTypeInfer(@NotNull Class extends ArgumentType>> type, @NotNull Class>... infer) {
- for (Class> clazz : infer) {
- ArgumentType.INFER_ARGUMENT_TYPES_MAP.put(clazz, type);
- }
- }
-
- /**
- * Returns the argument type that should be inferred for the specified type.
- * @param clazz The type to infer the argument type for.
- * @return The argument type that should be inferred for the specified type. Returns {@code null} if no
- * valid argument type was found.
- */
- public static Class extends ArgumentType>> getTypeInfer(@NotNull Class> clazz) {
- return ArgumentType.INFER_ARGUMENT_TYPES_MAP.get(clazz);
- }
-
- // add some default argument types.
- static {
- // we need to also specify the primitives... wish there was a better way to do this.
- ArgumentType.registerTypeInfer(StringArgumentType.class, String.class);
- ArgumentType.registerTypeInfer(IntegerArgumentType.class, int.class, Integer.class);
- ArgumentType.registerTypeInfer(BooleanArgumentType.class, boolean.class, Boolean.class);
- ArgumentType.registerTypeInfer(FloatArgumentType.class, float.class, Float.class);
- ArgumentType.registerTypeInfer(DoubleArgumentType.class, double.class, Double.class);
- ArgumentType.registerTypeInfer(LongArgumentType.class, long.class, Long.class);
- ArgumentType.registerTypeInfer(ShortArgumentType.class, short.class, Short.class);
- ArgumentType.registerTypeInfer(ByteArgumentType.class, byte.class, Byte.class);
- }
}
\ No newline at end of file
diff --git a/src/main/java/lanat/ArgumentTypeInfer.java b/src/main/java/lanat/ArgumentTypeInfer.java
new file mode 100644
index 00000000..40f207fd
--- /dev/null
+++ b/src/main/java/lanat/ArgumentTypeInfer.java
@@ -0,0 +1,52 @@
+package lanat;
+
+import lanat.argumentTypes.*;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.File;
+import java.util.HashMap;
+import java.util.function.Supplier;
+
+public class ArgumentTypeInfer {
+ /**
+ * Mapping of types to their corresponding argument types. Used for inferring.
+ * Argument types are stored as suppliers so that they can be instantiated when needed.
+ * */
+ private static final HashMap, Supplier extends ArgumentType>>> INFER_ARGUMENT_TYPES_MAP = new HashMap<>();
+
+ /**
+ * Registers an argument type to be inferred for the specified type/s.
+ * @param type The argument type to infer.
+ * @param infer The types to infer the argument type for.
+ */
+ public static void register(@NotNull Supplier extends ArgumentType>> type, @NotNull Class>... infer) {
+ for (Class> clazz : infer) {
+ ArgumentTypeInfer.INFER_ARGUMENT_TYPES_MAP.put(clazz, type);
+ }
+ }
+
+ /**
+ * Returns the argument type that should be inferred for the specified type.
+ * @param clazz The type to infer the argument type for.
+ * @return The argument type that should be inferred for the specified type. Returns {@code null} if no
+ * valid argument type was found.
+ */
+ public static ArgumentType> get(@NotNull Class> clazz) {
+ return ArgumentTypeInfer.INFER_ARGUMENT_TYPES_MAP.get(clazz).get();
+ }
+
+ // add some default argument types.
+ static {
+ // we need to also specify the primitives... wish there was a better way to do this.
+ register(StringArgumentType::new, String.class);
+ register(MultipleStringsArgumentType::new, String[].class);
+ register(IntegerArgumentType::new, int.class, Integer.class);
+ register(BooleanArgumentType::new, boolean.class, Boolean.class);
+ register(FloatArgumentType::new, float.class, Float.class);
+ register(DoubleArgumentType::new, double.class, Double.class);
+ register(LongArgumentType::new, long.class, Long.class);
+ register(ShortArgumentType::new, short.class, Short.class);
+ register(ByteArgumentType::new, byte.class, Byte.class);
+ register(() -> new FileArgumentType(false), File.class);
+ }
+}
diff --git a/src/main/java/lanat/CommandTemplate.java b/src/main/java/lanat/CommandTemplate.java
index 52f18967..2a79e62e 100644
--- a/src/main/java/lanat/CommandTemplate.java
+++ b/src/main/java/lanat/CommandTemplate.java
@@ -51,7 +51,7 @@
*
* If no Argument Type is specified on the annotation, the Argument Type will be attempted to be inferred from the
* field type if possible, which is the case for some built-in types, such as
- * {@link String}, {@link Integer}, {@link Double}, etc.
+ * {@link String}, {@link Integer}, {@link java.io.File}, etc.
*
*
* Example:
diff --git a/src/main/java/lanat/argumentTypes/NumberRangeArgumentType.java b/src/main/java/lanat/argumentTypes/NumberRangeArgumentType.java
index 0cac8054..907395a9 100644
--- a/src/main/java/lanat/argumentTypes/NumberRangeArgumentType.java
+++ b/src/main/java/lanat/argumentTypes/NumberRangeArgumentType.java
@@ -1,7 +1,7 @@
package lanat.argumentTypes;
import lanat.ArgumentType;
-import lanat.utils.UtlReflection;
+import lanat.ArgumentTypeInfer;
import lanat.utils.displayFormatter.Color;
import lanat.utils.displayFormatter.TextFormatter;
import org.jetbrains.annotations.NotNull;
@@ -28,13 +28,13 @@ public NumberRangeArgumentType(@NotNull T min, @NotNull T max) {
throw new IllegalArgumentException("min must be less than or equal to max");
}
- final var typeInferred = ArgumentType.getTypeInfer(min.getClass());
+ final var typeInferred = ArgumentTypeInfer.get(min.getClass());
if (typeInferred == null) {
throw new IllegalArgumentException("Unsupported type: " + min.getClass().getName());
}
- this.argumentType = (ArgumentType)UtlReflection.instantiate(typeInferred);
+ this.argumentType = (ArgumentType)typeInferred;
this.registerSubType(this.argumentType);
this.min = min;
From 42083e3fe02a7553bef68950290f0715f24e52b5 Mon Sep 17 00:00:00 2001
From: DarviL82
Date: Wed, 18 Oct 2023 14:59:14 +0200
Subject: [PATCH 07/15] feat: replace TupleArgumentType functionality so that
now it is basically a mapper of strings to what a subtype decides to parse
feat: add MultipleNumbersArgumentType feat: refactor
MultipleStringsArgumentType to inherit new TupleArgumentType feat: array
types may now be inferred from Command Templates automatically as well. This
includes all main number types and String. fix: ArgumentTypeInfer#register
allowing to pass no Classes, essentially doing nothing. fix:
ArgumentTypeInfer#register allowing to overwrite previous definitions. add:
test for MultipleNumbersArgumentType
---
src/main/java/lanat/ArgumentBuilder.java | 7 ++-
src/main/java/lanat/ArgumentTypeInfer.java | 63 +++++++++++++++----
.../MultipleNumbersArgumentType.java | 28 +++++++++
.../MultipleStringsArgumentType.java | 19 +++---
.../NumberRangeArgumentType.java | 9 +--
.../argumentTypes/TupleArgumentType.java | 36 +++++++++--
.../ArgumentTypeInferException.java | 7 +++
src/main/java/lanat/utils/UtlReflection.java | 1 -
src/test/java/lanat/test/UnitTests.java | 8 +--
.../lanat/test/units/TestArgumentTypes.java | 13 +++-
10 files changed, 147 insertions(+), 44 deletions(-)
create mode 100644 src/main/java/lanat/argumentTypes/MultipleNumbersArgumentType.java
create mode 100644 src/main/java/lanat/exceptions/ArgumentTypeInferException.java
diff --git a/src/main/java/lanat/ArgumentBuilder.java b/src/main/java/lanat/ArgumentBuilder.java
index a2839410..92c8142b 100644
--- a/src/main/java/lanat/ArgumentBuilder.java
+++ b/src/main/java/lanat/ArgumentBuilder.java
@@ -1,6 +1,7 @@
package lanat;
import lanat.argumentTypes.DummyArgumentType;
+import lanat.exceptions.ArgumentTypeInferException;
import lanat.utils.UtlReflection;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -48,7 +49,11 @@ public class ArgumentBuilder, TInner> {
return UtlReflection.instantiate(annotation.argType());
// try to infer the type from the field type. If it can't be inferred, return null
- return ArgumentTypeInfer.get(field.getType());
+ try {
+ return ArgumentTypeInfer.get(field.getType());
+ } catch (ArgumentTypeInferException e) {
+ return null;
+ }
}
/**
diff --git a/src/main/java/lanat/ArgumentTypeInfer.java b/src/main/java/lanat/ArgumentTypeInfer.java
index 40f207fd..5c1efd66 100644
--- a/src/main/java/lanat/ArgumentTypeInfer.java
+++ b/src/main/java/lanat/ArgumentTypeInfer.java
@@ -1,18 +1,22 @@
package lanat;
import lanat.argumentTypes.*;
+import lanat.exceptions.ArgumentTypeInferException;
+import lanat.utils.Range;
import org.jetbrains.annotations.NotNull;
import java.io.File;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.function.Supplier;
public class ArgumentTypeInfer {
/**
* Mapping of types to their corresponding argument types. Used for inferring.
- * Argument types are stored as suppliers so that they can be instantiated when needed.
+ * Argument types are stored as suppliers so that we have no shared references.
* */
private static final HashMap, Supplier extends ArgumentType>>> INFER_ARGUMENT_TYPES_MAP = new HashMap<>();
+ public static final Range DEFAULT_TYPE_RANGE = Range.AT_LEAST_ONE;
/**
* Registers an argument type to be inferred for the specified type/s.
@@ -20,33 +24,66 @@ public class ArgumentTypeInfer {
* @param infer The types to infer the argument type for.
*/
public static void register(@NotNull Supplier extends ArgumentType>> type, @NotNull Class>... infer) {
+ if (infer.length == 0)
+ throw new IllegalArgumentException("Must specify at least one type to infer the argument type for.");
+
for (Class> clazz : infer) {
+ if (ArgumentTypeInfer.INFER_ARGUMENT_TYPES_MAP.containsKey(clazz))
+ throw new IllegalArgumentException("Argument type already registered for type: " + clazz.getName());
+
ArgumentTypeInfer.INFER_ARGUMENT_TYPES_MAP.put(clazz, type);
}
}
/**
- * Returns the argument type that should be inferred for the specified type.
+ * Returns a new argument type instance for the specified type.
* @param clazz The type to infer the argument type for.
- * @return The argument type that should be inferred for the specified type. Returns {@code null} if no
- * valid argument type was found.
+ * @return The argument type that should be inferred for the specified type.
+ * @throws ArgumentTypeInferException If no argument type is found for the specified type.
*/
public static ArgumentType> get(@NotNull Class> clazz) {
- return ArgumentTypeInfer.INFER_ARGUMENT_TYPES_MAP.get(clazz).get();
+ var result = ArgumentTypeInfer.INFER_ARGUMENT_TYPES_MAP.get(clazz);
+
+ if (result == null)
+ throw new ArgumentTypeInferException(clazz);
+
+ return result.get();
+ }
+
+ /**
+ * Registers a numeric argument type with the specified tuple type as well.
+ * @param type The type of the numeric argument type.
+ * @param array The default value of the numeric argument type.
+ * @param infer The non-array types to infer the argument type for.
+ * @param The type of the numeric type.
+ * @param The type of the tuple argument type.
+ */
+ private static >
+ void registerNumericWithTuple(@NotNull Supplier type, Ti[] array, @NotNull Class>... infer) {
+ ArgumentTypeInfer.register(type, infer);
+ ArgumentTypeInfer.register(
+ () -> new MultipleNumbersArgumentType<>(DEFAULT_TYPE_RANGE, array),
+ Arrays.stream(infer)
+ .map(Class::arrayType)
+ .toArray(Class[]::new)
+ );
}
// add some default argument types.
static {
- // we need to also specify the primitives... wish there was a better way to do this.
register(StringArgumentType::new, String.class);
- register(MultipleStringsArgumentType::new, String[].class);
- register(IntegerArgumentType::new, int.class, Integer.class);
+ register(() -> new MultipleStringsArgumentType(DEFAULT_TYPE_RANGE), String[].class);
+
register(BooleanArgumentType::new, boolean.class, Boolean.class);
- register(FloatArgumentType::new, float.class, Float.class);
- register(DoubleArgumentType::new, double.class, Double.class);
- register(LongArgumentType::new, long.class, Long.class);
- register(ShortArgumentType::new, short.class, Short.class);
- register(ByteArgumentType::new, byte.class, Byte.class);
+
register(() -> new FileArgumentType(false), File.class);
+
+ // we need to specify the primitives as well... wish there was a better way to do this.
+ registerNumericWithTuple(IntegerArgumentType::new, new Integer[] {}, int.class, Integer.class);
+ registerNumericWithTuple(FloatArgumentType::new, new Float[] {}, float.class, Float.class);
+ registerNumericWithTuple(DoubleArgumentType::new, new Double[] {}, double.class, Double.class);
+ registerNumericWithTuple(LongArgumentType::new, new Long[] {}, long.class, Long.class);
+ registerNumericWithTuple(ShortArgumentType::new, new Short[] {}, short.class, Short.class);
+ registerNumericWithTuple(ByteArgumentType::new, new Byte[] {}, byte.class, Byte.class);
}
}
diff --git a/src/main/java/lanat/argumentTypes/MultipleNumbersArgumentType.java b/src/main/java/lanat/argumentTypes/MultipleNumbersArgumentType.java
new file mode 100644
index 00000000..cafe72de
--- /dev/null
+++ b/src/main/java/lanat/argumentTypes/MultipleNumbersArgumentType.java
@@ -0,0 +1,28 @@
+package lanat.argumentTypes;
+
+import lanat.ArgumentType;
+import lanat.ArgumentTypeInfer;
+import lanat.utils.Range;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * An argument type that takes multiple numbers.
+ * @param The type of number that this argument type is.
+ */
+public class MultipleNumbersArgumentType extends TupleArgumentType {
+ /**
+ * Creates a new {@link TupleArgumentType} with the specified range of values that the argument will take.
+ * @param range The range of values that the argument will take.
+ * @param defaultValue The default value of the argument. This will be used if no values are provided.
+ * @throws lanat.exceptions.ArgumentTypeInferException If the type of the default value is not supported.
+ */
+ @SuppressWarnings("unchecked")
+ public MultipleNumbersArgumentType(@NotNull Range range, @NotNull Ti[] defaultValue) {
+ super(
+ range,
+ // we can infer the type of the argument type from the default value
+ (ArgumentType)ArgumentTypeInfer.get(defaultValue.getClass().getComponentType()),
+ defaultValue
+ );
+ }
+}
diff --git a/src/main/java/lanat/argumentTypes/MultipleStringsArgumentType.java b/src/main/java/lanat/argumentTypes/MultipleStringsArgumentType.java
index 291c46b4..c19af4c5 100644
--- a/src/main/java/lanat/argumentTypes/MultipleStringsArgumentType.java
+++ b/src/main/java/lanat/argumentTypes/MultipleStringsArgumentType.java
@@ -1,26 +1,23 @@
package lanat.argumentTypes;
-import lanat.ArgumentType;
import lanat.utils.Range;
import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
/**
* An argument type that takes multiple strings.
*/
-public class MultipleStringsArgumentType extends ArgumentType {
- @Override
- public @NotNull Range getRequiredArgValueCount() {
- return Range.AT_LEAST_ONE;
+public class MultipleStringsArgumentType extends TupleArgumentType {
+ /**
+ * Creates a new {@link TupleArgumentType} with the specified range of values that the argument will take.
+ * @param range The range of values that the argument will take.
+ */
+ public MultipleStringsArgumentType(@NotNull Range range) {
+ super(range, new StringArgumentType(), new String[0]);
}
+ // no need for anything fancy here, simply return the args
@Override
public @NotNull String[] parseValues(@NotNull String @NotNull [] args) {
return args;
}
-
- @Override
- public @Nullable String getDescription() {
- return "Accepts multiple strings.";
- }
}
diff --git a/src/main/java/lanat/argumentTypes/NumberRangeArgumentType.java b/src/main/java/lanat/argumentTypes/NumberRangeArgumentType.java
index 907395a9..1c876e19 100644
--- a/src/main/java/lanat/argumentTypes/NumberRangeArgumentType.java
+++ b/src/main/java/lanat/argumentTypes/NumberRangeArgumentType.java
@@ -21,6 +21,7 @@ public class NumberRangeArgumentType> extends A
* Creates a new number range argument type.
* @param min The minimum value.
* @param max The maximum value.
+ * @throws lanat.exceptions.ArgumentTypeInferException If the type of the default value is not supported.
*/
@SuppressWarnings("unchecked")
public NumberRangeArgumentType(@NotNull T min, @NotNull T max) {
@@ -28,13 +29,7 @@ public NumberRangeArgumentType(@NotNull T min, @NotNull T max) {
throw new IllegalArgumentException("min must be less than or equal to max");
}
- final var typeInferred = ArgumentTypeInfer.get(min.getClass());
-
- if (typeInferred == null) {
- throw new IllegalArgumentException("Unsupported type: " + min.getClass().getName());
- }
-
- this.argumentType = (ArgumentType)typeInferred;
+ this.argumentType = (ArgumentType)ArgumentTypeInfer.get(min.getClass());
this.registerSubType(this.argumentType);
this.min = min;
diff --git a/src/main/java/lanat/argumentTypes/TupleArgumentType.java b/src/main/java/lanat/argumentTypes/TupleArgumentType.java
index 4b95155a..73d52b51 100644
--- a/src/main/java/lanat/argumentTypes/TupleArgumentType.java
+++ b/src/main/java/lanat/argumentTypes/TupleArgumentType.java
@@ -12,12 +12,32 @@
* Shows a properly formatted description and representation.
* @param the type of the value that the argument will take
*/
-public abstract class TupleArgumentType extends ArgumentType {
+public abstract class TupleArgumentType extends ArgumentType {
private final @NotNull Range argCount;
+ private final @NotNull ArgumentType argumentType;
- public TupleArgumentType(@NotNull Range range, @NotNull T initialValue) {
- super(initialValue);
+ /**
+ * Creates a new {@link TupleArgumentType} with the specified range and argument type.
+ * @param range The range of values that the argument will take.
+ * @param argumentType The argument type that will be used to parse the values.
+ * @param defaultValue The default value of the argument. This will be used if no values are provided.
+ */
+ public TupleArgumentType(@NotNull Range range, @NotNull ArgumentType argumentType, @NotNull T[] defaultValue) {
+ super(defaultValue);
this.argCount = range;
+ this.registerSubType(this.argumentType = argumentType);
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public T @Nullable [] parseValues(@NotNull String... args) {
+ var result = new Object[args.length];
+
+ for (int i = 0; i < args.length; i++) {
+ result[i] = this.argumentType.parseValues(args[i]);
+ }
+
+ return (T[])result;
}
@Override
@@ -26,14 +46,18 @@ public TupleArgumentType(@NotNull Range range, @NotNull T initialValue) {
}
@Override
- public @NotNull TextFormatter getRepresentation() {
- return new TextFormatter(this.getValue().getClass().getSimpleName())
+ public @Nullable TextFormatter getRepresentation() {
+ var argTypeRepr = this.argumentType.getRepresentation();
+ if (argTypeRepr == null)
+ return null;
+
+ return argTypeRepr
.concat(new TextFormatter(this.argCount.getRegexRange()).withForegroundColor(Color.BRIGHT_YELLOW));
}
@Override
public @Nullable String getDescription() {
return "Takes " + this.argCount.getMessage("value")
- + " of type " + this.getInitialValue().getClass().getSimpleName() + ".";
+ + " of type " + this.argumentType.getRepresentation() + ".";
}
}
\ No newline at end of file
diff --git a/src/main/java/lanat/exceptions/ArgumentTypeInferException.java b/src/main/java/lanat/exceptions/ArgumentTypeInferException.java
new file mode 100644
index 00000000..9e67177f
--- /dev/null
+++ b/src/main/java/lanat/exceptions/ArgumentTypeInferException.java
@@ -0,0 +1,7 @@
+package lanat.exceptions;
+
+public class ArgumentTypeInferException extends ArgumentTypeException {
+ public ArgumentTypeInferException(Class> clazz) {
+ super("No argument type found for type: " + clazz.getName());
+ }
+}
diff --git a/src/main/java/lanat/utils/UtlReflection.java b/src/main/java/lanat/utils/UtlReflection.java
index a1e8714e..687bcc27 100644
--- a/src/main/java/lanat/utils/UtlReflection.java
+++ b/src/main/java/lanat/utils/UtlReflection.java
@@ -64,5 +64,4 @@ public static Stream getMethods(Class> clazz) {
return UtlReflection.getMethods(clazz.getSuperclass());
return Stream.of(clazz.getDeclaredMethods());
}
-
}
diff --git a/src/test/java/lanat/test/UnitTests.java b/src/test/java/lanat/test/UnitTests.java
index 4f4c308c..65f3c366 100644
--- a/src/test/java/lanat/test/UnitTests.java
+++ b/src/test/java/lanat/test/UnitTests.java
@@ -6,7 +6,6 @@
import lanat.argumentTypes.CounterArgumentType;
import lanat.argumentTypes.IntegerArgumentType;
import lanat.argumentTypes.StringArgumentType;
-import lanat.argumentTypes.TupleArgumentType;
import lanat.helpRepresentation.HelpFormatter;
import lanat.utils.Range;
import lanat.utils.displayFormatter.TextFormatter;
@@ -17,9 +16,10 @@
import static org.junit.jupiter.api.Assertions.assertTrue;
-class StringJoiner extends TupleArgumentType {
- public StringJoiner() {
- super(Range.from(1).to(3), "");
+class StringJoiner extends ArgumentType {
+ @Override
+ public @NotNull Range getRequiredArgValueCount() {
+ return Range.from(1).to(3);
}
@Override
diff --git a/src/test/java/lanat/test/units/TestArgumentTypes.java b/src/test/java/lanat/test/units/TestArgumentTypes.java
index 7b4a1873..c04e4373 100644
--- a/src/test/java/lanat/test/units/TestArgumentTypes.java
+++ b/src/test/java/lanat/test/units/TestArgumentTypes.java
@@ -4,6 +4,7 @@
import lanat.argumentTypes.*;
import lanat.test.TestingParser;
import lanat.test.UnitTests;
+import lanat.utils.Range;
import org.junit.jupiter.api.Test;
import java.io.File;
@@ -24,7 +25,10 @@ protected TestingParser setParser() {
this.addArgument(Argument.create(new IntegerArgumentType(), "integer"));
this.addArgument(Argument.create(new FloatArgumentType(), "float"));
this.addArgument(Argument.create(new StringArgumentType(), "string"));
- this.addArgument(Argument.create(new MultipleStringsArgumentType(), "multiple-strings"));
+ this.addArgument(Argument.create(new MultipleStringsArgumentType(Range.AT_LEAST_ONE), "multiple-strings"));
+ this.addArgument(Argument.create(new MultipleNumbersArgumentType<>(
+ Range.AT_LEAST_ONE, new Integer[] { 10101 }), "multiple-ints")
+ );
this.addArgument(Argument.create(new FileArgumentType(true), "file"));
this.addArgument(Argument.create(new EnumArgumentType<>(TestEnum.TWO), "enum"));
this.addArgument(Argument.create(new KeyValuesArgumentType<>(new IntegerArgumentType()), "key-value"));
@@ -75,6 +79,13 @@ public void testStrings() {
assertArrayEquals(new String[] { "hello world" }, this.parseArg("multiple-strings", "'hello world'"));
}
+ @Test
+ public void testNumbers() {
+ assertArrayEquals(new Integer[] { 4 }, this.parseArg("multiple-ints", "4"));
+ assertArrayEquals(new Integer[] { 4, 5, 6 }, this.parseArg("multiple-ints", "4 5 6"));
+ assertArrayEquals(new Integer[] { 10101 }, this.parseArg("multiple-ints", ""));
+ }
+
@Test
public void testFile() {
assertNull(this.parseArg("file", "hello.txt"));
From 90bf31226374b33bad80dd3abc527996e8bed0c4 Mon Sep 17 00:00:00 2001
From: DarviL82
Date: Wed, 18 Oct 2023 18:17:13 +0200
Subject: [PATCH 08/15] fix: Command Template generation throwing exception
when attempting to cast array types from parsed values. add: test for array
parsed values
---
src/main/java/lanat/ArgumentParser.java | 46 +++++++++++++++++--
.../manualTests/CommandTemplateExample.java | 2 +
.../lanat/test/manualTests/ManualTests.java | 15 ++----
.../units/commandTemplates/CmdTemplates.java | 3 ++
.../units/commandTemplates/TestFromInto.java | 18 ++++++--
5 files changed, 64 insertions(+), 20 deletions(-)
diff --git a/src/main/java/lanat/ArgumentParser.java b/src/main/java/lanat/ArgumentParser.java
index 21d4073f..aa45bb96 100644
--- a/src/main/java/lanat/ArgumentParser.java
+++ b/src/main/java/lanat/ArgumentParser.java
@@ -8,6 +8,7 @@
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Arrays;
@@ -131,7 +132,11 @@ public static ArgumentParser from(@NotNull Class extends CommandTemplate> temp
*/
public static
@NotNull T parseFromInto(@NotNull Class templateClass, @NotNull CLInput input) {
- return ArgumentParser.parseFromInto(templateClass, input, opts -> opts.printErrors().exitIfErrors());
+ return ArgumentParser.parseFromInto(
+ templateClass,
+ input,
+ opts -> opts.printErrors().exitIfErrors().printHelpIfNoInput()
+ );
}
/**
@@ -366,7 +371,7 @@ private static T into(
// get the name of the argument from the annotation or field name
final String argName = annotation.names().length == 0 ? f.getName() : annotation.names()[0];
- final @NotNull Optional
*
- * To generate the help message, use {@link #generate(Command)} ()}.
+ * To generate the help message, use {@link #generate(Command)}.
*
*
* @see LayoutItem
@@ -32,6 +32,7 @@ public class HelpFormatter {
public static boolean debugLayout = false;
static {
+ // register the default tags before we start parsing descriptions
Tag.initTags();
}
diff --git a/src/main/java/lanat/helpRepresentation/LayoutItem.java b/src/main/java/lanat/helpRepresentation/LayoutItem.java
index 97bf1df8..9abef01e 100644
--- a/src/main/java/lanat/helpRepresentation/LayoutItem.java
+++ b/src/main/java/lanat/helpRepresentation/LayoutItem.java
@@ -10,7 +10,7 @@
/**
* Represents a layout item in the help message generated by {@link HelpFormatter}. This class is essentially just a
- * builder with some helper commands for setting a {@link Function} that generates a {@link String} for a given
+ * builder with some helper utilities for setting a {@link Function} that generates a {@link String} for a given
* {@link Command}.
*
* @see HelpFormatter
@@ -107,9 +107,10 @@ public LayoutItem margin(int margin) {
*
* It is shown as:
*
- * <title<:
+ * <title>:
* <content>
*
+ * If no content is generated, the title is not shown.
*
* @param title the title of the layout item
*/
diff --git a/src/main/java/lanat/utils/UtlString.java b/src/main/java/lanat/utils/UtlString.java
index ab91ece4..0965714c 100644
--- a/src/main/java/lanat/utils/UtlString.java
+++ b/src/main/java/lanat/utils/UtlString.java
@@ -72,7 +72,7 @@ private UtlString() {}
final var indentBuff = new StringBuilder(); // buffer for the current indentation that will be added to the beginning of each line if needed
int lineWidth = 0; // the current line width
- boolean jumped = true; // true if a newline was added. starts off as true in case the string with indentation
+ boolean jumped = true; // true if a newline was added. starts off as true in case the string starts with indentation
for (char chr : str.toCharArray()) {
if (chr == ' ' || chr == '\t') {
@@ -89,7 +89,7 @@ private UtlString() {}
}
endBuffer.append(wordBuff).append(chr);
// make sure to not count escape sequences on the length!
- lineWidth += UtlString.removeSequences(wordBuff.toString()).length() + 1;
+ lineWidth += UtlString.removeSequences(wordBuff.toString()).length() + 1; // +1 for the char we just added
wordBuff.setLength(0);
}
From 82bf46d10462026cb29771ad2bcae8b3f975babd Mon Sep 17 00:00:00 2001
From: DarviL
Date: Sun, 22 Oct 2023 21:03:02 +0200
Subject: [PATCH 13/15] fix: blank string not being detected as no input. feat:
Now possible to specify in FileArgumentType if the file should be a directory
or a regular file. feat: Improved the generated description and
representation for FileArgumentType.
---
src/main/java/lanat/CLInput.java | 2 +-
.../argumentTypes/BooleanArgumentType.java | 1 +
.../lanat/argumentTypes/ByteArgumentType.java | 1 +
.../argumentTypes/DoubleArgumentType.java | 1 +
.../lanat/argumentTypes/FileArgumentType.java | 80 ++++++++++++++++---
.../argumentTypes/FloatArgumentType.java | 1 +
.../FromParseableArgumentType.java | 1 +
.../argumentTypes/IntegerArgumentType.java | 1 +
.../argumentTypes/KeyValuesArgumentType.java | 1 +
.../lanat/argumentTypes/LongArgumentType.java | 1 +
.../argumentTypes/NumberArgumentType.java | 1 +
.../argumentTypes/ShortArgumentType.java | 1 +
.../argumentTypes/StringArgumentType.java | 1 +
src/main/java/lanat/utils/UtlMisc.java | 6 +-
.../manualTests/CommandTemplateExample.java | 24 ++++--
.../lanat/test/manualTests/ManualTests.java | 2 +-
16 files changed, 104 insertions(+), 21 deletions(-)
diff --git a/src/main/java/lanat/CLInput.java b/src/main/java/lanat/CLInput.java
index f5e2ce63..a72e0cce 100644
--- a/src/main/java/lanat/CLInput.java
+++ b/src/main/java/lanat/CLInput.java
@@ -12,7 +12,7 @@ public final class CLInput {
public final @NotNull String args;
private CLInput(@NotNull String args) {
- this.args = args;
+ this.args = args.trim();
}
/**
diff --git a/src/main/java/lanat/argumentTypes/BooleanArgumentType.java b/src/main/java/lanat/argumentTypes/BooleanArgumentType.java
index ec676c09..b80800cb 100644
--- a/src/main/java/lanat/argumentTypes/BooleanArgumentType.java
+++ b/src/main/java/lanat/argumentTypes/BooleanArgumentType.java
@@ -8,6 +8,7 @@
/**
* An argument type that is set in a true state if the argument was used.
+ * @see Boolean
*/
public class BooleanArgumentType extends ArgumentType {
public BooleanArgumentType() {
diff --git a/src/main/java/lanat/argumentTypes/ByteArgumentType.java b/src/main/java/lanat/argumentTypes/ByteArgumentType.java
index c92c3ac4..ef93a9ef 100644
--- a/src/main/java/lanat/argumentTypes/ByteArgumentType.java
+++ b/src/main/java/lanat/argumentTypes/ByteArgumentType.java
@@ -7,6 +7,7 @@
/**
* An argument type that takes a byte value.
+ * @see Byte
*/
public class ByteArgumentType extends NumberArgumentType {
@Override
diff --git a/src/main/java/lanat/argumentTypes/DoubleArgumentType.java b/src/main/java/lanat/argumentTypes/DoubleArgumentType.java
index 89825e46..8b169465 100644
--- a/src/main/java/lanat/argumentTypes/DoubleArgumentType.java
+++ b/src/main/java/lanat/argumentTypes/DoubleArgumentType.java
@@ -7,6 +7,7 @@
/**
* An argument type that takes a double precision floating point number.
+ * @see Double
*/
public class DoubleArgumentType extends NumberArgumentType {
@Override
diff --git a/src/main/java/lanat/argumentTypes/FileArgumentType.java b/src/main/java/lanat/argumentTypes/FileArgumentType.java
index 28179b8a..f4941037 100644
--- a/src/main/java/lanat/argumentTypes/FileArgumentType.java
+++ b/src/main/java/lanat/argumentTypes/FileArgumentType.java
@@ -1,6 +1,7 @@
package lanat.argumentTypes;
import lanat.ArgumentType;
+import lanat.utils.displayFormatter.TextFormatter;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -8,31 +9,88 @@
/**
* An argument type that takes a file path and returns a {@link File} instance representing it.
+ * This argument type can also check if the file exists and if it is a regular file or a directory.
+ * @see File
*/
public class FileArgumentType extends ArgumentType {
- private final boolean needsToExist;
+ public enum FileType {
+ REGULAR_FILE,
+ DIRECTORY,
+ ANY;
+
+ private @NotNull String getName(boolean shortName) {
+ return switch (this) {
+ case REGULAR_FILE -> "file";
+ case DIRECTORY -> "directory";
+ case ANY -> shortName ? "(file/dir)" : "file or directory";
+ };
+ }
+ }
+
+ private final boolean mustExist;
+ private final @NotNull FileType fileType;
+
/**
* Creates a new file argument type.
- * @param needsToExist whether the file needs to exist or not. If set to false, the file instance will be created
- * even it doesn't exist.
+ * @param mustExist whether the file must exist or not
+ * @param fileType the type of the file (regular file, directory, or any)
*/
- public FileArgumentType(boolean needsToExist) {
- this.needsToExist = needsToExist;
+ public FileArgumentType(boolean mustExist, @NotNull FileType fileType) {
+ this.mustExist = mustExist;
+ this.fileType = fileType;
+ }
+
+ /**
+ * Creates a new file argument type which accepts any kind of file.
+ * @param mustExist whether the file must exist or not
+ */
+ public FileArgumentType(boolean mustExist) {
+ this(mustExist, FileType.ANY);
+ }
+
+ /**
+ * Checks if the file is valid. This method may add errors to the type.
+ * @param file the file to check
+ * @return whether the file is valid or not
+ */
+ protected boolean checkFile(@NotNull File file) {
+ if (this.mustExist && !file.exists()) {
+ this.addError("File does not exist.");
+ return false;
+ }
+
+ if (this.fileType == FileType.REGULAR_FILE && !file.isFile()) {
+ this.addError("File is not a regular file.");
+ return false;
+ }
+
+ if (this.fileType == FileType.DIRECTORY && !file.isDirectory()) {
+ this.addError("File is not a directory.");
+ return false;
+ }
+
+ return true;
}
@Override
public File parseValues(@NotNull String @NotNull [] args) {
File file = new File(args[0]);
- if (this.needsToExist && !file.exists()) {
- this.addError("File does not exist.");
- return null;
- }
- return file;
+ return this.checkFile(file) ? file : null;
}
@Override
public @Nullable String getDescription() {
- return "A file path.";
+ return "A file path of"
+ + (this.mustExist ? " an existing " : " a ")
+ + this.fileType.getName(false)
+ + ".";
+ }
+
+ @Override
+ public @Nullable TextFormatter getRepresentation() {
+ return new TextFormatter(
+ "path" + File.separator + "to" + File.separator + this.fileType.getName(true)
+ );
}
}
diff --git a/src/main/java/lanat/argumentTypes/FloatArgumentType.java b/src/main/java/lanat/argumentTypes/FloatArgumentType.java
index 7a240f85..fa987fbd 100644
--- a/src/main/java/lanat/argumentTypes/FloatArgumentType.java
+++ b/src/main/java/lanat/argumentTypes/FloatArgumentType.java
@@ -7,6 +7,7 @@
/**
* An argument type that takes a floating point number.
+ * @see Float
*/
public class FloatArgumentType extends NumberArgumentType {
@Override
diff --git a/src/main/java/lanat/argumentTypes/FromParseableArgumentType.java b/src/main/java/lanat/argumentTypes/FromParseableArgumentType.java
index 0c41a446..cd15a13a 100644
--- a/src/main/java/lanat/argumentTypes/FromParseableArgumentType.java
+++ b/src/main/java/lanat/argumentTypes/FromParseableArgumentType.java
@@ -11,6 +11,7 @@
* method returns {@code null}, an error is added. The error message can be specified in the constructor.
* @param The {@link Parseable} type.
* @param The type of the value returned by the {@link Parseable#parseValues(String[])} method.
+ * @see Parseable
*/
public class FromParseableArgumentType, TInner> extends ArgumentType {
private final @NotNull T parseable;
diff --git a/src/main/java/lanat/argumentTypes/IntegerArgumentType.java b/src/main/java/lanat/argumentTypes/IntegerArgumentType.java
index 52637a41..4736711b 100644
--- a/src/main/java/lanat/argumentTypes/IntegerArgumentType.java
+++ b/src/main/java/lanat/argumentTypes/IntegerArgumentType.java
@@ -7,6 +7,7 @@
/**
* An argument type that takes an integer number.
+ * @see Integer
*/
public class IntegerArgumentType extends NumberArgumentType {
@Override
diff --git a/src/main/java/lanat/argumentTypes/KeyValuesArgumentType.java b/src/main/java/lanat/argumentTypes/KeyValuesArgumentType.java
index bcb650ea..caf095c8 100644
--- a/src/main/java/lanat/argumentTypes/KeyValuesArgumentType.java
+++ b/src/main/java/lanat/argumentTypes/KeyValuesArgumentType.java
@@ -18,6 +18,7 @@
*
* @param The type of the argument type used to parse the values.
* @param The type of the values.
+ * @see HashMap
*/
public class KeyValuesArgumentType, Ts> extends ArgumentType> {
private final @NotNull ArgumentType valueType;
diff --git a/src/main/java/lanat/argumentTypes/LongArgumentType.java b/src/main/java/lanat/argumentTypes/LongArgumentType.java
index bef388e8..25e4cc23 100644
--- a/src/main/java/lanat/argumentTypes/LongArgumentType.java
+++ b/src/main/java/lanat/argumentTypes/LongArgumentType.java
@@ -7,6 +7,7 @@
/**
* An argument type that takes a long integer number.
+ * @see Long
*/
public class LongArgumentType extends NumberArgumentType {
@Override
diff --git a/src/main/java/lanat/argumentTypes/NumberArgumentType.java b/src/main/java/lanat/argumentTypes/NumberArgumentType.java
index 0445ff3a..7f887f3d 100644
--- a/src/main/java/lanat/argumentTypes/NumberArgumentType.java
+++ b/src/main/java/lanat/argumentTypes/NumberArgumentType.java
@@ -10,6 +10,7 @@
* of {@link #parseValues(String[])} that will parse the first argument as a number using the
* function returned by {@link #getParseFunction()}.
* @param The type of number that this argument type is.
+ * @see Number
*/
public abstract class NumberArgumentType extends ArgumentType {
/**
diff --git a/src/main/java/lanat/argumentTypes/ShortArgumentType.java b/src/main/java/lanat/argumentTypes/ShortArgumentType.java
index 529d419c..4ed67dda 100644
--- a/src/main/java/lanat/argumentTypes/ShortArgumentType.java
+++ b/src/main/java/lanat/argumentTypes/ShortArgumentType.java
@@ -7,6 +7,7 @@
/**
* An argument type that takes a short integer number.
+ * @see Short
*/
public class ShortArgumentType extends NumberArgumentType {
@Override
diff --git a/src/main/java/lanat/argumentTypes/StringArgumentType.java b/src/main/java/lanat/argumentTypes/StringArgumentType.java
index 1f04a3c0..957a0b13 100644
--- a/src/main/java/lanat/argumentTypes/StringArgumentType.java
+++ b/src/main/java/lanat/argumentTypes/StringArgumentType.java
@@ -7,6 +7,7 @@
/**
* An argument type that takes a string of characters.
+ * @see String
*/
public class StringArgumentType extends ArgumentType {
@Override
diff --git a/src/main/java/lanat/utils/UtlMisc.java b/src/main/java/lanat/utils/UtlMisc.java
index 68c68899..f6775495 100644
--- a/src/main/java/lanat/utils/UtlMisc.java
+++ b/src/main/java/lanat/utils/UtlMisc.java
@@ -2,7 +2,6 @@
import lanat.CommandUser;
import lanat.MultipleNamesAndDescription;
-import lanat.exceptions.ObjectAlreadyExistsException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -20,7 +19,7 @@ private UtlMisc() {}
*/
public static void requireUniqueElements(
@NotNull List list,
- @NotNull Function exceptionSupplier
+ @NotNull Function exceptionSupplier
) {
for (int i = 0; i < list.size(); i++) {
final var el = list.get(i);
@@ -63,8 +62,9 @@ boolean equalsByNamesAndParentCmd(@NotNull T a, @NotNull T b) {
* @param defaultObj The function to apply to {@code obj} if it is not {@code null}
* @return {@code null} if {@code obj} is {@code null}, otherwise returns the result of the given function
* @param The type of the objects
+ * @param The type of the result of the function
*/
- public static @Nullable T nullOrElse(@Nullable T obj, @NotNull Function<@NotNull T, @NotNull T> defaultObj) {
+ public static @Nullable R nullOrElse(@Nullable T obj, @NotNull Function<@NotNull T, @NotNull R> defaultObj) {
return obj == null ? null : defaultObj.apply(obj);
}
}
diff --git a/src/test/java/lanat/test/manualTests/CommandTemplateExample.java b/src/test/java/lanat/test/manualTests/CommandTemplateExample.java
index f920fad8..c91fde58 100644
--- a/src/test/java/lanat/test/manualTests/CommandTemplateExample.java
+++ b/src/test/java/lanat/test/manualTests/CommandTemplateExample.java
@@ -4,12 +4,10 @@
import lanat.ArgumentGroup;
import lanat.Command;
import lanat.CommandTemplate;
-import lanat.argumentTypes.CounterArgumentType;
-import lanat.argumentTypes.NumberRangeArgumentType;
-import lanat.argumentTypes.StdinArgumentType;
-import lanat.argumentTypes.StringArgumentType;
+import lanat.argumentTypes.*;
import org.jetbrains.annotations.NotNull;
+import java.io.File;
import java.util.Optional;
@Command.Define(names = "my-program", description = "This is a test program.")
@@ -19,7 +17,7 @@ public CommandTemplateExample() {}
@Argument.Define(argType = StringArgumentType.class, description = "This is a string argument.")
public Optional string;
- @Argument.Define(description = "", required = true)
+ @Argument.Define(description = "")
public double number = 12;
@Argument.Define(argType = StdinArgumentType.class)
@@ -28,6 +26,8 @@ public CommandTemplateExample() {}
@Argument.Define(names = "arg1", argType = StringArgumentType.class)
public String arg1;
+ @Argument.Define(description = "")
+ public File file;
@Argument.Define(names = "arg1a", argType = StringArgumentType.class)
public String arg1copy;
@@ -42,6 +42,20 @@ public CommandTemplateExample() {}
public static void beforeInit(@NotNull CommandBuildHelper helper) {
helper., Double>arg("number")
.withArgType(new NumberRangeArgumentType<>(5.5, 15.89));
+ helper.arg("file")
+ .withArgType(new FileArgumentType(true, FileArgumentType.FileType.REGULAR_FILE) {
+ @Override
+ protected boolean checkFile(@NotNull File file) {
+ if (!super.checkFile(file)) return false;
+
+ if (!file.canExecute()) {
+ this.addError("File is not executable.");
+ return false;
+ }
+
+ return true;
+ }
+ });
}
@InitDef
diff --git a/src/test/java/lanat/test/manualTests/ManualTests.java b/src/test/java/lanat/test/manualTests/ManualTests.java
index 3bcfb21f..783de0b5 100644
--- a/src/test/java/lanat/test/manualTests/ManualTests.java
+++ b/src/test/java/lanat/test/manualTests/ManualTests.java
@@ -10,7 +10,7 @@
public final class ManualTests {
@Test
public void main() {
- String input = "--help --version sub-command -h";
+ String input = " ";
// write some stuff to stdin
System.setIn(new ByteArrayInputStream("hello world\ngoodbye".getBytes()));
From d766754abd898f7a471014c336453c7abf8a39ac Mon Sep 17 00:00:00 2001
From: DarviL
Date: Sun, 22 Oct 2023 21:04:56 +0200
Subject: [PATCH 14/15] fix: tests not running
---
src/test/java/lanat/test/manualTests/ManualTests.java | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/src/test/java/lanat/test/manualTests/ManualTests.java b/src/test/java/lanat/test/manualTests/ManualTests.java
index 783de0b5..54516626 100644
--- a/src/test/java/lanat/test/manualTests/ManualTests.java
+++ b/src/test/java/lanat/test/manualTests/ManualTests.java
@@ -15,7 +15,13 @@ public void main() {
// write some stuff to stdin
System.setIn(new ByteArrayInputStream("hello world\ngoodbye".getBytes()));
- var parsed = ArgumentParser.parseFromInto(CommandTemplateExample.class, CLInput.from(input));
+ var parsed = ArgumentParser.parseFromInto(
+ CommandTemplateExample.class,
+ CLInput.from(input),
+ o -> o.exitIfErrors()
+ .printErrors()
+ .printHelpIfNoInput()
+ );
parsed.string
.ifPresentOrElse(
From 3c49edc1e94c163ff6301ca80c36d12039d8404f Mon Sep 17 00:00:00 2001
From: DarviL
Date: Sun, 22 Oct 2023 21:16:39 +0200
Subject: [PATCH 15/15] fix incorrect assert comment
---
src/main/java/lanat/ArgumentTypeInfer.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/main/java/lanat/ArgumentTypeInfer.java b/src/main/java/lanat/ArgumentTypeInfer.java
index 7942c97f..cb9d88b4 100644
--- a/src/main/java/lanat/ArgumentTypeInfer.java
+++ b/src/main/java/lanat/ArgumentTypeInfer.java
@@ -69,7 +69,7 @@ void registerNumericWithTuple(
@NotNull Class> infer
) {
assert !infer.isPrimitive() && inferPrimitive.isPrimitive()
- : "Infer must be a primitive type and inferPrimitive must be a non-primitive type.";
+ : "Infer must be a non-primitive type and inferPrimitive must be a primitive type.";
// register both the primitive and non-primitive types
ArgumentTypeInfer.register(type, inferPrimitive, infer);