Skip to content

Commit

Permalink
more fixes, cleanup + autoformat and integration
Browse files Browse the repository at this point in the history
  • Loading branch information
nextdayy committed Apr 15, 2024
1 parent df5dfb9 commit a0b92cf
Show file tree
Hide file tree
Showing 109 changed files with 1,160 additions and 920 deletions.
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,9 @@ tasks {
}

apiValidation {
nonPublicMarkers.add("org.polyfrost.oneconfig.api.PlatformDeclaration")
ignoredProjects.add("OneConfig")
ignoredPackages.add("org.polyfrost.oneconfig.libs")
ignoredPackages.add("org.polyfrost.oneconfig.internal")
ignoredPackages.add("org.polyfrost.oneconfig.test")
ignoredPackages.add("org.polyfrost.oneconfig.platform.impl")
}
5 changes: 3 additions & 2 deletions modules/README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
# OneConfig Modules

This folder contains the many modules that OneConfig offers to developers.
Each is designed to be an independent package, though some are dependent on other packages for their usage.
Each is designed to be an independent package, though some are dependent on other packages for their usage.
They each are under the namespace `org.polyfrost.oneconfig.api.*`.

Here is a list of the modules, and a brief description of what they do (For more information on how to use each module, see the README in the module's folder):

- [commands](commands/README.md) - The tree based command system used in OneConfig.
- [config](config/README.md) - The tree based configuration system used in OneConfig.
- [config-impl](config-impl/README.md) - The default implementation of the configuration system used in OneConfig.
Expand All @@ -14,7 +15,7 @@ Here is a list of the modules, and a brief description of what they do (For more
- [utils](utils/README.md) - Various utilities used in OneConfig.

Each module has the design philosophy of being decoupled from the internal structure, so that the developer can use the modules with separate front-facing APIs
depending on preference. For example, the command system has three different ways to create commands, and the config system has two different ways to collect configuration data.
depending on preference. For example, the command system has three different ways to create commands, and the config system has two different ways to collect configuration data.
This also helps to reduce interdependence and reduce the complexity of the codebase.

> For Minecraft dependant code, see the `versions` directory. Some utilities and classes are found in this directory, under the same package name as the module.
6 changes: 4 additions & 2 deletions modules/commands/README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
# OneConfig Commands

`dependsOn("utils")`

This module contains the command system used in OneConfig.
This module contains the command system used in OneConfig.
It is based around a simple tree based structure where each command is a node in the tree.

Commands support variable arity, arbitrary data types for parameters (see `ArgumentParser`), overloading, autocompletion, and more.

As with the rest of OneConfig, one of the main features of this is the decoupling from internal structure and the creation
As with the rest of OneConfig, one of the main features of this is the decoupling from internal structure and the creation
of the command - so there are multiple 'factories' that can create commands, by default being:

- `CommandBuilder` - a java style builder pattern, similar to brigaider
- `CommandDSL` - a kotlin style DSL
- `AnnotationCommandFactory` - a factory using annotated methods to create commands
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,31 @@ public class CommandManager {
*/
public static final CommandManager INSTANCE = new CommandManager();
private static final PlatformCommandManager platform = ServiceLoader.load(PlatformCommandManager.class, PlatformCommandManager.class.getClassLoader()).iterator().next();
private final Set<CommandFactory> factories = new HashSet<>();
/**
* use {@link #registerParser(ArgumentParser)} to register a parser
*/
@ApiStatus.Internal
public final Map<Class<?>, ArgumentParser<?>> parsers = new HashMap<>();
private final Set<CommandFactory> factories = new HashSet<>();

private CommandManager() {
parsers.putAll(ArgumentParser.defaultParsers);
registerFactory(new DSLFactory());
registerFactory(new AnnotationCommandFactory());
registerFactory(new BuilderFactory());
}

public static CommandDSL dsl(String... aliases) {
return new CommandDSL(INSTANCE.parsers, aliases);
}

public static CommandBuilder builder(String... aliases) {
return new CommandBuilder(INSTANCE.parsers, aliases);
}

public static boolean registerCommand(Object obj) {
return INSTANCE.create(obj);
}

/**
* Register a factory which can be used to create commands from objects in the {@link #create(Object)} method.
Expand All @@ -80,13 +99,6 @@ public void registerParser(ArgumentParser<?>... parsers) {
}
}

private CommandManager() {
parsers.putAll(ArgumentParser.defaultParsers);
registerFactory(new DSLFactory());
registerFactory(new AnnotationCommandFactory());
registerFactory(new BuilderFactory());
}

/**
* Create a command from the provided object.
* <br>
Expand All @@ -98,18 +110,6 @@ public boolean create(Object obj) {
return createTree(obj) != null;
}

public static CommandDSL dsl(String... aliases) {
return new CommandDSL(INSTANCE.parsers, aliases);
}

public static CommandBuilder builder(String... aliases) {
return new CommandBuilder(INSTANCE.parsers, aliases);
}

public static boolean registerCommand(Object obj) {
return INSTANCE.create(obj);
}

/**
* Create a command from the given object
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,8 @@
@SuppressWarnings("unused")
public class CommandTree implements Node {
final String[] names;
public String description;

private final Map<String, List<Node>> commands = new HashMap<>();
public String description;
private Map<String, List<Node>> dedupedCommands = null;
private String[] help = null;
private boolean init = false;
Expand All @@ -64,6 +63,46 @@ public CommandTree(@NotNull String[] names, @Nullable String description) {
this.description = description;
}

/**
* Return a copy of in with all elements that are in any of the lists removed.
* <br>both lists and in are not modified.
*/
@Contract(pure = true)
static <T> @NotNull List<T> cullList(@NotNull Iterable<List<T>> lists, @NotNull List<T> in) {
List<T> out = new ArrayList<>(1);
for (T obj : in) {
boolean found = false;
for (List<T> list : lists) {
if (list.contains(obj)) {
found = true;
break;
}
}
if (!found) out.add(obj);
}
return out;
}

public static void append(StringBuilder sb, String toAppend, int amount) {
for (int i = 0; i < amount; i++) {
sb.append(toAppend);
}
}

static <T> boolean contains(T[] array, T item) {
for (T x : array) {
if (x.equals(item)) return true;
}
return false;
}

static String[] withEmpty(String[] current) {
String[] out = new String[current.length + 1];
System.arraycopy(current, 0, out, 0, current.length);
out[current.length] = "";
return out;
}

public void setDescription(String description) {
this.description = description;
if (init) {
Expand All @@ -80,7 +119,6 @@ public boolean isInitialized() {
return init;
}


private void put(String key, Executable node) {
if (init) throw new CommandCreationException("Cannot add executables after initialization!");
if (key.equals("main")) {
Expand Down Expand Up @@ -161,26 +199,6 @@ public Map<String, List<Node>> getDedupedCommands() {
return dedupedCommands;
}

/**
* Return a copy of in with all elements that are in any of the lists removed.
* <br>both lists and in are not modified.
*/
@Contract(pure = true)
static <T> @NotNull List<T> cullList(@NotNull Iterable<List<T>> lists, @NotNull List<T> in) {
List<T> out = new ArrayList<>(1);
for (T obj : in) {
boolean found = false;
for (List<T> list : lists) {
if (list.contains(obj)) {
found = true;
break;
}
}
if (!found) out.add(obj);
}
return out;
}

@NotNull
public String[] getHelp() {
if (help == null) {
Expand Down Expand Up @@ -238,12 +256,6 @@ private void fullString(CommandTree it, int depth, StringBuilder sb, boolean isH
});
}

public static void append(StringBuilder sb, String toAppend, int amount) {
for (int i = 0; i < amount; i++) {
sb.append(toAppend);
}
}

public String helpString() {
return String.join(", ", names) + (description == null ? "" : ": " + description);
}
Expand Down Expand Up @@ -430,20 +442,6 @@ public List<String> autocomplete(String... current) {
}
}

static <T> boolean contains(T[] array, T item) {
for (T x : array) {
if (x.equals(item)) return true;
}
return false;
}

static String[] withEmpty(String[] current) {
String[] out = new String[current.length + 1];
System.arraycopy(current, 0, out, 0, current.length);
out[current.length] = "";
return out;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,6 @@
import java.util.function.Function;

public class Executable implements Node {
public final String[] names;
public final String description;
public final Param[] parameters;
public final int arity;
public final Function<Object[], Object> function;
public final boolean isGreedy;
@Unmodifiable
public static final Map<Class<?>, Class<?>> primitiveWrappers;

Expand All @@ -65,6 +59,13 @@ public class Executable implements Node {
primitiveWrappers = Collections.unmodifiableMap(m);
}

public final String[] names;
public final String description;
public final Param[] parameters;
public final int arity;
public final Function<Object[], Object> function;
public final boolean isGreedy;

public Executable(@NotNull String[] names, @Nullable String description, @NotNull Param[] parameters, boolean isGreedy, @NotNull Function<Object[], Object> function) {
this.names = names;
this.description = description;
Expand All @@ -78,24 +79,6 @@ public Executable(@NotNull String[] names, @Nullable String description, @NotNul
this.arity = arity;
}

public Object execute(String... args) {
if (!isGreedy && args.length != arity) throw new CommandExecutionException("Invalid number of arguments!");
Object[] parsed = new Object[parameters.length];
if (arity == 0) return function.apply(parsed);
int offset = 0;
for (int i = 0; i < parameters.length; i++) {
Param p = parameters[i];
// if this is greedy, and we are doing last param, force the arity of the last parameter to be the remaining
// number of arguments so it consumes all of them
if (isGreedy && i == parameters.length - 1) {
p.arity = args.length - offset;
}
parsed[i] = unbox(p.parsed(offset, args));
offset += p.arity;
}
return function.apply(parsed);
}

/**
* Because casting arrays by default would be stupid!
*/
Expand Down Expand Up @@ -131,6 +114,24 @@ private static Object unbox(Object in) {
return out;
}

public Object execute(String... args) {
if (!isGreedy && args.length != arity) throw new CommandExecutionException("Invalid number of arguments!");
Object[] parsed = new Object[parameters.length];
if (arity == 0) return function.apply(parsed);
int offset = 0;
for (int i = 0; i < parameters.length; i++) {
Param p = parameters[i];
// if this is greedy, and we are doing last param, force the arity of the last parameter to be the remaining
// number of arguments so it consumes all of them
if (isGreedy && i == parameters.length - 1) {
p.arity = args.length - offset;
}
parsed[i] = unbox(p.parsed(offset, args));
offset += p.arity;
}
return function.apply(parsed);
}

public String[] names() {
return names;
}
Expand Down Expand Up @@ -185,6 +186,15 @@ public Param(@NotNull String name, @Nullable String description, int arity, @Not
this.arity = arity;
}

public static Param create(@NotNull String name, @Nullable String description, @NotNull Class<?> type, int arity, @NotNull Map<Class<?>, ArgumentParser<?>> parsers) {
if (type.isArray()) type = type.getComponentType();
ArgumentParser<?> parser = parsers.get(primitiveWrappers.getOrDefault(type, type));
if (parser == null) {
throw new CommandCreationException("No parser found for type " + type.getSimpleName() + "! Register with CommandManager#registerParser");
}
return new Param(name, description, arity, parser);
}

Object parsed(int offset, String[] args) {
if (arity == 1) return parser.parse(args[offset]);
Object first = parser.parse(args[offset]);
Expand All @@ -204,7 +214,8 @@ Object parsedOrNull(int offset, String[] args) {
}
}

@Nullable List<@NotNull String> tryAutoComplete(String arg) {
@Nullable
List<@NotNull String> tryAutoComplete(String arg) {
return parser.getAutoCompletions(arg);
}

Expand All @@ -219,15 +230,6 @@ public String toString() {
public Class<?> getType() {
return parser.getType();
}

public static Param create(@NotNull String name, @Nullable String description, @NotNull Class<?> type, int arity, @NotNull Map<Class<?>, ArgumentParser<?>> parsers) {
if (type.isArray()) type = type.getComponentType();
ArgumentParser<?> parser = parsers.get(primitiveWrappers.getOrDefault(type, type));
if (parser == null) {
throw new CommandCreationException("No parser found for type " + type.getSimpleName() + "! Register with CommandManager#registerParser");
}
return new Param(name, description, arity, parser);
}
}
}

Expand Down
Loading

0 comments on commit a0b92cf

Please sign in to comment.