semi-auto cleanup

This commit is contained in:
gnembon committed 2023-02-04 13:26:21 +01:00
1 parent cad80f82c6
commit 8f61271606
99 files changed
+11463 -8007

No files matched your search

@@ -105,32 +105,38 @@ public final class AnnotationParser
* @see ScarpetFunction
* @param clazz The class to parse
*/
public static void parseFunctionClass(Class<?> clazz)
public static void parseFunctionClass(final Class<?> clazz)
{
// Only try to instantiate or require concrete classes if there are non-static annotated methods
Supplier<Object> instanceSupplier = Suppliers.memoize(() -> {
final Supplier<Object> instanceSupplier = Suppliers.memoize(() -> {
if (Modifier.isAbstract(clazz.getModifiers()))
{
throw new IllegalArgumentException("Function class must be concrete to support non-static methods! Class: " + clazz.getSimpleName());
}
try
{
return clazz.getConstructor().newInstance();
}
catch (ReflectiveOperationException e)
catch (final ReflectiveOperationException e)
{
throw new IllegalArgumentException(
"Couldn't create instance of given " + clazz + ". This is needed for non-static methods. Make sure default constructor is available", e);
}
});
Method[] methodz = clazz.getDeclaredMethods();
for (Method method : methodz)
final Method[] methodz = clazz.getDeclaredMethods();
for (final Method method : methodz)
{
if (!method.isAnnotationPresent(ScarpetFunction.class))
{
continue;
}
if (method.getExceptionTypes().length != 0)
{
throw new IllegalArgumentException("Annotated method '" + method.getName() +"', provided in '"+clazz+"' must not declare checked exceptions");
}
ParsedFunction function = new ParsedFunction(method, clazz, instanceSupplier);
final ParsedFunction function = new ParsedFunction(method, clazz, instanceSupplier);
functionList.add(function);
}
}
@@ -141,10 +147,12 @@ public final class AnnotationParser
*
* @param expr The expression to add every function to
*/
public static void apply(Expression expr)
public static void apply(final Expression expr)
{
for (ParsedFunction function : functionList)
for (final ParsedFunction function : functionList)
{
expr.addLazyFunction(function.name, function.scarpetParamCount, function);
}
}
private static class ParsedFunction implements TriFunction<Context, Context.Type, List<LazyValue>, LazyValue>, UsageProvider
@@ -164,25 +172,27 @@ public final class AnnotationParser
private final int scarpetParamCount;
private final Context.Type contextType;
private ParsedFunction(Method method, Class<?> originClass, Supplier<Object> instance)
private ParsedFunction(final Method method, final Class<?> originClass, final Supplier<Object> instance)
{
this.name = method.getName();
this.isMethodVarArgs = method.isVarArgs();
this.methodParamCount = method.getParameterCount();
Parameter[] methodParameters = method.getParameters();
final Parameter[] methodParameters = method.getParameters();
this.valueConverters = new ValueConverter[isMethodVarArgs ? methodParamCount - 1 : methodParamCount];
for (int i = 0; i < this.methodParamCount; i++)
{
Parameter param = methodParameters[i];
final Parameter param = methodParameters[i];
if (!isMethodVarArgs || i != this.methodParamCount - 1) // Varargs converter is separate
{
this.valueConverters[i] = ValueConverter.fromAnnotatedType(param.getAnnotatedType());
}
}
Class<?> originalVarArgsType = isMethodVarArgs ? methodParameters[methodParamCount - 1].getType().getComponentType() : null;
final Class<?> originalVarArgsType = isMethodVarArgs ? methodParameters[methodParamCount - 1].getType().getComponentType() : null;
this.varArgsType = ClassUtils.primitiveToWrapper(originalVarArgsType); // Primitive array cannot be cast to Obj[]
this.primitiveVarArgs = originalVarArgsType != null && originalVarArgsType.isPrimitive();
this.varArgsConverter = isMethodVarArgs ? ValueConverter.fromAnnotatedType(methodParameters[methodParamCount - 1].getAnnotatedType()) : null;
@SuppressWarnings("unchecked") // Yes. Making a T is not worth
@SuppressWarnings("unchecked") final // Yes. Making a T is not worth
OutputConverter<Object> converter = OutputConverter.get((Class<Object>) method.getReturnType());
this.outputConverter = converter;
@@ -193,13 +203,19 @@ public final class AnnotationParser
{
maxParams = method.getAnnotation(ScarpetFunction.class).maxParams();
if (maxParams == UNDEFINED_PARAMS)
{
throw new IllegalArgumentException("No maximum number of params specified for " + name + ", use ScarpetFunction.UNLIMITED_PARAMS for unlimited. "
+ "Provided in " + originClass);
}
if (maxParams == ScarpetFunction.UNLIMITED_PARAMS)
{
maxParams = Integer.MAX_VALUE;
}
if (maxParams < this.minParams)
{
throw new IllegalArgumentException("Provided maximum number of params for " + name + " is smaller than method's param count."
+ "Provided in " + originClass);
}
}
this.maxParams = maxParams;
@@ -227,56 +243,66 @@ public final class AnnotationParser
}
@Override
public LazyValue apply(Context context, Context.Type t, List<LazyValue> lazyValues)
public LazyValue apply(final Context context, final Context.Type t, final List<LazyValue> lazyValues)
{
List<Value> lv = AbstractLazyFunction.unpackLazy(lazyValues, context, contextType);
final List<Value> lv = AbstractLazyFunction.unpackLazy(lazyValues, context, contextType);
if (isEffectivelyVarArgs)
{
if (lv.size() < minParams)
throw new InternalExpressionException("Function '" + name + "' expected at least " + minParams + " arguments, got " + lv.size() + ". "
{
throw new InternalExpressionException("Function '" + name + "' expected at least " + minParams + " arguments, got " + lv.size() + ". "
+ getUsage());
}
if (lv.size() > maxParams)
throw new InternalExpressionException("Function '" + name + " expected up to " + maxParams + " arguments, got " + lv.size() + ". "
{
throw new InternalExpressionException("Function '" + name + " expected up to " + maxParams + " arguments, got " + lv.size() + ". "
+ getUsage());
}
}
Object[] params = getMethodParams(lv, context, t);
final Object[] params = getMethodParams(lv, context, t);
try
{
return outputConverter.convert(handle.invokeExact(params));
}
catch (Throwable e)
catch (final Throwable e)
{
if (e instanceof RuntimeException)
throw (RuntimeException) e;
if (e instanceof RuntimeException re)
{
throw re;
}
throw (Error) e; // Stack overflow or something. Methods are guaranteed not to throw checked exceptions
}
}
// Hot code: Must be optimized
private Object[] getMethodParams(final List<Value> lv, final Context context, Context.Type theLazyT)
private Object[] getMethodParams(final List<Value> lv, final Context context, final Context.Type theLazyT)
{
Object[] params = new Object[methodParamCount];
ListIterator<Value> lvIterator = lv.listIterator();
final Object[] params = new Object[methodParamCount];
final ListIterator<Value> lvIterator = lv.listIterator();
int regularArgs = isMethodVarArgs ? methodParamCount - 1 : methodParamCount;
final int regularArgs = isMethodVarArgs ? methodParamCount - 1 : methodParamCount;
for (int i = 0; i < regularArgs; i++)
{
params[i] = valueConverters[i].checkAndConvert(lvIterator, context, theLazyT);
if (params[i] == null)
{
throw new InternalExpressionException("Incorrect argument passsed to '" + name + "' function.\n" + getUsage());
}
}
if (isMethodVarArgs)
{
int remaining = lv.size() - lvIterator.nextIndex();
Object[] varArgs;
final int remaining = lv.size() - lvIterator.nextIndex();
final Object[] varArgs;
if (varArgsConverter.consumesVariableArgs())
{
List<Object> varArgsList = new ArrayList<>(); // fastutil's is extremely slow in toArray, and we use that
final List<Object> varArgsList = new ArrayList<>(); // fastutil's is extremely slow in toArray, and we use that
while (lvIterator.hasNext())
{
Object obj = varArgsConverter.checkAndConvert(lvIterator, context, theLazyT);
final Object obj = varArgsConverter.checkAndConvert(lvIterator, context, theLazyT);
if (obj == null)
{
throw new InternalExpressionException("Incorrect argument passsed to '" + name + "' function.\n" + getUsage());
}
varArgsList.add(obj);
}
varArgs = varArgsList.toArray((Object[])Array.newInstance(varArgsType, 0));
@@ -287,7 +313,9 @@ public final class AnnotationParser
{
varArgs[i] = varArgsConverter.checkAndConvert(lvIterator, context, theLazyT);
if (varArgs[i] == null)
{
throw new InternalExpressionException("Incorrect argument passsed to '" + name + "' function.\n" + getUsage());
}
}
}
params[methodParamCount - 1] = primitiveVarArgs ? ArrayUtils.toPrimitive(varArgs) : varArgs; // Copies the array
@@ -299,7 +327,7 @@ public final class AnnotationParser
public String getUsage()
{
// Possibility: More descriptive messages using param.getName()? Would need changing gradle setup to keep those
StringBuilder builder = new StringBuilder("Usage: '");
final StringBuilder builder = new StringBuilder("Usage: '");
builder.append(name);
builder.append('(');
builder.append(Arrays.stream(valueConverters).map(ValueConverter::getTypeName).filter(Objects::nonNull).collect(Collectors.joining(", ")));
@@ -32,34 +32,38 @@ final class ListConverter<T> implements ValueConverter<List<T>>
}
@Override
public List<T> convert(Value value)
public List<T> convert(final Value value)
{
return value instanceof ListValue ? convertListValue((ListValue) value) : allowSingletonCreation ? convertSingleton(value) : null;
}
private List<T> convertListValue(ListValue values)
private List<T> convertListValue(final ListValue values)
{
List<T> list = new ArrayList<>(values.getItems().size());
for (Value value : values)
final List<T> list = new ArrayList<>(values.getItems().size());
for (final Value value : values)
{
T converted = itemConverter.convert(value);
final T converted = itemConverter.convert(value);
if (converted == null)
{
return null;
}
list.add(converted);
}
return list;
}
private List<T> convertSingleton(Value val)
private List<T> convertSingleton(final Value val)
{
T converted = itemConverter.convert(val);
final T converted = itemConverter.convert(val);
if (converted == null)
{
return null;
}
return Collections.singletonList(converted);
}
private ListConverter(AnnotatedType itemType, boolean allowSingletonCreation)
private ListConverter(final AnnotatedType itemType, final boolean allowSingletonCreation)
{
itemConverter = ValueConverter.fromAnnotatedType(itemType);
this.allowSingletonCreation = allowSingletonCreation;
@@ -79,11 +83,11 @@ final class ListConverter<T> implements ValueConverter<List<T>>
* @param annotatedType The type to get generics information from
* @return A new {@link ListConverter} for the data specified in the {@link AnnotatedType}
*/
static ListConverter<?> fromAnnotatedType(AnnotatedType annotatedType)
static ListConverter<?> fromAnnotatedType(final AnnotatedType annotatedType)
{
AnnotatedParameterizedType paramType = (AnnotatedParameterizedType) annotatedType;
AnnotatedType itemType = paramType.getAnnotatedActualTypeArguments()[0];
boolean allowSingletonCreation = annotatedType.isAnnotationPresent(Param.AllowSingleton.class);
final AnnotatedParameterizedType paramType = (AnnotatedParameterizedType) annotatedType;
final AnnotatedType itemType = paramType.getAnnotatedActualTypeArguments()[0];
final boolean allowSingletonCreation = annotatedType.isAnnotationPresent(Param.AllowSingleton.class);
return new ListConverter<>(itemType, allowSingletonCreation);
}
@@ -37,7 +37,7 @@ public interface Locator
@Documented
@Retention(RUNTIME)
@Target({ PARAMETER, TYPE_USE })
public @interface Block
@interface Block
{
/**
* <p>Whether or not should the locator accept a single {@link String} as the parameter and let parsing to {@link BlockValue}.</p>
@@ -66,7 +66,7 @@ public interface Locator
@Documented
@Retention(RUNTIME)
@Target({ PARAMETER, TYPE_USE })
public @interface Vec3d
@interface Vec3d
{
/**
* <p>Whether or not should the {@link Vector3Argument} locator accept an optional direction aside from the
@@ -94,7 +94,7 @@ public interface Locator
@Documented
@Retention(RUNTIME)
@Target({ PARAMETER, TYPE_USE })
public @interface Function
@interface Function
{
/**
* <p>Whether this Locator should allow no function to be passed.</p> <p>This is not compatible with {@link FunctionValue} type, since a
@@ -115,18 +115,27 @@ public interface Locator
*
* <p>Not part of the public API, just that interfaces must have all members public</p>
*/
static final class Locators
final class Locators
{
private Locators() {}
private Locators()
{
super();
}
static <R> ValueConverter<R> fromAnnotatedType(AnnotatedType annoType, Class<R> type)
static <R> ValueConverter<R> fromAnnotatedType(final AnnotatedType annoType, final Class<R> type)
{
if (annoType.isAnnotationPresent(Block.class))
return new BlockLocator<R>(annoType.getAnnotation(Block.class), type);
{
return new BlockLocator<>(annoType.getAnnotation(Block.class), type);
}
if (annoType.isAnnotationPresent(Function.class))
return new FunctionLocator<R>(annoType.getAnnotation(Function.class), type);
{
return new FunctionLocator<>(annoType.getAnnotation(Function.class), type);
}
if (annoType.isAnnotationPresent(Vec3d.class))
return new Vec3dLocator<R>(annoType.getAnnotation(Vec3d.class), type);
{
return new Vec3dLocator<>(annoType.getAnnotation(Vec3d.class), type);
}
throw new IllegalStateException("Locator#fromAnnotatedType got called with an incompatible AnnotatedType");
}
@@ -137,28 +146,42 @@ public interface Locator
private final boolean anyString;
private final boolean optional;
public BlockLocator(Block annotation, Class<R> type)
public BlockLocator(final Block annotation, final Class<R> type)
{
super();
this.acceptString = annotation.acceptString();
this.anyString = annotation.anyString();
this.optional = annotation.optional();
if (type != BlockArgument.class && (anyString || optional))
{
throw new IllegalArgumentException("Can only use anyString or optional parameters of Locator.Block if targeting a BlockArgument");
}
this.returnFunction = getReturnFunction(type);
if (returnFunction == null)
{
throw new IllegalArgumentException("Locator.Block can only be used against BlockArgument, BlockValue, BlockPos or BlockState types!");
}
}
@SuppressWarnings("unchecked")
private static <R> java.util.function.Function<BlockArgument, R> getReturnFunction(Class<R> type) {
private static <R> java.util.function.Function<BlockArgument, R> getReturnFunction(final Class<R> type)
{
if (type == BlockArgument.class)
{
return r -> (R) r;
}
if (type == BlockValue.class)
{
return r -> (R) r.block;
}
if (type == BlockPos.class)
{
return r -> (R) r.block.getPos();
}
if (type == BlockState.class)
{
return r -> (R) r.block.getBlockState();
}
return null;
}
@@ -169,9 +192,9 @@ public interface Locator
}
@Override
public R checkAndConvert(Iterator<Value> valueIterator, Context context, Context.Type theLazyT)
public R checkAndConvert(final Iterator<Value> valueIterator, final Context context, final Context.Type theLazyT)
{
BlockArgument locator = BlockArgument.findIn((CarpetContext) context, valueIterator, 0, acceptString, optional, anyString);
final BlockArgument locator = BlockArgument.findIn((CarpetContext) context, valueIterator, 0, acceptString, optional, anyString);
return returnFunction.apply(locator);
}
}
@@ -182,15 +205,19 @@ public interface Locator
private final boolean optionalEntity;
private final boolean returnVec3d;
public Vec3dLocator(Vec3d annotation, Class<R> type)
public Vec3dLocator(final Vec3d annotation, final Class<R> type)
{
this.optionalDirection = annotation.optionalDirection();
this.optionalEntity = annotation.optionalEntity();
this.returnVec3d = type == net.minecraft.world.phys.Vec3.class; // Because of the locator
if (returnVec3d && optionalDirection)
{
throw new IllegalArgumentException("optionalDirection Locator.Vec3d cannot be used for Vec3d type, use Vector3Argument instead");
}
if (!returnVec3d && type != Vector3Argument.class)
{
throw new IllegalArgumentException("Locator.Vec3d can only be used in Vector3Argument or Vec3d types");
}
}
@Override
@@ -200,11 +227,10 @@ public interface Locator
}
@Override
public R checkAndConvert(Iterator<Value> valueIterator, Context context, Context.Type theLazyT)
public R checkAndConvert(final Iterator<Value> valueIterator, final Context context, final Context.Type theLazyT)
{
Vector3Argument locator = Vector3Argument.findIn(valueIterator, 0, optionalDirection, optionalEntity);
@SuppressWarnings("unchecked")
R ret = (R) (returnVec3d ? locator.vec : locator);
final Vector3Argument locator = Vector3Argument.findIn(valueIterator, 0, optionalDirection, optionalEntity);
@SuppressWarnings("unchecked") final R ret = (R) (returnVec3d ? locator.vec : locator);
return ret;
}
}
@@ -215,24 +241,28 @@ public interface Locator
private final boolean allowNone;
private final boolean checkArgs;
FunctionLocator(Function annotation, Class<R> type)
FunctionLocator(final Function annotation, final Class<R> type)
{
super();
this.returnFunctionValue = type == FunctionValue.class;
if (!returnFunctionValue && type != FunctionArgument.class)
{
throw new IllegalArgumentException("Params annotated with Locator.Function must be of either FunctionArgument or FunctionValue type");
}
this.allowNone = annotation.allowNone();
this.checkArgs = annotation.checkArgs();
if (returnFunctionValue && allowNone)
{
throw new IllegalArgumentException("Cannot use allowNone of Locator.Function in FunctionValue types, use FunctionArgument");
}
}
@Override
public R checkAndConvert(Iterator<Value> valueIterator, Context context, Context.Type theLazyT)
public R checkAndConvert(final Iterator<Value> valueIterator, final Context context, final Context.Type theLazyT)
{
Module module = context.host.main;
FunctionArgument locator = FunctionArgument.findIn(context, module, Lists.newArrayList(valueIterator), 0, allowNone, checkArgs);
@SuppressWarnings("unchecked")
R ret = (R) (returnFunctionValue ? locator.function : locator);
final Module module = context.host.main;
final FunctionArgument locator = FunctionArgument.findIn(context, module, Lists.newArrayList(valueIterator), 0, allowNone, checkArgs);
@SuppressWarnings("unchecked") final R ret = (R) (returnFunctionValue ? locator.function : locator);
return ret;
}
@@ -243,10 +273,10 @@ public interface Locator
}
}
private static abstract class AbstractLocator<R> implements ValueConverter<R>, Locator
private abstract static class AbstractLocator<R> implements ValueConverter<R>, Locator
{
@Override
public R convert(Value value)
public R convert(final Value value)
{
throw new UnsupportedOperationException("Cannot call a locator in a parameter that doesn't contain a context!");
}
@@ -37,17 +37,19 @@ class MapConverter<K, V> implements ValueConverter<Map<K, V>>
}
@Override
public Map<K, V> convert(Value value)
public Map<K, V> convert(final Value value)
{
Map<K, V> result = new HashMap<>();
final Map<K, V> result = new HashMap<>();
if (value instanceof MapValue)
{
for (Entry<Value, Value> entry : ((MapValue) value).getMap().entrySet())
for (final Entry<Value, Value> entry : ((MapValue) value).getMap().entrySet())
{
K key = keyConverter.convert(entry.getKey());
V val = valueConverter.convert(entry.getValue());
final K key = keyConverter.convert(entry.getKey());
final V val = valueConverter.convert(entry.getValue());
if (key == null || val == null)
{
return null;
}
result.put(key, val);
}
return result;
@@ -55,8 +57,9 @@ class MapConverter<K, V> implements ValueConverter<Map<K, V>>
return null;
}
private MapConverter(AnnotatedType keyType, AnnotatedType valueType)
private MapConverter(final AnnotatedType keyType, final AnnotatedType valueType)
{
super();
keyConverter = ValueConverter.fromAnnotatedType(keyType);
valueConverter = ValueConverter.fromAnnotatedType(valueType);
}
@@ -77,9 +80,9 @@ class MapConverter<K, V> implements ValueConverter<Map<K, V>>
* @param annotatedType The type to get generics information from
* @return A new {@link MapConverter} for the data specified in the {@link AnnotatedType}
*/
static MapConverter<?, ?> fromAnnotatedType(AnnotatedType annotatedType)
static MapConverter<?, ?> fromAnnotatedType(final AnnotatedType annotatedType)
{
AnnotatedType[] annotatedGenerics = ((AnnotatedParameterizedType) annotatedType).getAnnotatedActualTypeArguments();
final AnnotatedType[] annotatedGenerics = ((AnnotatedParameterizedType) annotatedType).getAnnotatedActualTypeArguments();
return annotatedType.isAnnotationPresent(Param.KeyValuePairs.class)
? new PairConverter<>(annotatedGenerics[0], annotatedGenerics[1], annotatedType.getAnnotation(Param.KeyValuePairs.class))
: new MapConverter<>(annotatedGenerics[0], annotatedGenerics[1]);
@@ -89,7 +92,7 @@ class MapConverter<K, V> implements ValueConverter<Map<K, V>>
{
private final boolean acceptMultiParam;
private PairConverter(AnnotatedType keyType, AnnotatedType valueType, Param.KeyValuePairs config)
private PairConverter(final AnnotatedType keyType, final AnnotatedType valueType, final Param.KeyValuePairs config)
{
super(keyType, valueType);
acceptMultiParam = config.allowMultiparam();
@@ -102,51 +105,63 @@ class MapConverter<K, V> implements ValueConverter<Map<K, V>>
}
@Override
public Map<K, V> convert(Value value) {
public Map<K, V> convert(final Value value) {
return value instanceof MapValue ? super.convert(value)
: value instanceof ListValue ? convertList(((ListValue)value).getItems())
: null; // Multiparam mode can only be used in evalAndConvert
}
private Map<K, V> convertList(List<Value> valueList)
private Map<K, V> convertList(final List<Value> valueList)
{
if (valueList.size() % 2 == 1)
{
return null;
Map<K, V> map = new HashMap<>();
Iterator<Value> val = valueList.iterator();
}
final Map<K, V> map = new HashMap<>();
final Iterator<Value> val = valueList.iterator();
while (val.hasNext())
{
K key = keyConverter.convert(val.next());
V value = valueConverter.convert(val.next());
final K key = keyConverter.convert(val.next());
final V value = valueConverter.convert(val.next());
if (key == null || value == null)
{
return null;
}
map.put(key, value);
}
return map;
}
@Override
public Map<K, V> checkAndConvert(Iterator<Value> valueIterator, Context context, Context.Type theLazyT)
public Map<K, V> checkAndConvert(final Iterator<Value> valueIterator, final Context context, final Context.Type theLazyT)
{
if (!valueIterator.hasNext())
{
return null;
Value val = valueIterator.next();
}
final Value val = valueIterator.next();
if (!acceptMultiParam || val instanceof MapValue || (val instanceof ListValue && !(keyConverter instanceof ListConverter)))
{
return convert(val); // @KeyValuePairs Map<List<Something>, Boolean> will not support list consumption
}
Map<K, V> map = new HashMap<>();
final Map<K, V> map = new HashMap<>();
K key = keyConverter.convert(val); //First pair is manual since we got it to check for a different conversion mode
V value = valueConverter.checkAndConvert(valueIterator, context, theLazyT);
if (key == null || value == null)
{
return null;
}
map.put(key, value);
while (valueIterator.hasNext())
{
key = keyConverter.checkAndConvert(valueIterator, context, theLazyT);
value = valueConverter.checkAndConvert(valueIterator, context, theLazyT);
if (key == null || value == null)
{
return null;
}
map.put(key, value);
}
return map;
@@ -50,25 +50,33 @@ final class OptionalConverter<R> implements ValueConverter<Optional<R>>
* {@link #checkAndConvert(Iterator, Context, Context.Type)} and is only used as a fallback in types that don't support it.
*/
@Override
public Optional<R> convert(Value value)
public Optional<R> convert(final Value value)
{
if (value.isNull())
{
return Optional.empty();
R converted = typeConverter.convert(value);
}
final R converted = typeConverter.convert(value);
if (converted == null)
{
return null;
}
return Optional.of(converted);
}
@Override
public Optional<R> checkAndConvert(Iterator<Value> valueIterator, Context context, Context.Type theLazyT)
public Optional<R> checkAndConvert(final Iterator<Value> valueIterator, final Context context, final Context.Type theLazyT)
{
if (!valueIterator.hasNext() || valueIterator.next().isNull())
{
return Optional.empty();
}
((ListIterator<Value>) valueIterator).previous();
R converted = typeConverter.checkAndConvert(valueIterator, context, theLazyT);
final R converted = typeConverter.checkAndConvert(valueIterator, context, theLazyT);
if (converted == null)
{
return null;
}
return Optional.of(converted);
}
@@ -98,10 +106,10 @@ final class OptionalConverter<R> implements ValueConverter<Optional<R>>
* @param annotatedType The type to get generics information from
* @return A new {@link OptionalConverter} for the data specified in the {@link AnnotatedType}
*/
static OptionalConverter<?> fromAnnotatedType(AnnotatedType annotatedType)
static OptionalConverter<?> fromAnnotatedType(final AnnotatedType annotatedType)
{
AnnotatedParameterizedType paramType = (AnnotatedParameterizedType) annotatedType;
AnnotatedType wrappedType = paramType.getAnnotatedActualTypeArguments()[0];
final AnnotatedParameterizedType paramType = (AnnotatedParameterizedType) annotatedType;
final AnnotatedType wrappedType = paramType.getAnnotatedActualTypeArguments()[0];
return new OptionalConverter<>(wrappedType);
}
}
@@ -58,7 +58,7 @@ public final class OutputConverter<T>
private final Function<T, LazyValue> converter;
private OutputConverter(Function<T, LazyValue> converter)
private OutputConverter(final Function<T, LazyValue> converter)
{
this.converter = converter;
}
@@ -75,7 +75,9 @@ public final class OutputConverter<T>
public static <T> OutputConverter<T> get(Class<T> returnType)
{
if (Value.class.isAssignableFrom(returnType))
{
return (OutputConverter<T>) VALUE;
}
returnType = (Class<T>) ClassUtils.primitiveToWrapper(returnType); // wrapper holds same generic as primitive: wrapped
return (OutputConverter<T>) Objects.requireNonNull(byResult.get(returnType),
"Unregistered output type: " + returnType + ". Register in OutputConverter");
@@ -89,7 +91,7 @@ public final class OutputConverter<T>
* @param input The value to convert
* @return The converted value
*/
public LazyValue convert(T input)
public LazyValue convert(final T input)
{
return input == null ? LazyValue.NULL : converter.apply(input);
}
@@ -102,11 +104,13 @@ public final class OutputConverter<T>
* @param inputType The class of T
* @param converter The function that converts the an instance of T to a {@link LazyValue}
*/
public static <T> void register(Class<T> inputType, Function<T, LazyValue> converter)
public static <T> void register(final Class<T> inputType, final Function<T, LazyValue> converter)
{
OutputConverter<T> instance = new OutputConverter<T>(converter);
final OutputConverter<T> instance = new OutputConverter<>(converter);
if (byResult.containsKey(inputType))
{
throw new IllegalArgumentException(inputType + " already has a registered OutputConverter");
}
byResult.put(inputType, instance);
}
@@ -119,11 +123,13 @@ public final class OutputConverter<T>
* @param inputType The class of T
* @param converter The function that converts an instance of T to a {@link Value}
*/
public static <T> void registerToValue(Class<T> inputType, Function<T, Value> converter)
public static <T> void registerToValue(final Class<T> inputType, final Function<T, Value> converter)
{
OutputConverter<T> instance = new OutputConverter<>(converter.andThen(v -> (c, t) -> v));
final OutputConverter<T> instance = new OutputConverter<>(converter.andThen(v -> (c, t) -> v));
if (byResult.containsKey(inputType))
{
throw new IllegalArgumentException(inputType + " already has a registered OutputConverter");
}
byResult.put(inputType, instance);
}
}
@@ -51,7 +51,7 @@ public interface Param
@Documented
@Retention(RUNTIME)
@Target({ PARAMETER, TYPE_USE })
public @interface AllowSingleton
@interface AllowSingleton
{
}
@@ -71,7 +71,7 @@ public interface Param
@Documented
@Retention(RUNTIME)
@Target({ PARAMETER, TYPE_USE })
public @interface KeyValuePairs
@interface KeyValuePairs
{
/**
* <p>Whether or not this accepts the key-value pairs directly in the function call as myFunction(..., key, value, key2, value2)</p>
@@ -91,7 +91,7 @@ public interface Param
@Documented
@Retention(RUNTIME)
@Target({ PARAMETER, TYPE_USE })
public @interface Custom
@interface Custom
{
}
@@ -111,7 +111,7 @@ public interface Param
@Documented
@Retention(RUNTIME)
@Target({ PARAMETER, TYPE_USE })
public @interface Strict
@interface Strict
{
/**
* <p>Defines whether this parameter can accept types with "shallow strictness", that is, in order to get a {@link Component}, accepting either a
@@ -137,7 +137,7 @@ public interface Param
* @see #registerCustomConverterFactory(BiFunction)
*
*/
public static final class Params
final class Params
{
/**
* <p>A {@link ValueConverter} that outputs the {@link Context} in which the function has been called, and throws {@link UnsupportedOperationException} when trying to convert a {@link Value}
@@ -148,13 +148,13 @@ public interface Param
@Override public String getTypeName() { return null; }
@Override
public Context convert(Value value)
public Context convert(final Value value)
{
throw new UnsupportedOperationException("Called convert() with Value in Context Provider converter, where only checkAndConvert is supported");
}
@Override
public Context checkAndConvert(Iterator<Value> valueIterator, Context context, Context.Type theLazyT)
public Context checkAndConvert(final Iterator<Value> valueIterator, final Context context, final Context.Type theLazyT)
{
return context;
}
@@ -175,13 +175,13 @@ public interface Param
@Override public String getTypeName() { return null; }
@Override
public Context.Type convert(Value value)
public Context.Type convert(final Value value)
{
throw new UnsupportedOperationException("Called convert() with a Value in TheLazyT Provider, where only checkAndConvert is supported");
}
@Override
public Context.Type checkAndConvert(Iterator<Value> valueIterator, Context context, Context.Type theLazyT)
public Context.Type checkAndConvert(final Iterator<Value> valueIterator, final Context context, final Context.Type theLazyT)
{
return theLazyT;
}
@@ -193,7 +193,7 @@ public interface Param
}
};
static record StrictConverterInfo(Class<?> type, boolean shallow) {}
record StrictConverterInfo(Class<?> type, boolean shallow) {}
private static final Map<StrictConverterInfo, ValueConverter<?>> strictParamsByClassAndShallowness = new HashMap<>();
static
{ // TODO Specify strictness in name?
@@ -214,14 +214,16 @@ public interface Param
* @throws IllegalArgumentException If the type doesn't accept the {@link Strict} annotation or if it has been used incorrectly (shallow in
* unsupported places)
*/
static ValueConverter<?> getStrictConverter(AnnotatedType type)
static ValueConverter<?> getStrictConverter(final AnnotatedType type)
{
boolean shallow = type.getAnnotation(Strict.class).shallow();
Class<?> clazz = (Class<?>) type.getType();
var key = new StrictConverterInfo(clazz, shallow);
ValueConverter<?> converter = strictParamsByClassAndShallowness.get(key);
final boolean shallow = type.getAnnotation(Strict.class).shallow();
final Class<?> clazz = (Class<?>) type.getType();
final StrictConverterInfo key = new StrictConverterInfo(clazz, shallow);
final ValueConverter<?> converter = strictParamsByClassAndShallowness.get(key);
if (converter != null)
{
return converter;
}
throw new IllegalArgumentException("Incorrect use of @Param.Strict annotation");
}
@@ -237,11 +239,13 @@ public interface Param
* @param shallow {@code true} if you are registering a shallow-strict parameter, {@code false} if a "fully" strict one
* @param converter The {@link ValueConverter} for the given type and shallowness.
*/
public static <T> void registerStrictConverter(Class<T> type, boolean shallow, ValueConverter<T> converter)
public static <T> void registerStrictConverter(final Class<T> type, final boolean shallow, final ValueConverter<T> converter)
{
var key = new StrictConverterInfo(type, shallow);
final StrictConverterInfo key = new StrictConverterInfo(type, shallow);
if (strictParamsByClassAndShallowness.containsKey(key))
{
throw new IllegalArgumentException(type + " already has a registered " + (shallow ? "" : "non-") + "shallow StrictConverter");
}
strictParamsByClassAndShallowness.put(key, converter);
}
@@ -267,19 +271,21 @@ public interface Param
* avoid possible collisions with other extensions.
*/
@SuppressWarnings("unchecked") // this makes no sense... But I guess its preferable to enforce typesafety in callers
public static <T> void registerCustomConverterFactory(BiFunction<AnnotatedType, Class<T>, ValueConverter<T>> factory)
public static <T> void registerCustomConverterFactory(final BiFunction<AnnotatedType, Class<T>, ValueConverter<T>> factory)
{
customFactories.add((BiFunction<AnnotatedType, Class<?>, ValueConverter<?>>) (Object) factory);
}
@SuppressWarnings("unchecked") // Stored correctly
static <R> ValueConverter<R> getCustomConverter(AnnotatedType annoType, Class<R> type)
static <R> ValueConverter<R> getCustomConverter(final AnnotatedType annoType, final Class<R> type)
{
ValueConverter<R> result;
for (BiFunction<AnnotatedType, Class<?>, ValueConverter<?>> factory : customFactories)
for (final BiFunction<AnnotatedType, Class<?>, ValueConverter<?>> factory : customFactories)
{
if ((result = (ValueConverter<R>) factory.apply(annoType, type)) != null)
{
return result;
}
}
throw new IllegalArgumentException("No custom converter found for Param.Custom annotated param with type " + annoType.getType().getTypeName());
}
@@ -53,7 +53,7 @@ public @interface ScarpetFunction
/**
* <p>Used to define that this {@link ScarpetFunction} can accept an unlimited number of parameters</p>
*/
public static final int UNLIMITED_PARAMS = -1;
int UNLIMITED_PARAMS = -1;
/**
* <p>If the function can accept a variable number of parameters, either by declaring its last parameter as a varargs parameter or by having one
@@ -58,8 +58,9 @@ public final class SimpleTypeConverter<T extends Value, R> implements ValueConve
* @param inputType The required type for the input {@link Value}
* @param converter The function to convert an instance of inputType into R.
*/
public SimpleTypeConverter(Class<T> inputType, Function<T, R> converter, String typeName)
public SimpleTypeConverter(final Class<T> inputType, final Function<T, R> converter, final String typeName)
{
super();
this.converter = converter;
this.valueClass = inputType;
this.typeName = typeName;
@@ -79,14 +80,14 @@ public final class SimpleTypeConverter<T extends Value, R> implements ValueConve
* @return The {@link SimpleTypeConverter} for the specified outputType
*/
@SuppressWarnings("unchecked") // T always extends Value, R is always the same as map's key, since map is private.
static <R> SimpleTypeConverter<Value, R> get(Class<R> outputType)
static <R> SimpleTypeConverter<Value, R> get(final Class<R> outputType)
{
return (SimpleTypeConverter<Value, R>) byResult.get(outputType);
}
@Override
@SuppressWarnings("unchecked") // more than checked. not using class.cast because then "method is too big" for inlining, because javac is useless
public R convert(Value value) // and adds millions of casts. This one is even removed
public R convert(final Value value) // and adds millions of casts. This one is even removed
{
return valueClass.isInstance(value) ? converter.apply((T)value) : null;
}
@@ -103,10 +104,10 @@ public final class SimpleTypeConverter<T extends Value, R> implements ValueConve
* can also throw an {@link InternalExpressionException} by itself if really necessary.
* @param typeName The name of the type, following the conventions of {@link ValueConverter#getTypeName()}
*/
public static <T extends Value, R> void registerType(Class<T> requiredInputType, Class<R> outputType,
Function<T, R> converter, String typeName)
public static <T extends Value, R> void registerType(final Class<T> requiredInputType, final Class<R> outputType,
final Function<T, R> converter, final String typeName)
{
SimpleTypeConverter<T, R> type = new SimpleTypeConverter<>(requiredInputType, converter, typeName);
final SimpleTypeConverter<T, R> type = new SimpleTypeConverter<>(requiredInputType, converter, typeName);
byResult.put(outputType, type);
}
}
@@ -50,8 +50,9 @@ public final class ValueCaster<R> implements ValueConverter<R> // R always exten
private final Class<R> outputType;
private final String typeName;
private ValueCaster(Class<R> outputType, String typeName)
private ValueCaster(final Class<R> outputType, final String typeName)
{
super();
this.outputType = outputType;
this.typeName = typeName;
}
@@ -71,17 +72,19 @@ public final class ValueCaster<R> implements ValueConverter<R> // R always exten
*/
@SuppressWarnings("unchecked") // Casters are stored with their exact class, for sure since the map is private (&& class has same generic as
// caster)
public static <R> ValueCaster<R> get(Class<R> outputType)
public static <R> ValueCaster<R> get(final Class<R> outputType)
{
return (ValueCaster<R>) byResult.get(outputType);
}
@Override
@SuppressWarnings("unchecked") // more than checked, see SimpleTypeConverter#converter for reasoning
public R convert(Value value)
public R convert(final Value value)
{
if (!outputType.isInstance(value))
{
return null;
}
return (R)value;
}
@@ -94,9 +97,9 @@ public final class ValueCaster<R> implements ValueConverter<R> // R always exten
* required<!--, with the form //Outdated concept <code>(function name) requires a (typeName) to be passed as (argName, if
* available)</code>-->
*/
public static <R extends Value> void register(Class<R> valueClass, String typeName)
public static <R extends Value> void register(final Class<R> valueClass, final String typeName)
{
ValueCaster<R> caster = new ValueCaster<R>(valueClass, typeName);
final ValueCaster<R> caster = new ValueCaster<>(valueClass, typeName);
byResult.putIfAbsent(valueClass, caster);
}
}
@@ -37,7 +37,7 @@ public interface ValueConverter<R>
* @apiNote This method is intended to only be called when an error has occurred and therefore there is a need to print a stacktrace with some
* helpful usage instructions.
*/
public String getTypeName();
String getTypeName();
/**
* <p>Converts the given {@link Value} to {@code <R>}, which was defined when being registered.</p>
@@ -62,8 +62,7 @@ public interface ValueConverter<R>
* <p>Even with the above reasons, {@link ValueConverter} users should try to implement {@link #convert(Value)} whenever possible instead of
* {@link #checkAndConvert(Iterator, Context, Context.Type)}, since it allows its usage in generics of lists and maps.</p>
*/
@Nullable
public R convert(Value value);
@Nullable R convert(Value value);
/**
* <p>Returns whether this {@link ValueConverter} consumes a variable number of elements from the {@link Iterator} passed to it via
@@ -72,7 +71,7 @@ public interface ValueConverter<R>
* @implNote The default implementation returns {@code false}
* @see #valueConsumption()
*/
default public boolean consumesVariableArgs()
default boolean consumesVariableArgs()
{
return false;
}
@@ -87,7 +86,7 @@ public interface ValueConverter<R>
* @implNote The default implementation returns {@code 1}
*
*/
default public int valueConsumption()
default int valueConsumption()
{
return 1;
}
@@ -107,7 +106,7 @@ public interface ValueConverter<R>
* @return A usable {@link ValueConverter} to convert from a {@link Value} to {@code <R>}
*/
@SuppressWarnings("unchecked")
public static <R> ValueConverter<R> fromAnnotatedType(AnnotatedType annoType)
static <R> ValueConverter<R> fromAnnotatedType(final AnnotatedType annoType)
{
Class<R> type = annoType.getType() instanceof ParameterizedType ? // We are defining R here.
(Class<R>) ((ParameterizedType) annoType.getType()).getRawType() :
@@ -118,33 +117,51 @@ public interface ValueConverter<R>
// Example: AnnotatedGenericTypeArray (or similar) being (@Paran.KeyValuePairs Map<String, String>... name)
// Those will just fail with a ClassCastException.
if (type.isArray())
{
type = (Class<R>) type.getComponentType(); // Varargs
}
type = (Class<R>) ClassUtils.primitiveToWrapper(type); // It will be boxed anyway, this saves unboxing-boxing
if (type == List.class)
{
return (ValueConverter<R>) ListConverter.fromAnnotatedType(annoType); // Already checked that type is List
}
if (type == Map.class)
{
return (ValueConverter<R>) MapConverter.fromAnnotatedType(annoType); // Already checked that type is Map
}
if (type == Optional.class)
{
return (ValueConverter<R>) OptionalConverter.fromAnnotatedType(annoType);
}
if (annoType.getDeclaredAnnotations().length != 0)
{
if (annoType.isAnnotationPresent(Param.Custom.class))
return Param.Params.getCustomConverter(annoType, type); // Throws if incorrect usage
{
return Params.getCustomConverter(annoType, type); // Throws if incorrect usage
}
if (annoType.isAnnotationPresent(Param.Strict.class))
{
return (ValueConverter<R>) Params.getStrictConverter(annoType); // Throws if incorrect usage
}
if (annoType.getAnnotations()[0].annotationType().getEnclosingClass() == Locator.class)
{
return Locator.Locators.fromAnnotatedType(annoType, type);
}
}
// Class only checks
if (Value.class.isAssignableFrom(type))
{
return Objects.requireNonNull(ValueCaster.get(type), "Value subclass " + type + " is not registered. Register it in ValueCaster to use it");
// if (type == LazyValue.class) // No longer supported
// return (ValueConverter<R>) Params.LAZY_VALUE_IDENTITY;
}
if (type == Context.class)
{
return (ValueConverter<R>) Params.CONTEXT_PROVIDER;
}
if (type == Context.Type.class)
{
return (ValueConverter<R>) Params.CONTEXT_TYPE_PROVIDER;
}
return Objects.requireNonNull(SimpleTypeConverter.get(type), "Type " + type + " is not registered. Register it in SimpleTypeConverter to use it");
}
@@ -168,10 +185,8 @@ public interface ValueConverter<R>
* @implNote This method's default implementation runs the {@link #convert(Value)} function in the next {@link Value} ignoring {@link Context} and
* {@code theLazyT}.
*/
default public R checkAndConvert(Iterator<Value> valueIterator, Context context, Context.Type contextType)
default R checkAndConvert(final Iterator<Value> valueIterator, final Context context, final Context.Type contextType)
{
if (!valueIterator.hasNext())
return null;
return convert(valueIterator.next());
return !valueIterator.hasNext() ? null : convert(valueIterator.next());
}
}