feat: Improve content display API

- Change ContentProvider to return a list instead of add to existing
- Add prefix for inline handler
- Change from CommandSender to BukkitCommandIssuer, prep for locale
This commit is contained in:
Ben Woo 2023-02-13 23:31:26 +08:00
parent 7978193d88
commit 1c89b6458f
12 changed files with 155 additions and 121 deletions

View File

@ -1,15 +1,15 @@
package com.onarandombox.MultiverseCore.display;
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.ArrayList;
import java.util.List;
import java.util.Objects;
import co.aikar.commands.BukkitCommandIssuer;
import com.onarandombox.MultiverseCore.display.handlers.DefaultSendHandler;
import com.onarandombox.MultiverseCore.display.handlers.SendHandler;
import com.onarandombox.MultiverseCore.display.parsers.ContentProvider;
import org.jetbrains.annotations.NotNull;
/**
* Helps to display contents such as list and maps in a nicely formatted fashion.
*/
@ -25,7 +25,7 @@ public class ContentDisplay {
return new ContentDisplay();
}
private final List<ContentParser> contentParsers = new ArrayList<>();
private final List<ContentProvider> contentParsers = new ArrayList<>();
private SendHandler sendHandler = DefaultSendHandler.getInstance();
public ContentDisplay() {
@ -38,7 +38,7 @@ public class ContentDisplay {
* @return Same {@link ContentDisplay} for method chaining.
*/
@NotNull
public ContentDisplay addContentParser(@NotNull ContentParser parser) {
public ContentDisplay addContent(@NotNull ContentProvider parser) {
contentParsers.add(parser);
return this;
}
@ -58,12 +58,12 @@ public class ContentDisplay {
/**
* Format and display the message to command sender.
*
* @param sender The target command sender to show the display to.
* @param issuer The target command sender to show the display to.
*/
public void send(@NotNull CommandSender sender) {
public void send(@NotNull BukkitCommandIssuer issuer) {
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);
contentParsers.forEach(parser -> parsedContent.addAll(parser.parse(issuer)));
sendHandler.send(issuer, parsedContent);
}
}

View File

@ -32,4 +32,9 @@ public class DefaultContentFilter implements ContentFilter {
public boolean needToFilter() {
return false;
}
@Override
public String toString() {
return "N/A";
}
}

View File

@ -1,14 +1,14 @@
package com.onarandombox.MultiverseCore.display.filters;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
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.
*/
@ -34,7 +34,7 @@ public class RegexContentFilter implements ContentFilter {
return new RegexContentFilter(filterString.substring(2));
}
String cleanedFilter = REGEX_SPECIAL_CHARS.matcher(filterString.toLowerCase()).replaceAll("\\\\$0");
return new RegexContentFilter("(?i).*" + cleanedFilter + ".*");
return new RegexContentFilter(cleanedFilter);
}
private final String regexString;

View File

@ -1,14 +1,15 @@
package com.onarandombox.MultiverseCore.display.handlers;
import java.util.List;
import java.util.stream.Collectors;
import co.aikar.commands.BukkitCommandIssuer;
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;
import org.jetbrains.annotations.Nullable;
/**
* Base implementation of {@link SendHandler} with some common parameters.
@ -19,29 +20,30 @@ public abstract class BaseSendHandler<T extends BaseSendHandler<?>> implements S
protected String header = "";
protected ContentFilter filter = DefaultContentFilter.getInstance();
protected String noContentMessage = String.format("%sThere is no content to display.", ChatColor.RED);
/**
* {@inheritDoc}
*/
@Override
public void send(@NotNull CommandSender sender, @NotNull List<String> content) {
sendHeader(sender);
public void send(@NotNull BukkitCommandIssuer issuer, @NotNull List<String> content) {
sendHeader(issuer);
List<String> filteredContent = filterContent(content);
if (filteredContent.isEmpty()) {
sender.sendMessage(String.format("%sThere is no content to display.", ChatColor.RED));
if (filteredContent.isEmpty() && !Strings.isNullOrEmpty(noContentMessage)) {
issuer.sendMessage(noContentMessage);
return;
}
sendContent(sender, filteredContent);
sendContent(issuer, filteredContent);
}
/**
* Sends the header if header is present.
*
* @param sender The target which the header will be displayed to.
* @param issuer The target which the header will be displayed to.
*/
protected void sendHeader(CommandSender sender) {
protected void sendHeader(BukkitCommandIssuer issuer) {
if (!Strings.isNullOrEmpty(header)) {
sender.sendMessage(header);
issuer.sendMessage(header);
}
}
@ -61,10 +63,10 @@ public abstract class BaseSendHandler<T extends BaseSendHandler<?>> implements S
/**
* Display the contents.
*
* @param sender The target which the content will be displayed to.
* @param issuer 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);
protected abstract void sendContent(@NotNull BukkitCommandIssuer issuer, @NotNull List<String> content);
/**
* Sets header to be displayed.
@ -89,6 +91,17 @@ public abstract class BaseSendHandler<T extends BaseSendHandler<?>> implements S
return (T) this;
}
/**
* Sets the message to be displayed when there is no content to display.
*
* @param message The message to display. Null to disable.
* @return Same {@link T} for method chaining.
*/
public T noContentMessage(@Nullable String message) {
this.noContentMessage = message;
return (T) this;
}
public String getHeader() {
return header;
}
@ -96,4 +109,8 @@ public abstract class BaseSendHandler<T extends BaseSendHandler<?>> implements S
public ContentFilter getFilter() {
return filter;
}
public String getNoContentMessage() {
return noContentMessage;
}
}

View File

@ -1,10 +1,10 @@
package com.onarandombox.MultiverseCore.display.handlers;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import co.aikar.commands.BukkitCommandIssuer;
import org.jetbrains.annotations.NotNull;
public class DefaultSendHandler implements SendHandler {
private static DefaultSendHandler instance;
@ -23,7 +23,7 @@ public class DefaultSendHandler implements SendHandler {
* {@inheritDoc}
*/
@Override
public void send(@NotNull CommandSender sender, @NotNull List<String> content) {
sender.sendMessage(content.toArray(new String[0]));
public void send(@NotNull BukkitCommandIssuer issuer, @NotNull List<String> content) {
content.forEach(issuer::sendMessage);
}
}

View File

@ -1,11 +1,11 @@
package com.onarandombox.MultiverseCore.display.handlers;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import co.aikar.commands.BukkitCommandIssuer;
import org.bukkit.ChatColor;
import org.jetbrains.annotations.NotNull;
/**
* Display the contents in a single line.
*/
@ -21,6 +21,7 @@ public class InlineSendHandler extends BaseSendHandler<InlineSendHandler> {
}
private String delimiter = ChatColor.WHITE + ", ";
private String prefix = null;
public InlineSendHandler() {
}
@ -29,11 +30,15 @@ public class InlineSendHandler extends BaseSendHandler<InlineSendHandler> {
* {@inheritDoc}
*/
@Override
public void sendContent(@NotNull CommandSender sender, @NotNull List<String> content) {
public void sendContent(@NotNull BukkitCommandIssuer issuer, @NotNull List<String> content) {
if (filter.needToFilter()) {
sender.sendMessage(String.format("%s[Filter '%s']", ChatColor.GRAY, filter));
issuer.sendMessage(String.format("%s[Filter '%s']", ChatColor.GRAY, filter));
}
sender.sendMessage(String.join(delimiter, content));
String message = String.join(delimiter, content);
if (prefix != null) {
message = prefix + message;
}
issuer.sendMessage(message);
}
/**
@ -47,7 +52,16 @@ public class InlineSendHandler extends BaseSendHandler<InlineSendHandler> {
return this;
}
public InlineSendHandler withPrefix(String prefix) {
this.prefix = prefix;
return this;
}
public String getDelimiter() {
return delimiter;
}
public String getPrefix() {
return prefix;
}
}

View File

@ -1,12 +1,12 @@
package com.onarandombox.MultiverseCore.display.handlers;
import java.util.List;
import co.aikar.commands.BukkitCommandIssuer;
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.
*/
@ -34,50 +34,44 @@ public class PagedSendHandler extends BaseSendHandler<PagedSendHandler> {
* {@inheritDoc}
*/
@Override
public void sendContent(@NotNull CommandSender sender,
@NotNull List<String> content) {
if (!paginate || (sender instanceof ConsoleCommandSender && !paginateInConsole)) {
sendNormal(sender, content);
public void sendContent(@NotNull BukkitCommandIssuer issuer, @NotNull List<String> content) {
if (!paginate || (issuer instanceof ConsoleCommandSender && !paginateInConsole)) {
sendNormal(issuer, content);
return;
}
sendPaged(sender, content);
sendPaged(issuer, content);
}
/**
* Send content list without pagination.
*
* @param sender The target which the content will be displayed to.
* @param issuer 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) {
private void sendNormal(@NotNull BukkitCommandIssuer issuer, @NotNull List<String> content) {
if (filter.needToFilter()) {
sender.sendMessage(String.format("%s[Filter '%s']", ChatColor.GRAY, filter));
issuer.sendMessage(String.format("%s[Filter '%s']", ChatColor.GRAY, filter));
}
sender.sendMessage(content.toArray(new String[0]));
content.forEach(issuer::sendMessage);
}
/**
* Send content list with pagination.
*
* @param sender The target which the content will be displayed to.
* @param issuer 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) {
private void sendPaged(@NotNull BukkitCommandIssuer issuer, @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));
issuer.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));
issuer.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));
issuer.sendMessage(String.format("%s[Page %s of %s]", ChatColor.GRAY, targetPage, totalPages));
}
int startIndex = (targetPage - 1) * linesPerPage;
@ -89,7 +83,7 @@ public class PagedSendHandler extends BaseSendHandler<PagedSendHandler> {
pageContent.add("");
}
}
sender.sendMessage(pageContent.toArray(new String[0]));
pageContent.forEach(issuer::sendMessage);
}
/**

View File

@ -1,10 +1,10 @@
package com.onarandombox.MultiverseCore.display.handlers;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import co.aikar.commands.BukkitCommandIssuer;
import org.jetbrains.annotations.NotNull;
/**
* Handles the sending of all content to the command sender.
*/
@ -16,5 +16,5 @@ public interface SendHandler {
* @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);
void send(@NotNull BukkitCommandIssuer issuer, @NotNull List<String> content);
}

View File

@ -1,20 +0,0 @@
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,19 @@
package com.onarandombox.MultiverseCore.display.parsers;
import java.util.Collection;
import co.aikar.commands.BukkitCommandIssuer;
import org.jetbrains.annotations.NotNull;
/**
* Parse objects into string or list of strings.
*/
@FunctionalInterface
public interface ContentProvider {
/**
* Parse the object to string(s) and add it to the content.
*
* @param issuer The target which the content will be displayed to.
*/
Collection<String> parse(@NotNull BukkitCommandIssuer issuer);
}

View File

@ -1,33 +1,35 @@
package com.onarandombox.MultiverseCore.display.parsers;
import org.bukkit.command.CommandSender;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
import co.aikar.commands.BukkitCommandIssuer;
import org.jetbrains.annotations.NotNull;
/**
* Simple parser for list object.
*
* @param <T> List element type.
*/
public class ListContentParser<T> implements ContentParser {
public class ListContentProvider<T> implements ContentProvider {
/**
* 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.
* @return New {@link MapContentProvider} instance.
*/
public static <T> ListContentParser<T> forContent(List<T> list) {
return new ListContentParser<>(list);
public static <T> ListContentProvider<T> forContent(List<T> list) {
return new ListContentProvider<>(list);
}
private final List<T> list;
private String format = "%s";
public ListContentParser(List<T> list) {
public ListContentProvider(List<T> list) {
this.list = list;
}
@ -35,17 +37,17 @@ public class ListContentParser<T> implements ContentParser {
* {@inheritDoc}
*/
@Override
public void parse(@NotNull CommandSender sender, @NotNull List<String> content) {
list.forEach(element -> content.add(String.format(format, element)));
public Collection<String> parse(@NotNull BukkitCommandIssuer issuer) {
return list.stream().map(element -> String.format(format, element)).collect(Collectors.toList());
}
/**
* 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.
* @return Same {@link ListContentProvider} for method chaining.
*/
public ListContentParser<T> withFormat(String format) {
public ListContentProvider<T> withFormat(String format) {
this.format = format;
return this;
}

View File

@ -1,11 +1,12 @@
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.Collection;
import java.util.Map;
import java.util.stream.Collectors;
import co.aikar.commands.BukkitCommandIssuer;
import org.bukkit.ChatColor;
import org.jetbrains.annotations.NotNull;
/**
* Simple parser for map object.
@ -13,7 +14,7 @@ import java.util.Map;
* @param <K> Key type.
* @param <V> Value type.
*/
public class MapContentParser<K, V> implements ContentParser {
public class MapContentProvider<K, V> implements ContentProvider {
/**
* New map content parser for the given map.
@ -21,10 +22,10 @@ public class MapContentParser<K, V> implements ContentParser {
* @param map The map object to parse.
* @param <K> Key type.
* @param <V> Value type.
* @return New {@link MapContentParser} instance.
* @return New {@link MapContentProvider} instance.
*/
public static <K, V> MapContentParser<K, V> forContent(Map<K, V> map) {
return new MapContentParser<>(map);
public static <K, V> MapContentProvider<K, V> forContent(Map<K, V> map) {
return new MapContentProvider<>(map);
}
private final Map<K, V> map;
@ -34,7 +35,7 @@ public class MapContentParser<K, V> implements ContentParser {
private ChatColor valueColor = ChatColor.WHITE;
private String separator = ": ";
public MapContentParser(Map<K, V> map) {
public MapContentProvider(Map<K, V> map) {
this.map = map;
}
@ -42,17 +43,19 @@ public class MapContentParser<K, V> implements ContentParser {
* {@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)));
public Collection<String> parse(@NotNull BukkitCommandIssuer issuer) {
return map.entrySet().stream()
.map(e -> String.format(format, keyColor, e.getKey(), separator, valueColor, e.getValue()))
.collect(Collectors.toList());
}
/**
* 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.
* @return Same {@link MapContentProvider} for method chaining.
*/
public MapContentParser<K, V> withFormat(String format) {
public MapContentProvider<K, V> withFormat(String format) {
this.format = format;
return this;
}
@ -61,9 +64,9 @@ public class MapContentParser<K, V> implements ContentParser {
* Sets the color for the key text.
*
* @param keyColor The color to use.
* @return Same {@link MapContentParser} for method chaining.
* @return Same {@link MapContentProvider} for method chaining.
*/
public MapContentParser<K, V> withKeyColor(ChatColor keyColor) {
public MapContentProvider<K, V> withKeyColor(ChatColor keyColor) {
this.keyColor = keyColor;
return this;
}
@ -72,9 +75,9 @@ public class MapContentParser<K, V> implements ContentParser {
* Sets the color for the value text.
*
* @param valueColor The color to use.
* @return Same {@link MapContentParser} for method chaining.
* @return Same {@link MapContentProvider} for method chaining.
*/
public MapContentParser<K, V> withValueColor(ChatColor valueColor) {
public MapContentProvider<K, V> withValueColor(ChatColor valueColor) {
this.valueColor = valueColor;
return this;
}
@ -83,9 +86,9 @@ public class MapContentParser<K, V> implements ContentParser {
* Sets the separator between each key value pairing.
*
* @param separator The separator to use.
* @return Same {@link MapContentParser} for method chaining.
* @return Same {@link MapContentProvider} for method chaining.
*/
public MapContentParser<K, V> withSeparator(String separator) {
public MapContentProvider<K, V> withSeparator(String separator) {
this.separator = separator;
return this;
}