Skip to content

Instantly share code, notes, and snippets.

@Revxrsal
Created August 6, 2024 22:44
Show Gist options
  • Save Revxrsal/aa6c4db28e354a6d44b1f5de313847ed to your computer and use it in GitHub Desktop.
Save Revxrsal/aa6c4db28e354a6d44b1f5de313847ed to your computer and use it in GitHub Desktop.
package revxrsal.commands.parameters.builtins;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import revxrsal.commands.annotation.AnnotationList;
import revxrsal.commands.Lamp;
import revxrsal.commands.annotation.Delimiter;
import revxrsal.commands.annotation.Sized;
import revxrsal.commands.command.CommandActor;
import revxrsal.commands.node.ExecutionContext;
import revxrsal.commands.parameters.PrioritySpec;
import revxrsal.commands.resolver.ParameterResolver;
import revxrsal.commands.resolver.ParameterType;
import revxrsal.commands.stream.MutableStringStream;
import revxrsal.commands.util.Classes;
import java.lang.reflect.Array;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import static revxrsal.commands.util.Classes.getRawType;
public final class ArrayParameterTypeFactory implements ParameterType.Factory<CommandActor> {
@Override
@SuppressWarnings({"unchecked", "rawtypes"})
public @Nullable <T> ParameterType<CommandActor, T> create(@NotNull Type parameterType, @NotNull AnnotationList annotations, @NotNull Lamp<CommandActor> lamp) {
Type elementType = Classes.arrayComponentType(parameterType);
if (elementType == null) return null;
@NotNull ParameterResolver<CommandActor, Object> componentType = lamp.resolver(elementType);
if (!(componentType instanceof ParameterType))
throw new IllegalArgumentException("Received a ContextParameter for the array type: " + elementType);
Sized sized = annotations.get(Sized.class);
int min = 0, max = Integer.MAX_VALUE;
if (sized != null) {
min = sized.min();
max = sized.max();
//noinspection ConstantValue
if (min < 0 || max < 0 || max < min)
throw new IllegalArgumentException("Illegal range input in @Sized");
}
char delimiter = annotations.mapOr(Delimiter.class, Delimiter::value, ' ');
return new ArrayParameterType<>(delimiter, min, max, ((ParameterType) componentType), getRawType(elementType));
}
static class ArrayParameterType<A extends CommandActor, T> implements ParameterType<A, Object> {
private final char delimiter;
private final int minSize, maxSize;
private final ParameterType<A, T> componentType;
private final Class<?> elementType;
private final PrioritySpec priority;
@SuppressWarnings({"rawtypes", "unchecked"})
public ArrayParameterType(char delimiter, int minSize, int maxSize, ParameterType<A, T> componentType, Class<?> elementType) {
this.delimiter = delimiter;
this.minSize = minSize;
this.maxSize = maxSize;
this.componentType = componentType;
this.elementType = elementType;
priority = componentType.parsePriority().toBuilder()
.lowerThan(((Class) componentType.getClass()))
.build();
}
@Override
public Object parse(@NotNull MutableStringStream input, @NotNull ExecutionContext<A> context) {
List<T> elements = new ArrayList<>();
while (input.hasRemaining()) {
if (elements.size() >= maxSize)
break;
// we really shouldn't cast here, but I'll see if we can do better.
T el = componentType.parse(input, context);
elements.add(el);
if (input.hasRemaining()) {
if (input.peek() == delimiter)
input.moveForward();
else
throw new IllegalArgumentException("An element in the array has unconsumed input left: " + input.peek());
}
}
if (elements.size() < minSize)
throw new IllegalArgumentException("You must input at least " + minSize + " entries");
Object arr = Array.newInstance(elementType, elements.size());
for (int i = 0; i < elements.size(); i++)
Array.set(arr, i, elements.get(i));
return arr;
}
@Override
public @NotNull PrioritySpec parsePriority() {
return priority;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment