Readded PlutoCommandParser
This commit is contained in:
parent
b476087fcd
commit
c3bd358bed
|
@ -0,0 +1,9 @@
|
||||||
|
apply plugin: 'java-library'
|
||||||
|
|
||||||
|
description = ""
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
api project(":plutolib")
|
||||||
|
|
||||||
|
testImplementation("org.junit.jupiter:junit-jupiter:5.6.2")
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package cz.tefek.pluto.command;
|
||||||
|
|
||||||
|
public abstract class CommandBase
|
||||||
|
{
|
||||||
|
public abstract String name();
|
||||||
|
|
||||||
|
public abstract String[] aliases();
|
||||||
|
|
||||||
|
public abstract String description();
|
||||||
|
|
||||||
|
public abstract Class<?> commandClass();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final int hashCode()
|
||||||
|
{
|
||||||
|
return this.name().hashCode();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
package cz.tefek.pluto.command.context;
|
||||||
|
|
||||||
|
import cz.tefek.pluto.command.CommandBase;
|
||||||
|
|
||||||
|
public class CommandContextBuilder
|
||||||
|
{
|
||||||
|
private CommandContext ctx;
|
||||||
|
|
||||||
|
public CommandContextBuilder()
|
||||||
|
{
|
||||||
|
this.ctx = new CommandContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandContextBuilder prefix(String prefix)
|
||||||
|
{
|
||||||
|
this.ctx.usedPrefix = prefix;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandContextBuilder alias(String alias)
|
||||||
|
{
|
||||||
|
this.ctx.usedAlias = alias;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandContextBuilder command(CommandBase command)
|
||||||
|
{
|
||||||
|
this.ctx.command = command;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandContext resolved()
|
||||||
|
{
|
||||||
|
this.ctx.resolved = true;
|
||||||
|
|
||||||
|
return this.ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandContext unresolved(EnumCommandParseFailure cause)
|
||||||
|
{
|
||||||
|
this.ctx.resolved = false;
|
||||||
|
|
||||||
|
return this.ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class CommandContext
|
||||||
|
{
|
||||||
|
private boolean resolved;
|
||||||
|
private EnumCommandParseFailure failureCause;
|
||||||
|
|
||||||
|
private String usedPrefix;
|
||||||
|
private String usedAlias;
|
||||||
|
private CommandBase command;
|
||||||
|
|
||||||
|
private CommandContext()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUsedPrefix()
|
||||||
|
{
|
||||||
|
return this.usedPrefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUsedAlias()
|
||||||
|
{
|
||||||
|
return this.usedAlias;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CommandBase getCommand()
|
||||||
|
{
|
||||||
|
return this.command;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isResolved()
|
||||||
|
{
|
||||||
|
return this.resolved;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EnumCommandParseFailure getFailureCause()
|
||||||
|
{
|
||||||
|
return this.failureCause;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum EnumCommandParseFailure
|
||||||
|
{
|
||||||
|
UNRESOLVED_PREFIX,
|
||||||
|
UNRESOLVED_COMMAND_NAME,
|
||||||
|
UNRESOLVED_PARAMETERS,
|
||||||
|
UNRESOLVED_UNEXPECTED_STATE;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,252 @@
|
||||||
|
package cz.tefek.pluto.command.parser;
|
||||||
|
|
||||||
|
import java.util.PrimitiveIterator.OfInt;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
|
import cz.tefek.pluto.command.CommandBase;
|
||||||
|
import cz.tefek.pluto.command.context.CommandContextBuilder;
|
||||||
|
import cz.tefek.pluto.command.context.CommandContextBuilder.CommandContext;
|
||||||
|
import cz.tefek.pluto.command.context.CommandContextBuilder.EnumCommandParseFailure;
|
||||||
|
import cz.tefek.pluto.command.registry.CommandRegistry;
|
||||||
|
|
||||||
|
public class CommandParser
|
||||||
|
{
|
||||||
|
private String text;
|
||||||
|
private Set<OfInt> prefixes;
|
||||||
|
private EnumParserState state;
|
||||||
|
|
||||||
|
private StringBuilder prefixBuilder;
|
||||||
|
private StringBuilder commandNameBuilder;
|
||||||
|
|
||||||
|
private CommandBase command;
|
||||||
|
|
||||||
|
private StringBuilder parameterBuilder;
|
||||||
|
|
||||||
|
private CommandContextBuilder ctx;
|
||||||
|
|
||||||
|
private static final int CP_QUOTE = '"';
|
||||||
|
|
||||||
|
public CommandParser(String text)
|
||||||
|
{
|
||||||
|
this.text = text;
|
||||||
|
this.state = EnumParserState.BEGIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean readCodepoint(int cp)
|
||||||
|
{
|
||||||
|
switch (this.state)
|
||||||
|
{
|
||||||
|
case READING_PREFIX:
|
||||||
|
this.prefixBuilder.appendCodePoint(cp);
|
||||||
|
|
||||||
|
this.prefixes.removeIf(ii -> ii.nextInt() != cp);
|
||||||
|
|
||||||
|
if (this.prefixes.isEmpty())
|
||||||
|
{
|
||||||
|
this.state = EnumParserState.END_NO_PREFIX;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.hasEmptyPrefix())
|
||||||
|
{
|
||||||
|
this.ctx.prefix(this.prefixBuilder.toString());
|
||||||
|
this.state = EnumParserState.READING_COMMAND;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case READING_COMMAND:
|
||||||
|
this.commandNameBuilder.appendCodePoint(cp);
|
||||||
|
|
||||||
|
if (Character.isWhitespace(cp))
|
||||||
|
{
|
||||||
|
if (!this.resolveCommand())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.state = EnumParserState.READ_WHITESPACE;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case READ_WHITESPACE:
|
||||||
|
if (Character.isWhitespace(cp))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cp == CP_QUOTE)
|
||||||
|
{
|
||||||
|
this.state = EnumParserState.READING_PARAMETER_QUOTED;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.parameterBuilder.appendCodePoint(cp);
|
||||||
|
this.state = EnumParserState.READING_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case READING_PARAMETER_QUOTED:
|
||||||
|
if (cp == CP_QUOTE)
|
||||||
|
{
|
||||||
|
this.state = EnumParserState.READING_PARAMETER_CANDIDATE_UNQUOTE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.parameterBuilder.appendCodePoint(cp);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case READING_PARAMETER:
|
||||||
|
if (Character.isWhitespace(cp))
|
||||||
|
{
|
||||||
|
this.emitParameter();
|
||||||
|
this.state = EnumParserState.READ_WHITESPACE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.parameterBuilder.appendCodePoint(cp);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case READING_PARAMETER_CANDIDATE_UNQUOTE:
|
||||||
|
if (Character.isWhitespace(cp))
|
||||||
|
{
|
||||||
|
this.emitParameter();
|
||||||
|
this.state = EnumParserState.READ_WHITESPACE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.parameterBuilder.appendCodePoint(cp);
|
||||||
|
this.state = EnumParserState.READING_PARAMETER_QUOTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case END:
|
||||||
|
this.state = EnumParserState.UNEXPECTED_STATE_FALLBACK;
|
||||||
|
return false;
|
||||||
|
|
||||||
|
default:
|
||||||
|
this.state = EnumParserState.UNEXPECTED_STATE_FALLBACK;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean resolveCommand()
|
||||||
|
{
|
||||||
|
var alias = this.commandNameBuilder.toString();
|
||||||
|
|
||||||
|
this.ctx.alias(alias);
|
||||||
|
|
||||||
|
this.command = CommandRegistry.getByAlias(alias);
|
||||||
|
|
||||||
|
if (this.command == null)
|
||||||
|
{
|
||||||
|
this.state = EnumParserState.END_NO_COMMAND;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void emitParameter()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean hasEmptyPrefix()
|
||||||
|
{
|
||||||
|
return this.prefixes.stream().anyMatch(Predicate.not(OfInt::hasNext));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse using this parser and supplied prefixes. This function also
|
||||||
|
* resolves the command context and the parameters. Yeah it does a lot of
|
||||||
|
* stuff.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public CommandContext parse(Set<String> prefixes)
|
||||||
|
{
|
||||||
|
if (this.state != EnumParserState.BEGIN)
|
||||||
|
{
|
||||||
|
throw new IllegalStateException("Cannot run a parser that is not in the BEGIN state.");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.prefixBuilder = new StringBuilder();
|
||||||
|
this.ctx = new CommandContextBuilder();
|
||||||
|
|
||||||
|
this.prefixes = prefixes.stream().map(String::codePoints).map(IntStream::iterator).collect(Collectors.toSet());
|
||||||
|
|
||||||
|
if (prefixes.isEmpty() || this.hasEmptyPrefix())
|
||||||
|
{
|
||||||
|
this.state = EnumParserState.READING_COMMAND;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.state = EnumParserState.READING_PREFIX;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.text.codePoints().takeWhile(this::readCodepoint);
|
||||||
|
|
||||||
|
// Update the state for EOF
|
||||||
|
switch (this.state)
|
||||||
|
{
|
||||||
|
case READING_PARAMETER_QUOTED:
|
||||||
|
case READ_WHITESPACE:
|
||||||
|
case READING_PARAMETER:
|
||||||
|
case READING_PARAMETER_CANDIDATE_UNQUOTE:
|
||||||
|
this.state = EnumParserState.END;
|
||||||
|
this.emitParameter();
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case READING_COMMAND:
|
||||||
|
if (this.resolveCommand())
|
||||||
|
{
|
||||||
|
this.state = EnumParserState.END;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the end state
|
||||||
|
switch (this.state)
|
||||||
|
{
|
||||||
|
case READING_PREFIX:
|
||||||
|
case END_NO_PREFIX:
|
||||||
|
return this.ctx.unresolved(EnumCommandParseFailure.UNRESOLVED_PREFIX);
|
||||||
|
|
||||||
|
case END_NO_COMMAND:
|
||||||
|
return this.ctx.unresolved(EnumCommandParseFailure.UNRESOLVED_COMMAND_NAME);
|
||||||
|
|
||||||
|
case END:
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return this.ctx.unresolved(EnumCommandParseFailure.UNRESOLVED_UNEXPECTED_STATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// At this point we are 100% sure the command was resolved and can validate the parameters
|
||||||
|
|
||||||
|
/*
|
||||||
|
*
|
||||||
|
* TODO: Validate parameters here
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
return this.ctx.resolved();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package cz.tefek.pluto.command.parser;
|
||||||
|
|
||||||
|
public enum EnumParserState
|
||||||
|
{
|
||||||
|
BEGIN,
|
||||||
|
READING_PREFIX,
|
||||||
|
READING_COMMAND,
|
||||||
|
READING_PARAMETER,
|
||||||
|
READING_PARAMETER_QUOTED,
|
||||||
|
READING_PARAMETER_CANDIDATE_UNQUOTE,
|
||||||
|
READ_WHITESPACE,
|
||||||
|
END_NO_PREFIX,
|
||||||
|
END_NO_COMMAND,
|
||||||
|
END_EMISSION_FAILURE,
|
||||||
|
END,
|
||||||
|
UNEXPECTED_STATE_FALLBACK;
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package cz.tefek.pluto.command.platform;
|
||||||
|
|
||||||
|
import cz.tefek.pluto.command.context.CommandContextBuilder.EnumCommandParseFailure;
|
||||||
|
|
||||||
|
public abstract class CommandPlatform
|
||||||
|
{
|
||||||
|
public abstract String getID();
|
||||||
|
|
||||||
|
public abstract String getName();
|
||||||
|
|
||||||
|
public boolean shouldWarnOn(EnumCommandParseFailure failure)
|
||||||
|
{
|
||||||
|
switch (failure)
|
||||||
|
{
|
||||||
|
case UNRESOLVED_PREFIX:
|
||||||
|
case UNRESOLVED_COMMAND_NAME:
|
||||||
|
case UNRESOLVED_UNEXPECTED_STATE:
|
||||||
|
return false;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract int getMessageLimit();
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
package cz.tefek.pluto.command.registry;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import cz.tefek.pluto.command.CommandBase;
|
||||||
|
import cz.tefek.pluto.io.logger.Logger;
|
||||||
|
import cz.tefek.pluto.io.logger.SmartSeverity;
|
||||||
|
|
||||||
|
public final class CommandRegistry
|
||||||
|
{
|
||||||
|
private static CommandRegistry instance;
|
||||||
|
|
||||||
|
private final Set<CommandBase> commands;
|
||||||
|
private final Map<String, CommandBase> aliasTable;
|
||||||
|
|
||||||
|
static
|
||||||
|
{
|
||||||
|
instance = new CommandRegistry();
|
||||||
|
}
|
||||||
|
|
||||||
|
private CommandRegistry()
|
||||||
|
{
|
||||||
|
this.aliasTable = new HashMap<>();
|
||||||
|
this.commands = new HashSet<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void registerAlias(String alias, CommandBase command)
|
||||||
|
{
|
||||||
|
if (this.aliasTable.containsKey(alias))
|
||||||
|
{
|
||||||
|
Logger.logf(SmartSeverity.ERROR, "Alias '%s' for command '%s' is already used, skipping.%n", alias, command.name());
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.aliasTable.put(alias, command);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void registerCommand(CommandBase command)
|
||||||
|
{
|
||||||
|
if (!instance.commands.add(command))
|
||||||
|
{
|
||||||
|
Logger.logf(SmartSeverity.ERROR, "Command '%s' is already registered, skipping.%n", command.name());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
instance.registerAlias(command.name(), command);
|
||||||
|
|
||||||
|
Arrays.stream(command.aliases()).forEach(alias -> instance.registerAlias(alias, command));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CommandBase getByAlias(String alias)
|
||||||
|
{
|
||||||
|
return instance.aliasTable.get(alias);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Set<CommandBase> getCommands()
|
||||||
|
{
|
||||||
|
return Collections.unmodifiableSet(instance.commands);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void clear()
|
||||||
|
{
|
||||||
|
instance = new CommandRegistry();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
package cz.tefek.pluto.command.resolver;
|
||||||
|
|
||||||
|
public abstract class AbstractResolveException extends RuntimeException
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = -8934505669078637864L;
|
||||||
|
|
||||||
|
public AbstractResolveException(Exception exception)
|
||||||
|
{
|
||||||
|
super(exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AbstractResolveException(String what)
|
||||||
|
{
|
||||||
|
super(what);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected AbstractResolveException()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
package cz.tefek.pluto.command.resolver;
|
||||||
|
|
||||||
|
public abstract class AbstractResolver
|
||||||
|
{
|
||||||
|
public abstract Class<?> getOutputType();
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
package cz.tefek.pluto.command.resolver;
|
||||||
|
|
||||||
|
public enum EnumResolveFailure
|
||||||
|
{
|
||||||
|
INT_PARSE,
|
||||||
|
LONG_PARSE,
|
||||||
|
FLOAT_PARSE,
|
||||||
|
DOUBLE_PARSE,
|
||||||
|
FRAC_INVALID_PERCENTAGE,
|
||||||
|
FRAC_PERCENTAGE_OUT_OF_RANGE,
|
||||||
|
FRAC_VALUE_HIGHER_THAN_BASE,
|
||||||
|
OTHER;
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package cz.tefek.pluto.command.resolver;
|
||||||
|
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
public abstract class GenericResolver<R> extends AbstractResolver
|
||||||
|
{
|
||||||
|
protected Function<String, R> func;
|
||||||
|
|
||||||
|
public GenericResolver(Function<String, R> func)
|
||||||
|
{
|
||||||
|
this.func = func;
|
||||||
|
}
|
||||||
|
|
||||||
|
public R apply(String param)
|
||||||
|
{
|
||||||
|
return this.func.apply(param);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public abstract Class<R> getOutputType();
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package cz.tefek.pluto.command.resolver;
|
||||||
|
|
||||||
|
public class ResolveException extends AbstractResolveException
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
private static final long serialVersionUID = -3098373878754649161L;
|
||||||
|
|
||||||
|
private EnumResolveFailure failure;
|
||||||
|
|
||||||
|
public ResolveException(EnumResolveFailure failure)
|
||||||
|
{
|
||||||
|
this.failure = failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EnumResolveFailure getResolveFailure()
|
||||||
|
{
|
||||||
|
return this.failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
package cz.tefek.pluto.command.resolver.primitive;
|
||||||
|
|
||||||
|
import cz.tefek.pluto.command.resolver.EnumResolveFailure;
|
||||||
|
import cz.tefek.pluto.command.resolver.ResolveException;
|
||||||
|
|
||||||
|
public class BasicDoubleResolver extends DoubleResolver
|
||||||
|
{
|
||||||
|
public BasicDoubleResolver()
|
||||||
|
{
|
||||||
|
super(str ->
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return Double.parseDouble(str);
|
||||||
|
}
|
||||||
|
catch (NumberFormatException e)
|
||||||
|
{
|
||||||
|
throw new ResolveException(EnumResolveFailure.DOUBLE_PARSE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
package cz.tefek.pluto.command.resolver.primitive;
|
||||||
|
|
||||||
|
import cz.tefek.pluto.command.resolver.EnumResolveFailure;
|
||||||
|
import cz.tefek.pluto.command.resolver.ResolveException;
|
||||||
|
|
||||||
|
public class BasicIntResolver extends IntResolver
|
||||||
|
{
|
||||||
|
public BasicIntResolver()
|
||||||
|
{
|
||||||
|
super(str ->
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return Integer.parseInt(str);
|
||||||
|
}
|
||||||
|
catch (NumberFormatException e)
|
||||||
|
{
|
||||||
|
throw new ResolveException(EnumResolveFailure.INT_PARSE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
package cz.tefek.pluto.command.resolver.primitive;
|
||||||
|
|
||||||
|
import cz.tefek.pluto.command.resolver.EnumResolveFailure;
|
||||||
|
import cz.tefek.pluto.command.resolver.ResolveException;
|
||||||
|
|
||||||
|
public class BasicLongResolver extends LongResolver
|
||||||
|
{
|
||||||
|
public BasicLongResolver()
|
||||||
|
{
|
||||||
|
super(str ->
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return Long.parseLong(str);
|
||||||
|
}
|
||||||
|
catch (NumberFormatException e)
|
||||||
|
{
|
||||||
|
throw new ResolveException(EnumResolveFailure.LONG_PARSE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package cz.tefek.pluto.command.resolver.primitive;
|
||||||
|
|
||||||
|
import java.util.function.ToDoubleFunction;
|
||||||
|
|
||||||
|
import cz.tefek.pluto.command.resolver.AbstractResolver;
|
||||||
|
|
||||||
|
public class DoubleResolver extends AbstractResolver
|
||||||
|
{
|
||||||
|
protected ToDoubleFunction<String> func;
|
||||||
|
|
||||||
|
public DoubleResolver(ToDoubleFunction<String> func)
|
||||||
|
{
|
||||||
|
this.func = func;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double apply(String param)
|
||||||
|
{
|
||||||
|
return this.func.applyAsDouble(param);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<?> getOutputType()
|
||||||
|
{
|
||||||
|
return double.class;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
package cz.tefek.pluto.command.resolver.primitive;
|
||||||
|
|
||||||
|
import cz.tefek.pluto.command.resolver.EnumResolveFailure;
|
||||||
|
import cz.tefek.pluto.command.resolver.ResolveException;
|
||||||
|
|
||||||
|
public class IntFractionResolver extends IntResolver
|
||||||
|
{
|
||||||
|
public IntFractionResolver(int base)
|
||||||
|
{
|
||||||
|
super(str -> parseAmount(str, base));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int parseAmount(String amountString, int base) throws ResolveException
|
||||||
|
{
|
||||||
|
if (amountString.equalsIgnoreCase("all") || amountString.equalsIgnoreCase("everything"))
|
||||||
|
{
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (amountString.equalsIgnoreCase("half"))
|
||||||
|
{
|
||||||
|
return base / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (amountString.endsWith("%"))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
float percentage = Float.parseFloat(amountString.substring(0, amountString.length() - 1));
|
||||||
|
|
||||||
|
if (percentage < 0 || percentage > 100)
|
||||||
|
{
|
||||||
|
throw new ResolveException(EnumResolveFailure.FRAC_PERCENTAGE_OUT_OF_RANGE);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return Math.round(percentage / 100.0f * base);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (NumberFormatException e1)
|
||||||
|
{
|
||||||
|
throw new ResolveException(EnumResolveFailure.FRAC_INVALID_PERCENTAGE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int amount = Integer.parseInt(amountString);
|
||||||
|
|
||||||
|
if (amount > base)
|
||||||
|
{
|
||||||
|
throw new ResolveException(EnumResolveFailure.FRAC_VALUE_HIGHER_THAN_BASE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return amount;
|
||||||
|
}
|
||||||
|
catch (NumberFormatException e)
|
||||||
|
{
|
||||||
|
throw new ResolveException(EnumResolveFailure.INT_PARSE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package cz.tefek.pluto.command.resolver.primitive;
|
||||||
|
|
||||||
|
import java.util.function.ToIntFunction;
|
||||||
|
|
||||||
|
import cz.tefek.pluto.command.resolver.AbstractResolver;
|
||||||
|
|
||||||
|
public class IntResolver extends AbstractResolver
|
||||||
|
{
|
||||||
|
protected ToIntFunction<String> func;
|
||||||
|
|
||||||
|
public IntResolver(ToIntFunction<String> func)
|
||||||
|
{
|
||||||
|
this.func = func;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int apply(String param)
|
||||||
|
{
|
||||||
|
return this.func.applyAsInt(param);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final Class<?> getOutputType()
|
||||||
|
{
|
||||||
|
return int.class;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package cz.tefek.pluto.command.resolver.primitive;
|
||||||
|
|
||||||
|
import java.util.function.ToLongFunction;
|
||||||
|
|
||||||
|
import cz.tefek.pluto.command.resolver.AbstractResolver;
|
||||||
|
|
||||||
|
public class LongResolver extends AbstractResolver
|
||||||
|
{
|
||||||
|
protected ToLongFunction<String> func;
|
||||||
|
|
||||||
|
public LongResolver(ToLongFunction<String> func)
|
||||||
|
{
|
||||||
|
this.func = func;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long apply(String param)
|
||||||
|
{
|
||||||
|
return this.func.applyAsLong(param);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final Class<?> getOutputType()
|
||||||
|
{
|
||||||
|
return long.class;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
package cz.tefek.pluto.command.resolver;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Assertions;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import cz.tefek.pluto.command.resolver.primitive.BasicIntResolver;
|
||||||
|
|
||||||
|
class BasicIntResolverTest
|
||||||
|
{
|
||||||
|
@Test
|
||||||
|
void parse()
|
||||||
|
{
|
||||||
|
var resolver = new BasicIntResolver();
|
||||||
|
|
||||||
|
Assertions.assertEquals(5, resolver.apply("5"));
|
||||||
|
|
||||||
|
Assertions.assertEquals(-50, resolver.apply("-50"));
|
||||||
|
|
||||||
|
Assertions.assertEquals(155, resolver.apply("155"));
|
||||||
|
|
||||||
|
Assertions.assertEquals(0, resolver.apply("0"));
|
||||||
|
|
||||||
|
Assertions.assertEquals(-0, resolver.apply("-0"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void exceptions()
|
||||||
|
{
|
||||||
|
var resolver = new BasicIntResolver();
|
||||||
|
|
||||||
|
// No foreign characters
|
||||||
|
Assertions.assertThrows(NumberFormatException.class, () -> resolver.apply("abc"));
|
||||||
|
|
||||||
|
// No floats
|
||||||
|
Assertions.assertThrows(NumberFormatException.class, () -> resolver.apply("12.5"));
|
||||||
|
|
||||||
|
// No empty strings
|
||||||
|
Assertions.assertThrows(NumberFormatException.class, () -> resolver.apply(""));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
include 'plutolib',
|
include 'plutolib',
|
||||||
|
'plutocommandparser',
|
||||||
'plutostatic',
|
'plutostatic',
|
||||||
'plutotexturing',
|
'plutotexturing',
|
||||||
'plutomesher',
|
'plutomesher',
|
||||||
|
|
Loading…
Reference in New Issue