FreedomNetworkSuite/Patchwork/src/main/java/fns/patchwork/provider/ContextProvider.java

205 lines
5.8 KiB
Java

package fns.patchwork.provider;
import fns.patchwork.command.BukkitDelegate;
import fns.patchwork.command.annotation.Subcommand;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Stream;
import net.kyori.adventure.text.Component;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* This class is used to provide context to subcommand methods. This class is used by the BukkitDelegate to parse
* arguments for subcommands. The following types are supported:
* <ul>
* <li>Boolean</li>
* <li>Double</li>
* <li>Integer</li>
* <li>Long</li>
* <li>Float</li>
* <li>Material</li>
* <li>Player</li>
* <li>World</li>
* <li>Location</li>
* <li>CommandSender</li>
* <li>Component</li>
* </ul>
* All of these types can be parsed from a String input. If the String cannot be parsed into any of
* these types, then null will be returned.
*
* @see #fromString(String, Class)
*/
public class ContextProvider
{
public <T> T fromString(final String string, final Class<T> clazz)
{
return Stream.of(toBoolean(string, clazz),
toDouble(string, clazz),
toInt(string, clazz),
toLong(string, clazz),
toFloat(string, clazz),
toMaterial(string, clazz),
toPlayer(string, clazz),
toWorld(string, clazz),
toLocation(string, clazz),
toComponent(string, clazz))
.filter(Objects::nonNull)
.findFirst()
.map(clazz::cast)
.orElse(null);
}
private @Nullable Boolean toBoolean(final String string, final Class<?> clazz)
{
if (clazz != Boolean.class)
return null;
// Previously we used Boolean#parseBoolean, but that will always return a value and does not throw
// an exception. This means that if the string is not "true" or "false", it will return false.
if (string.equalsIgnoreCase("true"))
return true;
if (string.equalsIgnoreCase("false"))
return false;
return null;
}
private @Nullable Double toDouble(final String string, final Class<?> clazz)
{
if (clazz != Double.class)
return null;
try
{
return Double.parseDouble(string);
}
catch (NumberFormatException ignored)
{
return null;
}
}
private @Nullable Integer toInt(final String string, final Class<?> clazz)
{
if (clazz != Integer.class)
return null;
try
{
return Integer.parseInt(string);
}
catch (NumberFormatException ignored)
{
return null;
}
}
private @Nullable Long toLong(final String string, final Class<?> clazz)
{
if (clazz != Long.class)
return null;
try
{
return Long.parseLong(string);
}
catch (NumberFormatException ignored)
{
return null;
}
}
private @Nullable Float toFloat(final String string, final Class<?> clazz)
{
if (clazz != Float.class)
return null;
try
{
return Float.parseFloat(string);
}
catch (NumberFormatException ignored)
{
return null;
}
}
private @Nullable Material toMaterial(final String string, final Class<?> clazz)
{
if (clazz != Material.class)
return null;
return Material.matchMaterial(string);
}
private @Nullable Player toPlayer(final String string, final Class<?> clazz)
{
if (clazz != Player.class)
return null;
return Bukkit.getPlayer(string);
}
private @Nullable World toWorld(final String string, final Class<?> clazz)
{
if (clazz != World.class)
return null;
return Bukkit.getWorld(string);
}
/**
* When using this method, the next four arguments must be world, x, y, z.
* The world must be a valid world name, and x, y, and z must be valid doubles.
* If any of these are invalid, this will return null.
*
* @param string The string to parse
* @return A location object if xyz is valid
*/
private @Nullable Location toLocation(final String string, final Class<?> clazz)
{
if (clazz != Location.class)
return null;
final String[] split = string.split(" ");
if (split.length != 4 || toWorld(split[0], World.class) == null)
return null;
try
{
final double x = Double.parseDouble(split[1]);
final double y = Double.parseDouble(split[2]);
final double z = Double.parseDouble(split[3]);
return new Location(toWorld(split[0], World.class), x, y, z);
}
catch (NumberFormatException ex)
{
return null;
}
}
private @Nullable Component toComponent(final String string, final Class<?> clazz)
{
if (clazz != Component.class)
return null;
return Component.text(string);
}
public @NotNull <T> List<@Nullable T> getList(final List<String> resolvable, final Class<T> clazz)
{
final List<T> resolved = new ArrayList<>();
for (final String entry : resolvable)
{
resolved.add(this.fromString(entry, clazz));
}
return resolved;
}
}