package carpet.script.annotation; import java.lang.reflect.AnnotatedType; import java.lang.reflect.ParameterizedType; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import org.apache.commons.lang3.ClassUtils; import carpet.script.Context; import carpet.script.annotation.Param.Params; import carpet.script.value.Value; import org.jspecify.annotations.Nullable; /** *
Classes implementing this interface are able to convert {@link Value} instances into {@code
Returns the the user-friendly name of the result this {@link ValueConverter} converts to, without {@code a} or {@code an}, and without * capitalizing the first letter.
* *This method can return {@code null}, in which case users of this function should hide this {@link ValueConverter} from any aids or usages of * the function, meaning that the {@link ValueConverter} is only providing some meta information that isn't directly provided through the Scarpet * language.
* *Those aids calling this method may append an {@code s} to the return value of this method, in case the type is used in places where more * than one may be present, such as lists or varargs.
* * @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. */ @Nullable String getTypeName(); /** *Converts the given {@link Value} to {@code
Returns {@code null} if one of the conversions failed, either because the {@link Value} was incompatible in some position of the chain, or * because the actual converting function returned {@code null} (which usually only occurs when the {@link Value} is incompatible/does not hold * the appropriate information)
* *Functions using the converter can use {@link #getTypeName()} to get the name of the type this was trying to convert to, in case they are not * trying to convert to anything else, where it would be recommended to tell the user the name of the final type instead.
* * @param value The {@link Value} to convert * @param context The {@link Context} of the call * @return The converted value, or {@code null} if the conversion failed in the process * @apiNoteWhile most implementations of this method should and will return the type from this method, implementations that require * parameters from {@link #checkAndConvert(Iterator, Context, Context.Type)} or that require multiple parameters may decide to throw * {@link UnsupportedOperationException} in this method and override {@link #checkAndConvert(Iterator, Context, Context.Type)} instead. Those * implementations, however, should not be available for map or list types, since those can only operate with {@link Value}.
*Currently, the only implementations requiring that are {@link Params#CONTEXT_PROVIDER} and {@link Params#CONTEXT_TYPE_PROVIDER}
*Implementations can also provide different implementations for this and {@link #checkAndConvert(Iterator, Context, Context.Type)}, in case * they can support it in some situations that can't be used else, such as inside of lists or maps, although they should try to provide * in {@link #checkAndConvert(Iterator, Context, Context.Type)} at least the same conversion as the one from this method.
*Even with the above reasons, {@link ValueConverter} users should try to implement {@link #convert(Value, Context)} whenever possible instead of * {@link #checkAndConvert(Iterator, Context, Context.Type)}, since it allows its usage in generics of lists and maps.
*/ @Nullable R convert(Value value, @Nullable Context context); /** * Old version of {@link #convert(Value)} without taking a {@link Context}.* * This shouldn't be used given converters now take a context in the convert function to allow for converting * values in lists or other places without using static state.
* * @param value The value to convert * @return A converted value * @deprecated Calling this method instead of {@link #convert(Value, Context)} may not return values for some converters */ @Nullable @Deprecated(forRemoval = true) default R convert(Value value) { try { return convert(value, null); } catch (NullPointerException e) { return null; } } /** *
Returns whether this {@link ValueConverter} consumes a variable number of elements from the {@link Iterator} passed to it via * {@link #checkAndConvert(Iterator, Context, Context.Type)}.
* * @implNote The default implementation returns {@code false} * @see #valueConsumption() */ default boolean consumesVariableArgs() { return false; } /** *Declares the number of {@link Value}s this converter consumes from the {@link Iterator} passed to it in * {@link #checkAndConvert(Iterator, Context, Context.Type)}.
* *If this {@link ValueConverter} can accept a variable number of arguments (therefore the result of calling {@link #consumesVariableArgs()} * must return {@code true}), it will return the minimum number of arguments it will consume.
* * @implNote The default implementation returns {@code 1} * */ default int valueConsumption() { return 1; } /** *Gets the proper {@link ValueConverter} for the given {@link AnnotatedType}, considering the type of {@code R[]} as {@code R}.
* *This function does not only consider the actual type (class) of the passed {@link AnnotatedType}, but also its annotations and generic * parameters in order to get the most specific {@link ValueConverter}.
* *Some processing is delegated to the appropriate implementations of {@link ValueConverter} in order to get registered converters or generate * specific ones for some functions.
* * @paramChecks for the presence of the next {@link Value} in the given {@link Iterator}, evaluates it with the given {@link Context} and then * converts it to this {@link ValueConverter}'s output type.
* *This should be the preferred way to call the converter, since it allows multi-param converters and allows meta converters (such as the {@link Context} provider)
* * @implSpec Implementations of this method are not required to move the {@link Iterator} to the next position, such as in the case of meta * providers like {@link Context}.Implementations can also use more than a single parameter when being called with this function, * but in such case they must implement {@link #valueConsumption()} to return how many parameters do they consume at minimum, and, if * they may consume variable arguments, implement {@link #consumesVariableArgs()}
This method holds the same nullability * constraints as {@link #convert(Value, Context)}
* @param valueIterator An {@link Iterator} holding the {@link Value} to convert in next position * @param context The {@link Context} this function has been called with * @param contextType The {@link Context.Type} that the original function was called with * @return The next {@link Value} (s) converted to the type {@code