Skip to content

Commit

Permalink
Clarified #Width handling
Browse files Browse the repository at this point in the history
Added unit testing for width
  • Loading branch information
BreadMoirai committed Dec 12, 2017
1 parent 139d96e commit 4329e27
Show file tree
Hide file tree
Showing 10 changed files with 268 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -224,4 +224,6 @@ default CommandHandleBuilder configureParameter(int parameterIndex, Consumer<Com
* @return this
*/
CommandHandleBuilder setParameter(int parameterIndex, Function<CommandParser, ?> mapper);

List<CommandParameterBuilder> getParameters();
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,19 @@
import java.lang.reflect.Parameter;
import java.util.function.Consumer;

/**
* This is used to set settings for command parameters.
* <p>
* For guidance, refer to the
* <a href="https://github.com/BreadMoirai/BreadBotFramework/wiki/6)-Command-Parameters">
* <b>Github Wiki</b>: CommandParameters</a>
*/
public interface CommandParameterBuilder {

BreadBotClientBuilder getClientBuilder();

CommandHandleBuilder getCommandBuilder();

/**
* @return the {@link java.lang.reflect.Parameter}
*/
Expand All @@ -42,24 +53,29 @@ public interface CommandParameterBuilder {
/**
* Sets the flags to be passed to the {@link ArgumentTypeMapper}
*
* @param flags
* @param flags hints for the parser.
*/
CommandParameterBuilder setFlags(int flags);

/**
* If the index is known, the parser will only attempt to map the argument at the specified position.
* If number of arguments is less than the index The default value of {@code -1}
* If {@code index >= 0}, the parser will only attempt to map the argument at the specified position.
* If the position is out of bounds, this parameter will be passed {@code null}.
* If the width is not 1, the first argument consumed will always be at this index.
* If the width is positive and there are not enough arguments to satisfy that width starting from this index, {@code null} will be passed.
*
* @param index
* @return
* @param index the starting index of the argument to consume starting at 0.
* @return this builder instance
*/
CommandParameterBuilder setIndex(int index);

/**
* Sets the width of the argument, how many tokens the parse should consume.
*
* @param width
* @return
* By default this is set to {@code 1}, meaning it will only ever consume 1 argument or 0 if it cannot match anything.
* If the width is set to {@code 0}, it will try every contiguous combination of arguments starting from the largest size.
* If the width is set to a negative value, it will attempt each group of contiguous arguments.
* If the width is set to a positive value greater that {@code 1}, it will try every group of contiguous arguments of that exact size.
* @param width the number of arguments to consume.
* @return this builder instance
*/
CommandParameterBuilder setWidth(int width);

Expand All @@ -74,14 +90,32 @@ public interface CommandParameterBuilder {
* Sets the {@link ArgumentTypeMapper} to be used in mapping the {@link CommandParameter}.
* If an {@link ArgumentTypeMapper} is registered in {@link CommandParameterTypeManagerImpl}, it will not be used.
* The provided {@link ArgumentTypeMapper} will not be registered with {@link CommandParameterTypeManagerImpl}.
*
* This should only be used on parameters of the supported type which are as follows:
* <ul>
* <li>{@link CommandArgument}</li>
* <li>{@link java.util.List}</li>
* <li>{@link java.util.Queue}</li>
* <li>{@link java.util.Deque}</li>
* <li>{@link java.util.stream.Stream}</li>
* </ul>
*
* It is generally recommended to prefer using different {@link ArgumentFlags flags} on custom types to indicate that the {@link CommandArgument} should be mapped differently.
*
* @param type This parameter type
* @param mapper a public class that implements {@link ArgumentTypeMapper} and contains a no-args public constructor.
*/
default <T> CommandParameterBuilder setBaseType(Class<T> type, ArgumentTypeMapper<T> mapper) {
return setBaseType(type, null, mapper);
}

/**
* @param type
* @param predicate
* @param mapper
* @param <T>
* @return
*/
<T> CommandParameterBuilder setBaseType(Class<T> type, ArgumentTypePredicate predicate, ArgumentTypeMapper<T> mapper);

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,18 +63,21 @@ public CommandHandleBuilderFactoryImpl(BreadBotClientBuilder clientBuilder) {

@Override
public CommandHandleBuilderInternal createCommand(Consumer<CommandEvent> onCommand) {
return new CommandHandleBuilderImpl(
CommandParameterBuilder[] parameterBuilders = new CommandParameterBuilder[1];
CommandHandleBuilderImpl commandHandleBuilder = new CommandHandleBuilderImpl(
onCommand,
onCommand.getClass(),
null,
clientBuilder,
new CommandObjectFactory(() -> onCommand),
new CommandParameterBuilder[]{new CommandParameterFunctionBuilderImpl(null, "This parameter of type CommandEvent is inconfigurable", CommandParser::getEvent)},
parameterBuilders,
(o, objects) -> {
onCommand.accept(((CommandEvent) objects[0]));
return null;
},
new CommandPropertyMapImpl(CommandPackageProperties.getPropertiesForPackage(onCommand.getClass().getPackage())));
parameterBuilders[0] = new CommandParameterFunctionBuilderImpl(clientBuilder, commandHandleBuilder, null, "This parameter of type CommandEvent is inconfigurable", CommandParser::getEvent);
return commandHandleBuilder;
}

@Override
Expand Down Expand Up @@ -286,10 +289,10 @@ private Method getMainMethod(Class<?> commandClass, boolean mustBePresent) {
}

public CommandHandleBuilderInternal createHandleFromMethod(Object obj, Class<?> commandClass, Method method, CommandObjectFactory objectFactory, CommandPropertyMapImpl map) {
final CommandParameterBuilderFactory parameterFactory = new CommandParameterBuilderFactory(clientBuilder, map, method.getName());

final Parameter[] parameters = method.getParameters();
final CommandParameterBuilder[] parameterBuilders = new CommandParameterBuilder[parameters.length];
Arrays.setAll(parameterBuilders, value -> parameterFactory.builder(parameters[value]));

final MethodHandle handle;
try {
handle = MethodHandles.publicLookup().unreflect(method);
Expand All @@ -304,7 +307,12 @@ public CommandHandleBuilderInternal createHandleFromMethod(Object obj, Class<?>
} else {
factory = getSupplierForClass(method.getDeclaringClass());
}
return new CommandHandleBuilderImpl(obj, commandClass, method, clientBuilder, factory, parameterBuilders, invokableCommandHandle, map);

CommandHandleBuilderImpl commandHandleBuilder = new CommandHandleBuilderImpl(obj, commandClass, method, clientBuilder, factory, parameterBuilders, invokableCommandHandle, map);

final CommandParameterBuilderFactory parameterFactory = new CommandParameterBuilderFactory(clientBuilder, commandHandleBuilder, map, method.getName());
Arrays.setAll(parameterBuilders, value -> parameterFactory.builder(parameters[value]));
return commandHandleBuilder;
}

private CommandObjectFactory getSupplierForClass(Class<?> klass) throws BreadBotException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ public CommandParameterBuilder getParameter(int parameterIndex) {
public CommandHandleBuilder setParameter(int parameterIndex, Function<CommandParser, ?> mapper) {
Checks.notNull(mapper, "mapper");
final CommandParameterBuilder parameter = parameterBuilders[parameterIndex];
parameterBuilders[parameterIndex] = new CommandParameterFunctionBuilderImpl(parameter.getDeclaringParameter(), "You have defined this parameter with a function. Further modifications are useless.", mapper);
parameterBuilders[parameterIndex] = new CommandParameterFunctionBuilderImpl(getClientBuilder(), this, parameter.getDeclaringParameter(), "You have defined this parameter with a function. Further modifications are useless.", mapper);
return this;
}

Expand Down Expand Up @@ -289,6 +289,11 @@ public CommandHandleBuilder addCommand(Supplier<?> commandSupplier) {
return this;
}

@Override
public List<CommandParameterBuilder> getParameters() {
return Collections.unmodifiableList(Arrays.asList(parameterBuilders));
}

@Override
public CommandHandleImpl build(CommandHandle parent) {
if (keys == null || keys.length == 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,36 +56,60 @@ public Object map(CommandArgumentList list, CommandParser parser) {

for (; i < limit; i++) {
if (parser.hasMappedArgument(i)) continue;
int[] indexes;
final CommandArgument commandArgument;
if (width == 1) {
indexes = new int[]{i};
commandArgument = list.get(i);
} else if (width < 1) {
Object o = map(parser, list.get(i), new int[]{i});
if (o != null) {
return o;
}
} else if (width == 0) {
int j = i;
for (; j < list.size(); j++) {
if (parser.hasMappedArgument(j)) {
break;
}
}
for (; j > i; j--) {
StringJoiner sj = new StringJoiner(" ");
for (int k = i; k < j; k++) {
sj.add(list.get(k).getArgument());
}
Object o = map(parser, sj.toString(), IntStream.range(i, j).toArray());
if (o != null) {
return o;
}
}

} else if (width < 0) {
final StringJoiner sj = new StringJoiner(" ");
int j = i;
for (; j < list.size(); j++) {
if (parser.hasMappedArgument(j)) break;
sj.add(list.get(j).getArgument());
}
indexes = IntStream.range(i, j).toArray();
commandArgument = new GenericCommandArgument(parser.getEvent(), sj.toString());
int[] indexes = IntStream.range(i, j).toArray();
i = j;
Object o = map(parser, sj.toString(), indexes);
if (o != null) {
return o;
}
} else {
final StringJoiner sj = new StringJoiner(" ");
if (i + width >= list.size())
if (i + width > list.size())
continue;
for (int j = i; j < i + width && j < list.size(); j++) {
if (parser.hasMappedArgument(i)) break;
sj.add(list.get(i).getArgument());
int j = i;
for (; j < i + width; j++) {
if (parser.hasMappedArgument(j)) break;
sj.add(list.get(j).getArgument());
}
if (j == i + width) {
int[] indexes = IntStream.range(i, i + width).toArray();
Object o = map(parser, sj.toString(), indexes);
if (o != null) {
return o;
}
}
indexes = IntStream.range(i, i + width).toArray();
commandArgument = new GenericCommandArgument(parser.getEvent(), sj.toString());
}
final Object o = mapper.map(commandArgument, flags);
if (o != null) {
parser.markMappedArguments(indexes);
return o;
}

}

if (mustBePresent) parser.fail();
Expand Down Expand Up @@ -119,4 +143,17 @@ public boolean isMustBePresent() {
return mustBePresent;
}

private Object map(CommandParser parser, String arg, int[] indexes) {
return map(parser, new GenericCommandArgument(parser.getEvent(), arg), indexes);
}

private Object map(CommandParser parser, CommandArgument arg, int[] indexes) {
Object o = mapper.map(arg, flags);
if (o != null) {
parser.markMappedArguments(indexes);
return o;
}
return o;

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,31 +19,34 @@
import com.github.breadmoirai.breadbot.framework.CommandEvent;
import com.github.breadmoirai.breadbot.framework.CommandModule;
import com.github.breadmoirai.breadbot.framework.builder.BreadBotClientBuilder;
import com.github.breadmoirai.breadbot.framework.builder.CommandHandleBuilder;
import com.github.breadmoirai.breadbot.framework.builder.CommandParameterBuilder;
import com.github.breadmoirai.breadbot.framework.command.CommandPropertyMap;
import com.github.breadmoirai.breadbot.framework.parameter.CommandParser;

import java.lang.reflect.Parameter;

public class CommandParameterBuilderFactory {
private BreadBotClientBuilder clientBuilder;
private final BreadBotClientBuilder clientBuilder;
private final CommandHandleBuilder handleBuilder;
private final CommandPropertyMap map;
private final String methodName;

public CommandParameterBuilderFactory(BreadBotClientBuilder clientBuilder, CommandPropertyMap map, String methodName) {
public CommandParameterBuilderFactory(BreadBotClientBuilder clientBuilder, CommandHandleBuilder handleBuilder, CommandPropertyMap map, String methodName) {
this.clientBuilder = clientBuilder;
this.handleBuilder = handleBuilder;
this.map = map;
this.methodName = methodName;
}

public CommandParameterBuilder builder(Parameter parameter) {
final Class<?> type = parameter.getType();
if (type == CommandEvent.class) {
return new CommandParameterFunctionBuilderImpl(parameter, "This parameter of type CommandEvent is inconfigurable", CommandParser::getEvent);
return new CommandParameterFunctionBuilderImpl(clientBuilder, handleBuilder, parameter, "This parameter of type CommandEvent is inconfigurable", CommandParser::getEvent);
} else if (CommandModule.class.isAssignableFrom(type)) {
return new CommandParameterFunctionBuilderImpl(parameter, "This parameter of type " + type.getSimpleName() + " is inconfigurable", (commandParser) -> commandParser.getEvent().getClient().getModule(type));
return new CommandParameterFunctionBuilderImpl(clientBuilder, handleBuilder, parameter, "This parameter of type " + type.getSimpleName() + " is inconfigurable", (commandParser) -> commandParser.getEvent().getClient().getModule(type));
} else {
CommandParameterBuilderImpl param = new CommandParameterBuilderImpl(clientBuilder, parameter, methodName, map);
CommandParameterBuilderImpl param = new CommandParameterBuilderImpl(clientBuilder, handleBuilder, parameter, methodName, map);
clientBuilder.applyModifiers(param);
return param;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package com.github.breadmoirai.breadbot.framework.internal.parameter.builder;

import com.github.breadmoirai.breadbot.framework.builder.BreadBotClientBuilder;
import com.github.breadmoirai.breadbot.framework.builder.CommandHandleBuilder;
import com.github.breadmoirai.breadbot.framework.builder.CommandParameterBuilder;
import com.github.breadmoirai.breadbot.framework.command.CommandPropertyMap;
import com.github.breadmoirai.breadbot.framework.error.MissingTypeMapperException;
Expand All @@ -38,6 +39,7 @@
import java.util.stream.Stream;

public class CommandParameterBuilderImpl implements CommandParameterBuilder {
private final CommandHandleBuilder commandBuilder;
private final Parameter parameter;
private String methodName;
private final CommandPropertyMapImpl map;
Expand All @@ -52,7 +54,8 @@ public class CommandParameterBuilderImpl implements CommandParameterBuilder {
private AbsentArgumentHandler absentArgumentHandler = null;
private final BreadBotClientBuilder clientBuilder;

public CommandParameterBuilderImpl(BreadBotClientBuilder builder, Parameter parameter, String methodName, CommandPropertyMap map) {
public CommandParameterBuilderImpl(BreadBotClientBuilder builder, CommandHandleBuilder commandBuilder, Parameter parameter, String methodName, CommandPropertyMap map) {
this.commandBuilder = commandBuilder;
this.parameter = parameter;
this.map = new CommandPropertyMapImpl(map, parameter.getAnnotations());
this.paramName = parameter.getName();
Expand Down Expand Up @@ -204,6 +207,16 @@ public CommandParameterBuilder configure(Consumer<CommandParameterBuilder> confi
return this;
}

@Override
public BreadBotClientBuilder getClientBuilder() {
return clientBuilder;
}

@Override
public CommandHandleBuilder getCommandBuilder() {
return commandBuilder;
}

@Override
public Parameter getDeclaringParameter() {
return parameter;
Expand Down
Loading

0 comments on commit 4329e27

Please sign in to comment.