feat: @benwoo1110 Revamp content display API

commit 1211dfb057
Author: Ben Woo <30431861+benwoo1110@users.noreply.github.com>
Date:   Tue Aug 17 16:29:31 2021 +0800

    Implement suggested improvements.

    * Use singleton pattern for DefaultContentFilter with getInstance method.
    * Have a default SendHandler.
    * Don't need streams for small dataset.
    * Private WorldListContentParser class to improve readability.

commit 562eed8255
Author: Ben Woo <30431861+benwoo1110@users.noreply.github.com>
Date:   Tue Aug 17 00:26:57 2021 +0800

    Revamp content display API.
This commit is contained in:
Ben Woo 2023-02-03 20:23:53 +08:00
parent ccf802e32f
commit 0117159159
28 changed files with 841 additions and 968 deletions

View File

@ -8,9 +8,12 @@
package com.onarandombox.MultiverseCore.commandsold;
import com.onarandombox.MultiverseCore.MultiverseCore;
import com.onarandombox.MultiverseCore.display.ColorAlternator;
import com.onarandombox.MultiverseCore.display.ContentDisplay;
import com.onarandombox.MultiverseCore.display.settings.MapDisplaySettings;
import com.onarandombox.MultiverseCore.display.filters.ContentFilter;
import com.onarandombox.MultiverseCore.display.filters.DefaultContentFilter;
import com.onarandombox.MultiverseCore.display.filters.RegexContentFilter;
import com.onarandombox.MultiverseCore.display.handlers.InlineSendHandler;
import com.onarandombox.MultiverseCore.display.parsers.MapContentParser;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.GameRule;
@ -31,8 +34,8 @@ public class GamerulesCommand extends MultiverseCommand {
public GamerulesCommand(MultiverseCore plugin) {
super(plugin);
this.setName("List the Minecraft Game Rules for a World.");
this.setCommandUsage("/mv gamerules" + ChatColor.GOLD + " [WORLD]");
this.setArgRange(0, 1);
this.setCommandUsage("/mv gamerules" + ChatColor.GOLD + " [WORLD] [FILTER]");
this.setArgRange(0, 2);
this.addKey("mv gamerules");
this.addKey("mv rules");
this.addKey("mvgamerules");
@ -42,11 +45,13 @@ public class GamerulesCommand extends MultiverseCommand {
this.setPermission("multiverse.core.gamerule.list", "Allows a player to list gamerules.", PermissionDefault.OP);
}
@Override
public void runCommand(CommandSender sender, List<String> args) {
// We NEED a world from the command line
final Player p;
Player p;
World world;
ContentFilter filter = DefaultContentFilter.getInstance();
if (sender instanceof Player) {
p = (Player) sender;
} else {
@ -61,9 +66,20 @@ public class GamerulesCommand extends MultiverseCommand {
return;
}
final World world;
// Not the best way, need to fix with ACF soon...
if (args.size() == 0) {
world = p.getWorld();
} else if (args.size() == 1) {
world = Bukkit.getWorld(args.get(0));
if (world == null) {
if (p == null) {
sender.sendMessage(ChatColor.RED + "Failure!" + ChatColor.WHITE + " World " + ChatColor.AQUA + args.get(0)
+ ChatColor.WHITE + " does not exist.");
return;
}
world = p.getWorld();
filter = RegexContentFilter.fromString(args.get(0));
}
} else {
world = Bukkit.getWorld(args.get(0));
if (world == null) {
@ -71,23 +87,23 @@ public class GamerulesCommand extends MultiverseCommand {
+ ChatColor.WHITE + " does not exist.");
return;
}
filter = RegexContentFilter.fromString(args.get(1));
}
ContentDisplay.forContent(getGameRuleMap(world))
.header("=== Gamerules for %s%s%s ===", ChatColor.AQUA, world.getName(), ChatColor.WHITE)
.colorTool(ColorAlternator.with(ChatColor.GREEN, ChatColor.GOLD))
.setting(MapDisplaySettings.OPERATOR, ": ")
.show(sender);
ContentDisplay.create()
.addContentParser(MapContentParser.forContent(getGameRuleMap(world))
.withKeyColor(ChatColor.GREEN)
.withValueColor(ChatColor.YELLOW))
.withSendHandler(InlineSendHandler.create()
.withHeader("====[ Gamerules for %s%s%s ]====", ChatColor.AQUA, world.getName(), ChatColor.WHITE)
.withFilter(filter))
.send(sender);
}
private Map<String, Object> getGameRuleMap(World world) {
Map<String, Object> gameRuleMap = new HashMap<>();
for (GameRule<?> rule : GameRule.values()) {
Object value = world.getGameRuleValue(rule);
if (value == null) {
gameRuleMap.put(rule.getName(), "null");
continue;
}
gameRuleMap.put(rule.getName(), value);
}
return gameRuleMap;

View File

@ -9,11 +9,12 @@ package com.onarandombox.MultiverseCore.commandsold;
import com.onarandombox.MultiverseCore.MultiverseCore;
import com.onarandombox.MultiverseCore.api.MultiverseWorld;
import com.onarandombox.MultiverseCore.display.ColorAlternator;
import com.onarandombox.MultiverseCore.display.ContentDisplay;
import com.onarandombox.MultiverseCore.display.ContentFilter;
import com.onarandombox.MultiverseCore.display.DisplayHandlers;
import com.onarandombox.MultiverseCore.display.settings.PagedDisplaySettings;
import com.onarandombox.MultiverseCore.display.filters.ContentFilter;
import com.onarandombox.MultiverseCore.display.filters.DefaultContentFilter;
import com.onarandombox.MultiverseCore.display.filters.RegexContentFilter;
import com.onarandombox.MultiverseCore.display.handlers.PagedSendHandler;
import com.onarandombox.MultiverseCore.display.parsers.ContentParser;
import org.bukkit.ChatColor;
import org.bukkit.World;
import org.bukkit.command.CommandSender;
@ -21,9 +22,7 @@ import org.bukkit.entity.Player;
import org.bukkit.permissions.PermissionDefault;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
/**
* Displays a listing of all worlds that a player can enter.
@ -43,7 +42,7 @@ public class ListCommand extends MultiverseCommand {
@Override
public void runCommand(CommandSender sender, List<String> args) {
ContentFilter filter = ContentFilter.DEFAULT;
ContentFilter filter = DefaultContentFilter.getInstance();
int page = 1;
// Either page or filter.
@ -51,13 +50,13 @@ public class ListCommand extends MultiverseCommand {
try {
page = Integer.parseInt(args.get(0));
} catch (NumberFormatException ignore) {
filter = new ContentFilter(args.get(0));
filter = RegexContentFilter.fromString(args.get(0));
}
}
// Filter then page.
if (args.size() == 2) {
filter = new ContentFilter(args.get(0));
filter = RegexContentFilter.fromString(args.get(0));
try {
page = Integer.parseInt(args.get(1));
} catch (NumberFormatException ignore) {
@ -65,55 +64,57 @@ public class ListCommand extends MultiverseCommand {
}
}
ContentDisplay.forContent(getListContents(sender))
.header("%s====[ Multiverse World List ]====", ChatColor.GOLD)
.displayHandler(DisplayHandlers.PAGE_LIST)
.colorTool(ColorAlternator.with(ChatColor.AQUA, ChatColor.GOLD))
.filter(filter)
.setting(PagedDisplaySettings.SHOW_PAGE, page)
.show(sender);
ContentDisplay.create()
.addContentParser(new WorldListContentParser())
.withSendHandler(PagedSendHandler.create()
.withHeader("%s====[ Multiverse World List ]====", ChatColor.GOLD)
.withFilter(filter)
.withTargetPage(page))
.send(sender);
}
private Collection<String> getListContents(@NotNull CommandSender sender) {
Player player = (sender instanceof Player) ? (Player) sender : null;
private class WorldListContentParser implements ContentParser {
List<String> worldList = this.plugin.getMVWorldManager().getMVWorlds().stream()
.filter(world -> player == null || plugin.getMVPerms().canEnterWorld(player, world))
.filter(world -> canSeeWorld(player, world))
.map(world -> hiddenText(world) + world.getColoredWorldString() + " - " + parseColouredEnvironment(world.getEnvironment()))
.collect(Collectors.toList());
@Override
public void parse(@NotNull CommandSender sender, @NotNull List<String> content) {
Player player = (sender instanceof Player) ? (Player) sender : null;
this.plugin.getMVWorldManager().getUnloadedWorlds().stream()
.filter(world -> plugin.getMVPerms().hasPermission(sender, "multiverse.access." + world, true))
.map(world -> ChatColor.GRAY + world + " - UNLOADED")
.forEach(worldList::add);
return worldList;
}
plugin.getMVWorldManager().getMVWorlds().stream()
.filter(world -> player == null || plugin.getMVPerms().canEnterWorld(player, world))
.filter(world -> canSeeWorld(player, world))
.map(world -> hiddenText(world) + world.getColoredWorldString() + " - " + parseColouredEnvironment(world.getEnvironment()))
.forEach(content::add);
private boolean canSeeWorld(Player player, MultiverseWorld world) {
return !world.isHidden()
|| player == null
|| this.plugin.getMVPerms().hasPermission(player, "multiverse.core.modify", true);
}
private String hiddenText(MultiverseWorld world) {
return (world.isHidden()) ? String.format("%s[H] ", ChatColor.GRAY) : "";
}
private String parseColouredEnvironment(World.Environment env) {
ChatColor color = ChatColor.GOLD;
switch (env) {
case NETHER:
color = ChatColor.RED;
break;
case NORMAL:
color = ChatColor.GREEN;
break;
case THE_END:
color = ChatColor.AQUA;
break;
plugin.getMVWorldManager().getUnloadedWorlds().stream()
.filter(world -> plugin.getMVPerms().hasPermission(sender, "multiverse.access." + world, true))
.map(world -> ChatColor.GRAY + world + " - UNLOADED")
.forEach(content::add);
}
private boolean canSeeWorld(Player player, MultiverseWorld world) {
return !world.isHidden()
|| player == null
|| plugin.getMVPerms().hasPermission(player, "multiverse.core.modify", true);
}
private String hiddenText(MultiverseWorld world) {
return (world.isHidden()) ? String.format("%s[H] ", ChatColor.GRAY) : "";
}
private String parseColouredEnvironment(World.Environment env) {
ChatColor color = ChatColor.GOLD;
switch (env) {
case NETHER:
color = ChatColor.RED;
break;
case NORMAL:
color = ChatColor.GREEN;
break;
case THE_END:
color = ChatColor.AQUA;
break;
}
return color + env.toString();
}
return color + env.toString();
}
}

View File

@ -1,62 +0,0 @@
package com.onarandombox.MultiverseCore.display;
import org.bukkit.ChatColor;
import org.jetbrains.annotations.NotNull;
/**
* Helper class to switch between 2 {@link ChatColor}.
*/
public class ColorAlternator implements ColorTool {
/**
* Creates a new {@link ColorAlternator} with 2 {@link ChatColor}s.
*
* @param colorThis The first color.
* @param colorThat The second color.
* @return The {@link ColorAlternator} created for you.
*/
public static ColorAlternator with(@NotNull ChatColor colorThis,
@NotNull ChatColor colorThat) {
return new ColorAlternator(colorThis, colorThat);
}
private boolean switcher;
private final ChatColor thisColor;
private final ChatColor thatColor;
/**
* @param colorThis The first color.
* @param colorThat The second color.
*/
public ColorAlternator(@NotNull ChatColor colorThis,
@NotNull ChatColor colorThat) {
this.thisColor = colorThis;
this.thatColor = colorThat;
}
/**
* Gets the color. Everytime this method is called, it swaps the color that it returns.
*
* @return The color.
*/
@Override
public ChatColor get() {
return (this.switcher ^= true) ? this.thisColor : this.thatColor;
}
/**
* @return The first color.
*/
public ChatColor getThisColor() {
return thisColor;
}
/**
* @return The second color.
*/
public ChatColor getThatColor() {
return thatColor;
}
}

View File

@ -1,22 +0,0 @@
package com.onarandombox.MultiverseCore.display;
import org.bukkit.ChatColor;
/**
* Tools to allow customisation.
*/
@FunctionalInterface
public interface ColorTool {
/**
* Gets a chat color.
*
* @return The color.
*/
ChatColor get();
/**
* Default implementation of this interface. Returns a default white color.
*/
ColorTool DEFAULT = () -> ChatColor.WHITE;
}

View File

@ -1,268 +1,69 @@
package com.onarandombox.MultiverseCore.display;
import com.onarandombox.MultiverseCore.display.settings.DisplaySetting;
import org.bukkit.ChatColor;
import com.onarandombox.MultiverseCore.display.handlers.DefaultSendHandler;
import com.onarandombox.MultiverseCore.display.handlers.SendHandler;
import com.onarandombox.MultiverseCore.display.parsers.ContentParser;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.Map;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.WeakHashMap;
/**
* Helps to display contents such as list and maps in a nicely formatted fashion.
*
* @param <T> Type of content to display.
*/
public class ContentDisplay<T> {
public static final String LINE_BREAK = "%br%";
public class ContentDisplay {
/**
* Creates a ContentDisplay.Builder for the given content.
* Makes a new {@link ContentDisplay} instance to use.
*
* @param content The content to be displayed.
* @param <T> The type of the content which can be inferred.
* @return A new Builder.
*/
public static <T> Builder<T> forContent(T content) {
return new Builder<>(content);
}
/**
* Creates a ContentDisplay.Builder for the given collection of content.
*
* @param content The content to be displayed.
* @return A new Builder.
*/
public static Builder<Collection<String>> forContent(Collection<String> content) {
return new Builder<>(content).displayHandler(DisplayHandlers.LIST);
}
/**
* Creates a ContentDisplay.Builder for the given map of content.
*
* @param content The content to be displayed.
* @return A new Builder.
*/
public static Builder<Map<String, Object>> forContent(Map<String, Object> content) {
return new Builder<>(content).displayHandler(DisplayHandlers.INLINE_MAP);
}
private final T contents;
private String header;
private String emptyMessage = "No matching content to display.";
private DisplayHandler<T> displayHandler;
private ColorTool colorTool = ColorTool.DEFAULT;
private ContentFilter filter = ContentFilter.DEFAULT;
private final Map<DisplaySetting<?>, Object> settingsMap = new WeakHashMap<>();
private ContentDisplay(T contents) {
this.contents = contents;
}
/**
* Do the actual displaying of contents to the sender.
*
* @param sender The CommandSender to show the display to.
*/
public void show(@NotNull CommandSender sender) {
Collection<String> formattedContent;
try {
formattedContent = (this.contents == null) ? null : this.displayHandler.format(sender, this);
} catch (DisplayFormatException e) {
sender.sendMessage(String.format("%sError: %s", ChatColor.RED, e.getMessage()));
return;
}
this.displayHandler.sendHeader(sender, this);
this.displayHandler.sendSubHeader(sender, this);
this.displayHandler.sendBody(sender, this, formattedContent);
}
/**
* @return Gets the header to display.
*/
public String getHeader() {
return header;
}
/**
* Sets the header text.
*/
public void setHeader(@NotNull String header) {
this.header = header;
}
/**
* @return Gets the contents to display.
*/
public T getContents() {
return contents;
}
/**
* @return Gets the message to display when no content is shown.
* @return New {@link ContentDisplay} instance.
*/
@NotNull
public String getEmptyMessage() {
return emptyMessage;
public static ContentDisplay create() {
return new ContentDisplay();
}
private final List<ContentParser> contentParsers = new ArrayList<>();
private SendHandler sendHandler = DefaultSendHandler.getInstance();
public ContentDisplay() {
}
/**
* @return Gets the display handler that formats and sends content to sender.
* Adds content to be displayed.
*
* @param parser The content parser to add.
* @return Same {@link ContentDisplay} for method chaining.
*/
@NotNull
public DisplayHandler<T> getDisplayHandler() {
return displayHandler;
public ContentDisplay addContentParser(@NotNull ContentParser parser) {
contentParsers.add(parser);
return this;
}
/**
* @return Gets the color tool used.
* Sets the handler for displaying the message to command sender.
*
* @param handler The send handler to use.
* @return Same {@link ContentDisplay} for method chaining.
*/
@NotNull
public ColorTool getColorTool() {
return colorTool;
public ContentDisplay withSendHandler(@NotNull SendHandler handler) {
sendHandler = handler;
return this;
}
/**
* @return Gets the filter used.
*/
@NotNull
public ContentFilter getFilter() {
return filter;
}
/**
* Gets the value for a given setting option.
* Format and display the message to command sender.
*
* @param setting The setting option.
* @param <S> The setting type.
* @return Value set for the given setting.
* @param sender The target command sender to show the display to.
*/
public <S> S getSetting(@NotNull DisplaySetting<S> setting) {
return (S) settingsMap.getOrDefault(setting, setting.defaultValue());
}
/**
* Sets other specific settings that may be used by the {@link DisplayHandler}.
*
* @param setting The settings option.
* @param value The value to set.
* @param <S> The type of setting.
*/
public <S> void setSetting(@NotNull DisplaySetting<S> setting, S value) {
this.settingsMap.put(setting, value);
}
/**
* Builds a {@link ContentDisplay}.
*
* @param <T> Type of content to display.
*/
public static class Builder<T> {
private final ContentDisplay<T> display;
private Builder(T content) {
this.display = new ContentDisplay<>(content);
}
/**
* Sets header to be displayed.
*
* @param header The header text.
* @param replacements String formatting replacements.
* @return The builder.
*/
@NotNull
public Builder<T> header(@NotNull String header, Object...replacements) {
this.display.header = String.format(header, replacements);
return this;
}
/**
* Sets the message to show when no content is available for display.
*
* @param emptyMessage The message text.
* @param replacements String formatting replacements.
* @return The builder.
*/
@NotNull
public Builder<T> emptyMessage(@NotNull String emptyMessage, Object...replacements) {
this.display.emptyMessage = String.format(emptyMessage, replacements);
return this;
}
/**
* Sets the display handler that does the formatting and sending of content. <b>Required.</b>
*
* @param displayHandler The display handler for the given content type.
* @return The builder.
*/
@NotNull
public Builder<T> displayHandler(@NotNull DisplayHandler<T> displayHandler) {
this.display.displayHandler = displayHandler;
return this;
}
/**
* Sets the color tool used to make messages more colourful.
*
* @param colorTool The color tool to use.
* @return The builder.
*/
@NotNull
public Builder<T> colorTool(@NotNull ColorTool colorTool) {
this.display.colorTool = colorTool;
return this;
}
/**
* Sets content filter used to match specific content to be displayed.
*
* @param filter The filter to use.
* @return The builder.
*/
@NotNull
public Builder<T> filter(@NotNull ContentFilter filter) {
this.display.filter = filter;
return this;
}
/**
* Sets other specific settings that may be used by the {@link DisplayHandler}.
*
* @param setting The settings option.
* @param value The value to set.
* @param <S> The type of setting.
* @return The builder.
*/
@NotNull
public <S> Builder<T> setting(@NotNull DisplaySetting<S> setting, S value) {
this.display.settingsMap.put(setting, value);
return this;
}
/**
* Validates and build the content display.
*
* @return The content display.
*/
@NotNull
public ContentDisplay<T> build() {
Objects.requireNonNull(this.display.displayHandler);
return this.display;
}
/**
* Build and show the content to the sender.
*
* @param sender The CommandSender to show the display to.
*/
public void show(CommandSender sender) {
this.build().show(sender);
}
public void send(@NotNull CommandSender sender) {
Objects.requireNonNull(sendHandler, "No send handler set for content display");
List<String> parsedContent = new ArrayList<>();
contentParsers.forEach(parser -> parser.parse(sender, parsedContent));
sendHandler.send(sender, parsedContent);
}
}

View File

@ -1,153 +0,0 @@
package com.onarandombox.MultiverseCore.display;
import com.dumptruckman.minecraft.util.Logging;
import org.bukkit.ChatColor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
/**
* <p>Filter content and text based on regex matching.</p>
*
* <p>Compile regex pattern based on {@link ContentFilter#filterString}. When prefixed with 'r=',
* use {@link ContentFilter#filterString} as the full regex pattern. Else, set to any match that
* contains the {@link ContentFilter#filterString}.<p>
*/
public class ContentFilter {
public static final ContentFilter DEFAULT = new ContentFilter();
private static final Pattern REGEX_SPECIAL_CHARS = Pattern.compile("[.+*?\\[^\\]$(){}=!<>|:-\\\\]");
private String filterString;
private Pattern filterPattern;
private boolean exactMatch;
private ContentFilter() {
}
/**
* @param filterString The text to do matching, either plaintext or regex.
*/
public ContentFilter(@NotNull String filterString) {
this(filterString, false);
}
/**
* @param filterString The text to do matching, else plaintext or regex.
* @param exactMatch Should check for exact match when doing regex matching.
*/
public ContentFilter(@NotNull String filterString,
boolean exactMatch) {
this.filterString = filterString;
this.exactMatch = exactMatch;
parseFilter();
}
private void parseFilter() {
if (filterString == null) {
return;
}
if (filterString.startsWith("r=")) {
convertToMatcher(filterString.substring(2));
return;
}
String cleanedFilter = REGEX_SPECIAL_CHARS.matcher(filterString.toLowerCase()).replaceAll("\\\\$0");
convertToMatcher("(?i).*" + cleanedFilter + ".*");
}
/**
* Compile and store the regex into a {@link Pattern}.
*
* @param regex The regex text.
*/
private void convertToMatcher(@NotNull String regex) {
try {
this.filterPattern = Pattern.compile(regex);
Logging.finest("Parsed regex pattern: %s", this.filterPattern.toString());
}
catch (PatternSyntaxException ignored) {
Logging.warning("Error parsing regex: %s", filterString);
}
}
/**
* Do regex matching.
*
* @param text String to check regex on.
* @return True of matches regex pattern, false otherwise.
*/
public boolean checkMatch(@Nullable Object text) {
if (!hasFilter()) {
return true;
}
if (text == null || !hasValidPattern()) {
return false;
}
text = ChatColor.stripColor(String.valueOf(text));
return (exactMatch)
? filterPattern.matcher((CharSequence) text).matches()
: filterPattern.matcher((CharSequence) text).find();
}
/**
* Checks if a filter string is present.
*
* @return True if there is a filter string, else false.
*/
public boolean hasFilter() {
return filterString != null;
}
/**
* Checks if regex pattern syntax is valid.
*
* @return True if valid, else false.
*/
public boolean hasValidPattern() {
return filterPattern != null;
}
/**
* @return The filter string.
*/
@Nullable
public String getString() {
return filterString;
}
/**
* @return The regex pattern.
*/
@Nullable
public Pattern getPattern() {
return filterPattern;
}
/**
* @return True if filter is set to do exact matching, else false.
*/
public boolean isExactMatch() {
return exactMatch;
}
/**
* Nicely format the filter string to be used for showing the sender.
*
* @return The formatted filter string.
*/
public @NotNull String getFormattedString() {
return String.format("%sFilter: '%s'", ChatColor.ITALIC, filterString);
}
@Override
public String toString() {
return "ContentFilter{" +
"filterString='" + filterString + '\'' +
", filterPattern=" + filterPattern +
", exactMatch=" + exactMatch +
'}';
}
}

View File

@ -1,25 +0,0 @@
package com.onarandombox.MultiverseCore.display;
/**
* Thrown when an issue occur while formatting content.
*/
public class DisplayFormatException extends Exception {
public DisplayFormatException() {
}
public DisplayFormatException(String message) {
super(message);
}
public DisplayFormatException(String message, Throwable cause) {
super(message, cause);
}
public DisplayFormatException(Throwable cause) {
super(cause);
}
public DisplayFormatException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}

View File

@ -1,68 +0,0 @@
package com.onarandombox.MultiverseCore.display;
import com.google.common.base.Strings;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
/**
* Handles the formatting and sending of all content by the {@link ContentDisplay}.
*
* @param <T> Type of content to display.
*/
@FunctionalInterface
public interface DisplayHandler<T> {
/**
* Formats the raw content into a {@link Collection<String>} for displaying to the given sender.
*
* @param sender The {@link CommandSender} who will the content will be displayed to.
* @param display The responsible {@link ContentDisplay}.
* @return The formatted content.
* @throws DisplayFormatException Issue occurred while formatting content. E.g. invalid page.
*/
Collection<String> format(@NotNull CommandSender sender, @NotNull ContentDisplay<T> display)
throws DisplayFormatException;
/**
* Sends the header.
*
* @param sender The {@link CommandSender} who will the header will be displayed to.
* @param display The responsible {@link ContentDisplay}.
*/
default void sendHeader(@NotNull CommandSender sender, @NotNull ContentDisplay<T> display) {
if (!Strings.isNullOrEmpty(display.getHeader())) {
sender.sendMessage(display.getHeader());
}
}
/**
* Sends info such as filter and page.
*
* @param sender The {@link CommandSender} who will the sub header will be displayed to.
* @param display The responsible {@link ContentDisplay}.
*/
default void sendSubHeader(@NotNull CommandSender sender, @NotNull ContentDisplay<T> display) {
if (display.getFilter().hasFilter()) {
sender.sendMessage(String.format("%s[ %s ]", ChatColor.GRAY, display.getFilter().getFormattedString()));
}
}
/**
* Sends the content.
*
* @param sender The {@link CommandSender} who will the body will be displayed to.
* @param display The responsible {@link ContentDisplay}.
* @param formattedContent The content after being formatted by {@link #format(CommandSender, ContentDisplay)}
*/
default void sendBody(@NotNull CommandSender sender, @NotNull ContentDisplay<T> display,
Collection<String> formattedContent) {
if (formattedContent == null || formattedContent.size() == 0) {
sender.sendMessage(display.getEmptyMessage());
return;
}
sender.sendMessage(formattedContent.toArray(new String[0]));
}
}

View File

@ -1,47 +0,0 @@
package com.onarandombox.MultiverseCore.display;
import com.onarandombox.MultiverseCore.display.handlers.InlineListDisplayHandler;
import com.onarandombox.MultiverseCore.display.handlers.InlineMapDisplayHandler;
import com.onarandombox.MultiverseCore.display.handlers.ListDisplayHandler;
import com.onarandombox.MultiverseCore.display.handlers.PagedListDisplayHandler;
import com.onarandombox.MultiverseCore.display.settings.InlineDisplaySettings;
import com.onarandombox.MultiverseCore.display.settings.PagedDisplaySettings;
import com.onarandombox.MultiverseCore.display.settings.MapDisplaySettings;
import java.util.Collection;
import java.util.Map;
/**
* Various implementations of {@link DisplayHandler}.
*/
public class DisplayHandlers {
/**
* Standard list display.
*
* Supported settings: none.
*/
public static final DisplayHandler<Collection<String>> LIST = new ListDisplayHandler();
/**
* List display with paging.
*
* Supported settings: {@link PagedDisplaySettings#SHOW_PAGE}, {@link PagedDisplaySettings#LINES_PER_PAGE},
* {@link PagedDisplaySettings#PAGE_IN_CONSOLE}, {@link PagedDisplaySettings#DO_END_PADDING}.
*/
public static final DisplayHandler<Collection<String>> PAGE_LIST = new PagedListDisplayHandler();
/**
* Display a list inline.
*
* Supported settings: {@link InlineDisplaySettings#SEPARATOR}.
*/
public static final DisplayHandler<Collection<String>> INLINE_LIST = new InlineListDisplayHandler();
/**
* Display key value pair inline.
*
* Supported settings: {@link InlineDisplaySettings#SEPARATOR}, {@link MapDisplaySettings#OPERATOR}.
*/
public static final DisplayHandler<Map<String, Object>> INLINE_MAP = new InlineMapDisplayHandler();
}

View File

@ -0,0 +1,21 @@
package com.onarandombox.MultiverseCore.display.filters;
/**
* Filter display content for it show only certain string.
*/
public interface ContentFilter {
/**
* Checks if a particular string should be displayed.
*
* @param value String to check on.
* @return True if should be display, false otherwise.
*/
boolean checkMatch(String value);
/**
* Gets whether content needs to be filtered by this filter.
*
* @return True if content should be filtered, false otherwise.
*/
boolean needToFilter();
}

View File

@ -0,0 +1,35 @@
package com.onarandombox.MultiverseCore.display.filters;
/**
* Default implementation of {@link ContentFilter} that doesn't filter anything.
*/
public class DefaultContentFilter implements ContentFilter {
public static DefaultContentFilter instance;
public static DefaultContentFilter getInstance() {
if (instance == null) {
instance = new DefaultContentFilter();
}
return instance;
}
private DefaultContentFilter() {
}
/**
* {@inheritDoc}
*/
@Override
public boolean checkMatch(String value) {
return true;
}
/**
* {@inheritDoc}
*/
@Override
public boolean needToFilter() {
return false;
}
}

View File

@ -0,0 +1,99 @@
package com.onarandombox.MultiverseCore.display.filters;
import com.dumptruckman.minecraft.util.Logging;
import com.google.common.base.Strings;
import org.bukkit.ChatColor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
/**
* Filter content and text based on regex matching.
*/
public class RegexContentFilter implements ContentFilter {
private static final Pattern REGEX_SPECIAL_CHARS = Pattern.compile("[.+*?\\[^\\]$(){}=!<>|:-\\\\]");
/**
* Compile regex pattern to create a regex filter.
*
* When prefixed with 'r=', filter string is used as the full regex pattern.
* Else, set to regex that contains the filterString.
*
* @param filterString The target string to create filter.
* @return A new instance of {@link RegexContentFilter} with filter applied.
*/
@NotNull
public static RegexContentFilter fromString(@Nullable String filterString) {
if (filterString == null) {
return new RegexContentFilter(null);
}
if (filterString.startsWith("r=")) {
return new RegexContentFilter(filterString.substring(2));
}
String cleanedFilter = REGEX_SPECIAL_CHARS.matcher(filterString.toLowerCase()).replaceAll("\\\\$0");
return new RegexContentFilter("(?i).*" + cleanedFilter + ".*");
}
private final String regexString;
private Pattern regexPattern;
public RegexContentFilter(@Nullable String regexString) {
this.regexString = regexString;
convertToPattern();
}
/**
* Try to compile and store the regex into a {@link Pattern}.
*/
private void convertToPattern() {
if (Strings.isNullOrEmpty(regexString)) {
return;
}
try {
regexPattern = Pattern.compile(regexString);
} catch (PatternSyntaxException ignored) {
regexPattern = null;
Logging.fine("Error parsing regex: %s", regexString);
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean checkMatch(String value) {
if (!hasValidRegex()) {
return false;
}
String text = ChatColor.stripColor(String.valueOf(value));
return regexPattern.matcher(text).find();
}
/**
* {@inheritDoc}
*/
@Override
public boolean needToFilter() {
return hasValidRegex();
}
public boolean hasValidRegex() {
return regexPattern != null;
}
public String getRegexString() {
return regexString;
}
public Pattern getRegexPattern() {
return regexPattern;
}
@Override
public String toString() {
return regexString;
}
}

View File

@ -0,0 +1,99 @@
package com.onarandombox.MultiverseCore.display.handlers;
import com.google.common.base.Strings;
import com.onarandombox.MultiverseCore.display.filters.ContentFilter;
import com.onarandombox.MultiverseCore.display.filters.DefaultContentFilter;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.stream.Collectors;
/**
* Base implementation of {@link SendHandler} with some common parameters.
*
* @param <T> The subclass that inherited this baseclass.
*/
public abstract class BaseSendHandler<T extends BaseSendHandler<?>> implements SendHandler {
protected String header = "";
protected ContentFilter filter = DefaultContentFilter.getInstance();
/**
* {@inheritDoc}
*/
@Override
public void send(@NotNull CommandSender sender, @NotNull List<String> content) {
sendHeader(sender);
List<String> filteredContent = filterContent(content);
if (filteredContent.isEmpty()) {
sender.sendMessage(String.format("%sThere is no content to display.", ChatColor.RED));
return;
}
sendContent(sender, filteredContent);
}
/**
* Sends the header if header is present.
*
* @param sender The target which the header will be displayed to.
*/
protected void sendHeader(CommandSender sender) {
if (!Strings.isNullOrEmpty(header)) {
sender.sendMessage(header);
}
}
/**
* Filter to keep only contents that matches the filter.
*
* @param content The content to filter on.
* @return The filtered list of content.
*/
protected List<String> filterContent(@NotNull List<String> content) {
if (filter.needToFilter()) {
return content.stream().filter(filter::checkMatch).collect(Collectors.toList());
}
return content;
}
/**
* Display the contents.
*
* @param sender The target which the content will be displayed to.
* @param content The content to display.
*/
protected abstract void sendContent(@NotNull CommandSender sender, @NotNull List<String> content);
/**
* Sets header to be displayed.
*
* @param header The header text.
* @param replacements String formatting replacements.
* @return Same {@link T} for method chaining.
*/
public T withHeader(@NotNull String header, @NotNull Object...replacements) {
this.header = String.format(header, replacements);
return (T) this;
}
/**
* Sets content filter used to match specific content to be displayed.
*
* @param filter The filter to use.
* @return Same {@link T} for method chaining.
*/
public T withFilter(@NotNull ContentFilter filter) {
this.filter = filter;
return (T) this;
}
public String getHeader() {
return header;
}
public ContentFilter getFilter() {
return filter;
}
}

View File

@ -0,0 +1,29 @@
package com.onarandombox.MultiverseCore.display.handlers;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import java.util.List;
public class DefaultSendHandler implements SendHandler {
private static DefaultSendHandler instance;
public static DefaultSendHandler getInstance() {
if (instance == null) {
instance = new DefaultSendHandler();
}
return instance;
}
private DefaultSendHandler() {
}
/**
* {@inheritDoc}
*/
@Override
public void send(@NotNull CommandSender sender, @NotNull List<String> content) {
sender.sendMessage(content.toArray(new String[0]));
}
}

View File

@ -1,36 +0,0 @@
package com.onarandombox.MultiverseCore.display.handlers;
import com.onarandombox.MultiverseCore.display.ContentDisplay;
import com.onarandombox.MultiverseCore.display.DisplayFormatException;
import com.onarandombox.MultiverseCore.display.DisplayHandler;
import com.onarandombox.MultiverseCore.display.settings.InlineDisplaySettings;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
public class InlineListDisplayHandler implements DisplayHandler<Collection<String>> {
@Override
public Collection<String> format(@NotNull CommandSender sender, @NotNull ContentDisplay<Collection<String>> display)
throws DisplayFormatException {
StringBuilder builder = new StringBuilder();
String separator = display.getSetting(InlineDisplaySettings.SEPARATOR);
for (Iterator<String> iterator = display.getContents().iterator(); iterator.hasNext(); ) {
String content = iterator.next();
if (!display.getFilter().checkMatch(content)) {
continue;
}
builder.append(display.getColorTool().get()).append(content);
if (iterator.hasNext()) {
builder.append(separator);
}
}
return (builder.length() == 0)
? Collections.singletonList(display.getEmptyMessage())
: Collections.singleton(builder.toString());
}
}

View File

@ -1,44 +0,0 @@
package com.onarandombox.MultiverseCore.display.handlers;
import com.onarandombox.MultiverseCore.display.ContentDisplay;
import com.onarandombox.MultiverseCore.display.DisplayFormatException;
import com.onarandombox.MultiverseCore.display.DisplayHandler;
import com.onarandombox.MultiverseCore.display.settings.InlineDisplaySettings;
import com.onarandombox.MultiverseCore.display.settings.MapDisplaySettings;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
public class InlineMapDisplayHandler implements DisplayHandler<Map<String, Object>> {
@Override
public Collection<String> format(@NotNull CommandSender sender,
@NotNull ContentDisplay<Map<String, Object>> display)
throws DisplayFormatException {
StringBuilder builder = new StringBuilder();
String separator = display.getSetting(InlineDisplaySettings.SEPARATOR);
String operator = display.getSetting(MapDisplaySettings.OPERATOR);
for (Iterator<Map.Entry<String, Object>> iterator = display.getContents().entrySet().iterator(); iterator.hasNext(); ) {
Map.Entry<String, Object> entry = iterator.next();
if (!display.getFilter().checkMatch(entry.getKey()) && !display.getFilter().checkMatch(entry.getValue())) {
continue;
}
builder.append(display.getColorTool().get())
.append(entry.getKey())
.append(operator)
.append(display.getColorTool().get())
.append(entry.getValue());
if (iterator.hasNext()) {
builder.append(separator);
}
}
return (builder.length() == 0)
? Collections.singletonList(display.getEmptyMessage())
: Collections.singleton(builder.toString());
}
}

View File

@ -0,0 +1,53 @@
package com.onarandombox.MultiverseCore.display.handlers;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* Display the contents in a single line.
*/
public class InlineSendHandler extends BaseSendHandler<InlineSendHandler> {
/**
* Makes a new {@link InlineSendHandler} instance to use.
*
* @return New {@link InlineSendHandler} instance.
*/
public static InlineSendHandler create() {
return new InlineSendHandler();
}
private String delimiter = ChatColor.WHITE + ", ";
public InlineSendHandler() {
}
/**
* {@inheritDoc}
*/
@Override
public void sendContent(@NotNull CommandSender sender, @NotNull List<String> content) {
if (filter.needToFilter()) {
sender.sendMessage(String.format("%s[Filter '%s']", ChatColor.GRAY, filter));
}
sender.sendMessage(String.join(delimiter, content));
}
/**
* Sets the delimiter. A sequence of characters that is used to separate each of the elements in content.
*
* @param delimiter The delimiter to use.
* @return Same {@link InlineSendHandler} for method chaining.
*/
public InlineSendHandler withDelimiter(String delimiter) {
this.delimiter = delimiter;
return this;
}
public String getDelimiter() {
return delimiter;
}
}

View File

@ -1,22 +0,0 @@
package com.onarandombox.MultiverseCore.display.handlers;
import com.onarandombox.MultiverseCore.display.ContentDisplay;
import com.onarandombox.MultiverseCore.display.DisplayFormatException;
import com.onarandombox.MultiverseCore.display.DisplayHandler;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.stream.Collectors;
public class ListDisplayHandler implements DisplayHandler<Collection<String>> {
@Override
public Collection<String> format(@NotNull CommandSender sender, @NotNull ContentDisplay<Collection<String>> display)
throws DisplayFormatException {
return display.getContents().stream()
.filter(display.getFilter()::checkMatch)
.map(s -> (ContentDisplay.LINE_BREAK.equals(s)) ? "" : display.getColorTool().get() + s)
.collect(Collectors.toList());
}
}

View File

@ -1,105 +0,0 @@
package com.onarandombox.MultiverseCore.display.handlers;
import com.onarandombox.MultiverseCore.display.ContentDisplay;
import com.onarandombox.MultiverseCore.display.DisplayFormatException;
import com.onarandombox.MultiverseCore.display.settings.PagedDisplaySettings;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.IntStream;
public class PagedListDisplayHandler extends ListDisplayHandler {
@Override
public Collection<String> format(@NotNull CommandSender sender, @NotNull ContentDisplay<Collection<String>> display)
throws DisplayFormatException {
if (dontNeedPaging(sender, display)) {
return super.format(sender, display);
}
int pages = 1;
int currentLength = 0;
int targetPage = display.getSetting(PagedDisplaySettings.SHOW_PAGE);
int linesPerPage = display.getSetting(PagedDisplaySettings.LINES_PER_PAGE);
List<String> content = new ArrayList<>(linesPerPage);
// Calculate the paging.
for (String line : display.getContents()) {
if (!display.getFilter().checkMatch(line)) {
continue;
}
// When it's the next page.
boolean isLineBreak = ContentDisplay.LINE_BREAK.equals(line);
if (isLineBreak || ++currentLength > linesPerPage) {
pages++;
currentLength = 0;
if (isLineBreak) {
continue;
}
}
if (pages == targetPage) {
// Let first line be the header when no header is defined.
if (display.getHeader() == null) {
display.setHeader(line);
currentLength--;
continue;
}
content.add(display.getColorTool().get() + line);
}
}
// Page out of range.
if (targetPage < 1 || targetPage > pages) {
if (pages == 1) {
throw new DisplayFormatException("There is only 1 page!");
}
throw new DisplayFormatException("Please enter a page from 1 to " + pages + ".");
}
// No content
if (content.size() == 0) {
content.add(display.getEmptyMessage());
}
// Add empty lines to make output length consistent.
if (display.getSetting(PagedDisplaySettings.DO_END_PADDING)) {
IntStream.range(0, linesPerPage - content.size()).forEach(i -> content.add(""));
}
display.setSetting(PagedDisplaySettings.TOTAL_PAGE, pages);
return content;
}
@Override
public void sendSubHeader(@NotNull CommandSender sender, @NotNull ContentDisplay<Collection<String>> display) {
if (dontNeedPaging(sender, display)) {
super.sendSubHeader(sender, display);
return;
}
if (display.getFilter().hasFilter()) {
sender.sendMessage(String.format("%s[ Page %s of %s, %s ]",
ChatColor.GRAY,
display.getSetting(PagedDisplaySettings.SHOW_PAGE),
display.getSetting(PagedDisplaySettings.TOTAL_PAGE),
display.getFilter().getFormattedString())
);
return;
}
sender.sendMessage(String.format("%s[ Page %s of %s ]",
ChatColor.GRAY,
display.getSetting(PagedDisplaySettings.SHOW_PAGE),
display.getSetting(PagedDisplaySettings.TOTAL_PAGE))
);
}
private boolean dontNeedPaging(CommandSender sender, ContentDisplay<Collection<String>> display) {
return sender instanceof ConsoleCommandSender
&& !display.getSetting(PagedDisplaySettings.PAGE_IN_CONSOLE);
}
}

View File

@ -0,0 +1,170 @@
package com.onarandombox.MultiverseCore.display.handlers;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.bukkit.command.ConsoleCommandSender;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* Display content as a list with optional pagination.
*/
public class PagedSendHandler extends BaseSendHandler<PagedSendHandler> {
/**
* Makes a new {@link PagedSendHandler} instance to use.
*
* @return New {@link PagedSendHandler} instance.
*/
public static PagedSendHandler create() {
return new PagedSendHandler();
}
private boolean paginate = true;
private boolean paginateInConsole = false;
private boolean padEnd = true;
private int linesPerPage = 8;
private int targetPage = 1;
public PagedSendHandler() {
}
/**
* {@inheritDoc}
*/
@Override
public void sendContent(@NotNull CommandSender sender,
@NotNull List<String> content) {
if (!paginate || (sender instanceof ConsoleCommandSender && !paginateInConsole)) {
sendNormal(sender, content);
return;
}
sendPaged(sender, content);
}
/**
* Send content list without pagination.
*
* @param sender The target which the content will be displayed to.
* @param content The content to display.
*/
private void sendNormal(@NotNull CommandSender sender,
@NotNull List<String> content) {
if (filter.needToFilter()) {
sender.sendMessage(String.format("%s[Filter '%s']", ChatColor.GRAY, filter));
}
sender.sendMessage(content.toArray(new String[0]));
}
/**
* Send content list with pagination.
*
* @param sender The target which the content will be displayed to.
* @param content The content to display.
*/
private void sendPaged(@NotNull CommandSender sender,
@NotNull List<String> content) {
int totalPages = (content.size() + linesPerPage - 1) / linesPerPage; // Basically just divide round up
if (targetPage < 1 || targetPage > totalPages) {
sender.sendMessage(String.format("%sInvalid page number. Please enter a page number between 1 and %s", ChatColor.RED, totalPages));
return;
}
if (filter.needToFilter()) {
sender.sendMessage(String.format("%s[Page %s of %s] [Filter '%s']", ChatColor.GRAY, targetPage, totalPages, filter));
} else {
sender.sendMessage(String.format("%s[Page %s of %s]", ChatColor.GRAY, targetPage, totalPages));
}
int startIndex = (targetPage - 1) * linesPerPage;
int pageEndIndex = startIndex + linesPerPage;
int endIndex = Math.min(pageEndIndex, content.size());
List<String> pageContent = content.subList(startIndex, endIndex);
if (padEnd) {
for (int i = 0; i < (pageEndIndex - endIndex); i++) {
pageContent.add("");
}
}
sender.sendMessage(pageContent.toArray(new String[0]));
}
/**
* Sets whether display output should be paginated.
*
* @param paginate State of doing pagination.
* @return Same {@link PagedSendHandler} for method chaining.
*/
public PagedSendHandler doPagination(boolean paginate) {
this.paginate = paginate;
return this;
}
/**
* Sets whether display output should be paginated if is for console output.
* This option will be useless of {@link PagedSendHandler#paginate} is set to false.
*
* @param paginateInConsole State of doing pagination in console.
* @return Same {@link PagedSendHandler} for method chaining.
*/
public PagedSendHandler doPaginationInConsole(boolean paginateInConsole) {
this.paginateInConsole = paginateInConsole;
return this;
}
/**
* Sets whether empty lines should be added if content lines shown is less that {@link PagedSendHandler#linesPerPage}.
*
* @param padEnd State of doing end padding.
* @return Same {@link PagedSendHandler} for method chaining.
*/
public PagedSendHandler doEndPadding(boolean padEnd) {
this.padEnd = padEnd;
return this;
}
/**
* Sets the max number of lines per page. This excludes header.
*
* @param linesPerPage The number of lines per page.
* @return Same {@link PagedSendHandler} for method chaining.
*/
public PagedSendHandler withLinesPerPage(int linesPerPage) {
this.linesPerPage = linesPerPage;
return this;
}
/**
* Sets the page number to display.
*
* @param targetPage The target page number to display.
* @return Same {@link PagedSendHandler} for method chaining.
*/
public PagedSendHandler withTargetPage(int targetPage) {
this.targetPage = targetPage;
return this;
}
public boolean isPaginate() {
return paginate;
}
public boolean isPaginateInConsole() {
return paginateInConsole;
}
public boolean isPadEnd() {
return padEnd;
}
public int getLinesPerPage() {
return linesPerPage;
}
public int getTargetPage() {
return targetPage;
}
}

View File

@ -0,0 +1,20 @@
package com.onarandombox.MultiverseCore.display.handlers;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* Handles the sending of all content to the command sender.
*/
@FunctionalInterface
public interface SendHandler {
/**
* Sends all the content to the given command sender.
*
* @param sender The target which the content will be displayed to.
* @param content The content to display.
*/
void send(@NotNull CommandSender sender, @NotNull List<String> content);
}

View File

@ -0,0 +1,20 @@
package com.onarandombox.MultiverseCore.display.parsers;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* Parse objects into string or list of strings.
*/
@FunctionalInterface
public interface ContentParser {
/**
* Parse the object to string(s) and add it to the content.
*
* @param sender The target which the content will be displayed to.
* @param content The content to display.
*/
void parse(@NotNull CommandSender sender, @NotNull List<String> content);
}

View File

@ -0,0 +1,60 @@
package com.onarandombox.MultiverseCore.display.parsers;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* Simple parser for list object.
*
* @param <T> List element type.
*/
public class ListContentParser<T> implements ContentParser {
/**
* New list content parser for the given list.
*
* @param list The list object to parse.
* @param <T> List element type.
* @return New {@link MapContentParser} instance.
*/
public static <T> ListContentParser<T> forContent(List<T> list) {
return new ListContentParser<>(list);
}
private final List<T> list;
private String format = "%s";
public ListContentParser(List<T> list) {
this.list = list;
}
/**
* {@inheritDoc}
*/
@Override
public void parse(@NotNull CommandSender sender, @NotNull List<String> content) {
list.forEach(element -> content.add(String.format(format, element)));
}
/**
* Sets the format that will be used to parse each list entry. Uses java string format pattern.
*
* @param format The format to use.
* @return Same {@link ListContentParser} for method chaining.
*/
public ListContentParser<T> withFormat(String format) {
this.format = format;
return this;
}
public List<T> getList() {
return list;
}
public String getFormat() {
return format;
}
}

View File

@ -0,0 +1,112 @@
package com.onarandombox.MultiverseCore.display.parsers;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.Map;
/**
* Simple parser for map object.
*
* @param <K> Key type.
* @param <V> Value type.
*/
public class MapContentParser<K, V> implements ContentParser {
/**
* New map content parser for the given map.
*
* @param map The map object to parse.
* @param <K> Key type.
* @param <V> Value type.
* @return New {@link MapContentParser} instance.
*/
public static <K, V> MapContentParser<K, V> forContent(Map<K, V> map) {
return new MapContentParser<>(map);
}
private final Map<K, V> map;
private String format = "%s%s%s%s%s";
private ChatColor keyColor = ChatColor.WHITE;
private ChatColor valueColor = ChatColor.WHITE;
private String separator = ": ";
public MapContentParser(Map<K, V> map) {
this.map = map;
}
/**
* {@inheritDoc}
*/
@Override
public void parse(@NotNull CommandSender sender, @NotNull List<String> content) {
map.forEach((k, v) -> content.add(String.format(format, keyColor, k, separator, valueColor, v)));
}
/**
* Sets the format that will be used to parse each map entry. Uses java string format pattern.
*
* @param format The format to use.
* @return Same {@link MapContentParser} for method chaining.
*/
public MapContentParser<K, V> withFormat(String format) {
this.format = format;
return this;
}
/**
* Sets the color for the key text.
*
* @param keyColor The color to use.
* @return Same {@link MapContentParser} for method chaining.
*/
public MapContentParser<K, V> withKeyColor(ChatColor keyColor) {
this.keyColor = keyColor;
return this;
}
/**
* Sets the color for the value text.
*
* @param valueColor The color to use.
* @return Same {@link MapContentParser} for method chaining.
*/
public MapContentParser<K, V> withValueColor(ChatColor valueColor) {
this.valueColor = valueColor;
return this;
}
/**
* Sets the separator between each key value pairing.
*
* @param separator The separator to use.
* @return Same {@link MapContentParser} for method chaining.
*/
public MapContentParser<K, V> withSeparator(String separator) {
this.separator = separator;
return this;
}
public Map<K, V> getMap() {
return map;
}
public String getFormat() {
return format;
}
public ChatColor getKeyColor() {
return keyColor;
}
public ChatColor getValueColor() {
return valueColor;
}
public String getSeparator() {
return separator;
}
}

View File

@ -1,19 +0,0 @@
package com.onarandombox.MultiverseCore.display.settings;
import com.onarandombox.MultiverseCore.display.DisplayHandler;
/**
* Represents a setting option that can be used by {@link DisplayHandler}.
*
* @param <T>
*/
@FunctionalInterface
public interface DisplaySetting<T> {
/**
* Gets the default value of this Display Setting.
*
* @return The default value.
*/
T defaultValue();
}

View File

@ -1,15 +0,0 @@
package com.onarandombox.MultiverseCore.display.settings;
import com.onarandombox.MultiverseCore.display.DisplayHandler;
import org.bukkit.ChatColor;
/**
* Collection of {@link DisplaySetting} that are used by various {@link DisplayHandler}.
*/
public class InlineDisplaySettings {
/**
* Inline separator. E.g. '1, 2, 3'
*/
public static final DisplaySetting<String> SEPARATOR = () -> ChatColor.WHITE + ", ";
}

View File

@ -1,15 +0,0 @@
package com.onarandombox.MultiverseCore.display.settings;
import com.onarandombox.MultiverseCore.display.DisplayHandler;
import org.bukkit.ChatColor;
/**
* Collection of {@link DisplaySetting} that are used by various {@link DisplayHandler}.
*/
public class MapDisplaySettings {
/**
* The thing between a key value pair. E.g. 'Me = Smart'
*/
public static final DisplaySetting<String> OPERATOR = () -> ChatColor.WHITE + " = ";
}

View File

@ -1,30 +0,0 @@
package com.onarandombox.MultiverseCore.display.settings;
public class PagedDisplaySettings {
/**
* Page to display.
*/
public static final DisplaySetting<Integer> SHOW_PAGE = () -> 1;
/**
* Total pages available to display.
*/
public static final DisplaySetting<Integer> TOTAL_PAGE = () -> 1;
/**
* The max number of lines per page. This excludes header.
*/
public static final DisplaySetting<Integer> LINES_PER_PAGE = () -> 8;
/**
* Should add empty lines if content lines shown is less that {@link #LINES_PER_PAGE}.
*/
public static final DisplaySetting<Boolean> DO_END_PADDING = () -> true;
/**
* Should display with paging when it's sent to console.
*/
public static final DisplaySetting<Boolean> PAGE_IN_CONSOLE = () -> false;
}