From 34e015cb0132641679b7627f7dc69d1823edfc9c Mon Sep 17 00:00:00 2001 From: Marc Baloup Date: Mon, 8 Jun 2020 16:16:18 +0200 Subject: [PATCH] Aditionnal methods in DisplayUtil + some refactoring --- src/main/java/fr/pandacube/util/DateUtil.java | 60 ----- src/main/java/fr/pandacube/util/DirUtils.java | 6 +- ...gestions.java => SuggestionsSupplier.java} | 32 +-- .../pandacube/util/measurement/TimeUtil.java | 57 +++++ .../pandacube/util/text_display/Display.java | 15 ++ .../util/text_display/DisplayUtil.java | 213 +++++++++++++++++- 6 files changed, 302 insertions(+), 81 deletions(-) delete mode 100644 src/main/java/fr/pandacube/util/DateUtil.java rename src/main/java/fr/pandacube/util/commands/{Suggestions.java => SuggestionsSupplier.java} (79%) diff --git a/src/main/java/fr/pandacube/util/DateUtil.java b/src/main/java/fr/pandacube/util/DateUtil.java deleted file mode 100644 index 4d2e109..0000000 --- a/src/main/java/fr/pandacube/util/DateUtil.java +++ /dev/null @@ -1,60 +0,0 @@ -package fr.pandacube.util; - -import java.util.Calendar; -import java.util.GregorianCalendar; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -public class DateUtil { - - /** - * @see {@link com.earth2me.essentials.utils.DateUtil#parseDateDiff(String, boolean)} - */ - public static long parseDateDiff(String time, boolean future) throws Exception { - Pattern timePattern = Pattern.compile("(?:([0-9]+)\\s*y[a-z]*[,\\s]*)?" + "(?:([0-9]+)\\s*mo[a-z]*[,\\s]*)?" - + "(?:([0-9]+)\\s*w[a-z]*[,\\s]*)?" + "(?:([0-9]+)\\s*d[a-z]*[,\\s]*)?" - + "(?:([0-9]+)\\s*h[a-z]*[,\\s]*)?" + "(?:([0-9]+)\\s*m[a-z]*[,\\s]*)?" - + "(?:([0-9]+)\\s*(?:s[a-z]*)?)?", Pattern.CASE_INSENSITIVE); - Matcher m = timePattern.matcher(time); - int years = 0; - int months = 0; - int weeks = 0; - int days = 0; - int hours = 0; - int minutes = 0; - int seconds = 0; - boolean found = false; - while (m.find()) { - if (m.group() == null || m.group().isEmpty()) continue; - for (int i = 0; i < m.groupCount(); i++) - if (m.group(i) != null && !m.group(i).isEmpty()) { - found = true; - break; - } - if (found) { - if (m.group(1) != null && !m.group(1).isEmpty()) years = Integer.parseInt(m.group(1)); - if (m.group(2) != null && !m.group(2).isEmpty()) months = Integer.parseInt(m.group(2)); - if (m.group(3) != null && !m.group(3).isEmpty()) weeks = Integer.parseInt(m.group(3)); - if (m.group(4) != null && !m.group(4).isEmpty()) days = Integer.parseInt(m.group(4)); - if (m.group(5) != null && !m.group(5).isEmpty()) hours = Integer.parseInt(m.group(5)); - if (m.group(6) != null && !m.group(6).isEmpty()) minutes = Integer.parseInt(m.group(6)); - if (m.group(7) != null && !m.group(7).isEmpty()) seconds = Integer.parseInt(m.group(7)); - break; - } - } - if (!found) throw new Exception("Format de durée invalide"); - Calendar c = new GregorianCalendar(); - if (years > 0) c.add(Calendar.YEAR, years * (future ? 1 : -1)); - if (months > 0) c.add(Calendar.MONTH, months * (future ? 1 : -1)); - if (weeks > 0) c.add(Calendar.WEEK_OF_YEAR, weeks * (future ? 1 : -1)); - if (days > 0) c.add(Calendar.DAY_OF_MONTH, days * (future ? 1 : -1)); - if (hours > 0) c.add(Calendar.HOUR_OF_DAY, hours * (future ? 1 : -1)); - if (minutes > 0) c.add(Calendar.MINUTE, minutes * (future ? 1 : -1)); - if (seconds > 0) c.add(Calendar.SECOND, seconds * (future ? 1 : -1)); - Calendar max = new GregorianCalendar(); - max.add(Calendar.YEAR, 10); - if (c.after(max)) return max.getTimeInMillis(); - return c.getTimeInMillis(); - } - -} diff --git a/src/main/java/fr/pandacube/util/DirUtils.java b/src/main/java/fr/pandacube/util/DirUtils.java index a1d378b..4268024 100644 --- a/src/main/java/fr/pandacube/util/DirUtils.java +++ b/src/main/java/fr/pandacube/util/DirUtils.java @@ -3,11 +3,13 @@ package fr.pandacube.util; import java.io.File; public class DirUtils { + + + public static void delete(final File target) { if (target.isDirectory()) - for (final File child : target.listFiles()) + for (File child : target.listFiles()) delete(child); - target.delete(); } } diff --git a/src/main/java/fr/pandacube/util/commands/Suggestions.java b/src/main/java/fr/pandacube/util/commands/SuggestionsSupplier.java similarity index 79% rename from src/main/java/fr/pandacube/util/commands/Suggestions.java rename to src/main/java/fr/pandacube/util/commands/SuggestionsSupplier.java index c2ca298..0701a52 100644 --- a/src/main/java/fr/pandacube/util/commands/Suggestions.java +++ b/src/main/java/fr/pandacube/util/commands/SuggestionsSupplier.java @@ -12,10 +12,10 @@ import java.util.stream.Stream; import fr.pandacube.util.ListUtil; @FunctionalInterface -public interface Suggestions { +public interface SuggestionsSupplier { /** - * Number of suggestion visible at once without having to scrolleeeeeeeeeeeeeeee + * Number of suggestion visible at once without having to scroll */ public static int VISIBLE_SUGGESTION_COUNT = 10; @@ -45,33 +45,33 @@ public interface Suggestions { - public static Suggestions empty() { return (s, ti, t, a) -> Collections.emptyList(); } + public static SuggestionsSupplier empty() { return (s, ti, t, a) -> Collections.emptyList(); } - public static Suggestions fromCollection(Collection suggestions) { + public static SuggestionsSupplier fromCollection(Collection suggestions) { return (s, ti, token, a) -> collectFilteredStream(suggestions.stream(), token); } - public static Suggestions fromArray(String... suggestions) { + public static SuggestionsSupplier fromArray(String... suggestions) { return (s, ti, token, a) -> collectFilteredStream(Arrays.stream(suggestions), token); } - public static , S> Suggestions fromEnum(Class enumClass) { + public static , S> SuggestionsSupplier fromEnum(Class enumClass) { return fromEnumValues(enumClass.getEnumConstants()); } - public static , S> Suggestions fromEnum(Class enumClass, boolean lowerCase) { + public static , S> SuggestionsSupplier fromEnum(Class enumClass, boolean lowerCase) { return fromEnumValues(lowerCase, enumClass.getEnumConstants()); } @SafeVarargs - public static , S> Suggestions fromEnumValues(E... enumValues) { + public static , S> SuggestionsSupplier fromEnumValues(E... enumValues) { return fromEnumValues(false, enumValues); } @SafeVarargs - public static , S> Suggestions fromEnumValues(boolean lowerCase, E... enumValues) { + public static , S> SuggestionsSupplier fromEnumValues(boolean lowerCase, E... enumValues) { return (s, ti, token, a) -> { Stream st = Arrays.stream(enumValues).map(Enum::name); if (lowerCase) @@ -82,14 +82,14 @@ public interface Suggestions { /** - * Create a {@link Suggestions} that suggest numbers according to the provided range. + * Create a {@link SuggestionsSupplier} that suggest numbers according to the provided range. * * The current implementation only support range that include either -1 or 1. * @param min * @param max * @return */ - public static Suggestions fromIntRange(int min, int max) { + public static SuggestionsSupplier fromIntRange(int min, int max) { return fromLongRange(min, max); } @@ -97,14 +97,14 @@ public interface Suggestions { /** - * Create a {@link Suggestions} that suggest numbers according to the provided range. + * Create a {@link SuggestionsSupplier} that suggest numbers according to the provided range. * * The current implementation only support range that include either -1 or 1. * @param min * @param max * @return */ - public static Suggestions fromLongRange(long min, long max) { + public static SuggestionsSupplier fromLongRange(long min, long max) { if (max < min) { throw new IllegalArgumentException("min should be less or equals than max"); } @@ -168,12 +168,12 @@ public interface Suggestions { } /** - * Create a {@link Suggestions} that support greedy strings argument using the suggestion from this {@link Suggestions}. + * Create a {@link SuggestionsSupplier} that support greedy strings argument using the suggestion from this {@link SuggestionsSupplier}. * @param args all the arguments currently in the buffer * @param index the index of the first argument of the greedy string argument * @return */ - public default Suggestions greedyString(int index) { + public default SuggestionsSupplier greedyString(int index) { return (s, ti, token, args) -> { @@ -206,7 +206,7 @@ public interface Suggestions { - public default Suggestions requires(Predicate check) { + public default SuggestionsSupplier requires(Predicate check) { return (s, ti, to, a) -> { return check.test(s) ? getSuggestions(s, ti, to, a) : Collections.emptyList(); }; diff --git a/src/main/java/fr/pandacube/util/measurement/TimeUtil.java b/src/main/java/fr/pandacube/util/measurement/TimeUtil.java index 8531c01..551d5cc 100644 --- a/src/main/java/fr/pandacube/util/measurement/TimeUtil.java +++ b/src/main/java/fr/pandacube/util/measurement/TimeUtil.java @@ -1,5 +1,10 @@ package fr.pandacube.util.measurement; +import java.util.Calendar; +import java.util.GregorianCalendar; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + public class TimeUtil { public static String durationToString(long msec_time, boolean dec_seconde) { boolean neg = msec_time < 0; @@ -37,5 +42,57 @@ public class TimeUtil { public static String durationToString(long msec_time) { return durationToString(msec_time, false); } + + + + /** + * @see {@link com.earth2me.essentials.utils.DateUtil#parseDateDiff(String, boolean)} + */ + public static long parseDateDiff(String time, boolean future) throws Exception { + Pattern timePattern = Pattern.compile("(?:([0-9]+)\\s*y[a-z]*[,\\s]*)?" + "(?:([0-9]+)\\s*mo[a-z]*[,\\s]*)?" + + "(?:([0-9]+)\\s*w[a-z]*[,\\s]*)?" + "(?:([0-9]+)\\s*d[a-z]*[,\\s]*)?" + + "(?:([0-9]+)\\s*h[a-z]*[,\\s]*)?" + "(?:([0-9]+)\\s*m[a-z]*[,\\s]*)?" + + "(?:([0-9]+)\\s*(?:s[a-z]*)?)?", Pattern.CASE_INSENSITIVE); + Matcher m = timePattern.matcher(time); + int years = 0; + int months = 0; + int weeks = 0; + int days = 0; + int hours = 0; + int minutes = 0; + int seconds = 0; + boolean found = false; + while (m.find()) { + if (m.group() == null || m.group().isEmpty()) continue; + for (int i = 0; i < m.groupCount(); i++) + if (m.group(i) != null && !m.group(i).isEmpty()) { + found = true; + break; + } + if (found) { + if (m.group(1) != null && !m.group(1).isEmpty()) years = Integer.parseInt(m.group(1)); + if (m.group(2) != null && !m.group(2).isEmpty()) months = Integer.parseInt(m.group(2)); + if (m.group(3) != null && !m.group(3).isEmpty()) weeks = Integer.parseInt(m.group(3)); + if (m.group(4) != null && !m.group(4).isEmpty()) days = Integer.parseInt(m.group(4)); + if (m.group(5) != null && !m.group(5).isEmpty()) hours = Integer.parseInt(m.group(5)); + if (m.group(6) != null && !m.group(6).isEmpty()) minutes = Integer.parseInt(m.group(6)); + if (m.group(7) != null && !m.group(7).isEmpty()) seconds = Integer.parseInt(m.group(7)); + break; + } + } + if (!found) throw new Exception("Format de durée invalide"); + Calendar c = new GregorianCalendar(); + if (years > 0) c.add(Calendar.YEAR, years * (future ? 1 : -1)); + if (months > 0) c.add(Calendar.MONTH, months * (future ? 1 : -1)); + if (weeks > 0) c.add(Calendar.WEEK_OF_YEAR, weeks * (future ? 1 : -1)); + if (days > 0) c.add(Calendar.DAY_OF_MONTH, days * (future ? 1 : -1)); + if (hours > 0) c.add(Calendar.HOUR_OF_DAY, hours * (future ? 1 : -1)); + if (minutes > 0) c.add(Calendar.MINUTE, minutes * (future ? 1 : -1)); + if (seconds > 0) c.add(Calendar.SECOND, seconds * (future ? 1 : -1)); + Calendar max = new GregorianCalendar(); + max.add(Calendar.YEAR, 10); + if (c.after(max)) return max.getTimeInMillis(); + return c.getTimeInMillis(); + } } diff --git a/src/main/java/fr/pandacube/util/text_display/Display.java b/src/main/java/fr/pandacube/util/text_display/Display.java index 77d5e0b..54f2153 100644 --- a/src/main/java/fr/pandacube/util/text_display/Display.java +++ b/src/main/java/fr/pandacube/util/text_display/Display.java @@ -108,6 +108,9 @@ public class Display { * @return this */ public Display next(BaseComponent[] components) { + if (components != null && components.length == 1) + return next(components[0]); + BaseComponent bc = new TextComponent(); for (BaseComponent c : components) bc.addExtra(c); @@ -256,6 +259,16 @@ public class Display { return this; } + /** + * Allow the player to click on the current component to copy content to the clipboard. + * @param str the string to copy to clipboard + * @return this + */ + public Display clickClipboard(String str) { + current.setClickEvent(new ClickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, str)); + return this; + } + /** * Allow the player to click on the current component to run the specified command. * This method is only relevant if this Display is intended to be displayed in the chat, in a book or on a sign. @@ -301,6 +314,8 @@ public class Display { */ public BaseComponent get() { finalizeCurrentComponent(); + if (!root.hasFormatting() && root.getExtra() != null && root.getExtra().size() == 1) + return root.getExtra().get(0); return root; } diff --git a/src/main/java/fr/pandacube/util/text_display/DisplayUtil.java b/src/main/java/fr/pandacube/util/text_display/DisplayUtil.java index 7413c7d..2a9b97b 100644 --- a/src/main/java/fr/pandacube/util/text_display/DisplayUtil.java +++ b/src/main/java/fr/pandacube/util/text_display/DisplayUtil.java @@ -4,6 +4,8 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; +import java.util.Set; +import java.util.TreeSet; import com.google.common.collect.ImmutableMap; @@ -17,11 +19,12 @@ public class DisplayUtil { public static final int DEFAULT_CHAR_SIZE = 6; public static final Map CHARS_SIZE = new ImmutableMap.Builder() .put(-6, "§") - .put(2, "!.,:;i|¡") - .put(3, "'`lìí") + .put(2, "!.,:;i|¡'") + .put(3, "`lìí’‘") .put(4, " I[]tï×") .put(5, "\"()*<>fk{}") - .put(7, "@~®") + .put(7, "@~®©«»") + .put(9, "├└") .build(); @@ -115,6 +118,63 @@ public class DisplayUtil { + /** + * @param cmdFormat the command with %d inside to be replaced with the page number (must start with slash) + */ + public static BaseComponent createPagination(String prefix, String cmdFormat, int currentPage, int nbPages, int nbPagesToDisplay) { + Set pagesToDisplay = new TreeSet<>(); + + for (int i = 0; i < nbPagesToDisplay && i < nbPages && nbPages - i > 0; i++) { + pagesToDisplay.add(i + 1); + pagesToDisplay.add(nbPages - i); + } + + for (int i = currentPage - nbPagesToDisplay + 1; i < currentPage + nbPagesToDisplay; i++) { + if (i > 0 && i <= nbPages) + pagesToDisplay.add(i); + } + + Display d = new Display(prefix); + boolean first = true; + int previous = 0; + + for (int page : pagesToDisplay) { + if (!first) { + if (page == previous + 1) { + d.next(" "); + } + else { + if (cmdFormat.endsWith("%d")) { + d.next(" "); + d.next(createCommandSuggest("...", cmdFormat.substring(0, cmdFormat.length() - 2), "Choisir la page")); + d.next(" "); + } + else + d.next(" ... "); + } + } + else + first = false; + + d.next(createCommandLink(Integer.toString(page), String.format(cmdFormat, page), "Aller à la page " + page)); + if (page == currentPage) { + d.color(ChatColor.WHITE); + } + + previous = page; + } + + + return d.get(); + } + + + + + + + + // TODO refaire les 4 methodes ci-dessous @@ -416,5 +476,152 @@ public class DisplayUtil { + + + + /** + * Force a text to be italic, while keeping other formatting and colors. + * The text is prefixed with the ITALIC tag, but is not reset at the end. + * @param legacyText the original text + * @return the text fully italic + */ + public static String forceItalic(String legacyText) { + return forceFormat(legacyText, ChatColor.ITALIC); + } + + /** + * Force a text to be bold, while keeping other formatting and colors. + * The text is prefixed with the BOLD tag, but is not reset at the end. + * @param legacyText the original text + * @return the text fully bold + */ + public static String forceBold(String legacyText) { + return forceFormat(legacyText, ChatColor.BOLD); + } + + /** + * Force a text to be underlined, while keeping other formatting and colors. + * The text is prefixed with the UNDERLINE tag, but is not reset at the end. + * @param legacyText the original text + * @return the text fully underlined + */ + public static String forceUnderline(String legacyText) { + return forceFormat(legacyText, ChatColor.UNDERLINE); + } + + /** + * Force a text to be stroked through, while keeping other formatting and colors. + * The text is prefixed with the STRIKETHROUGH tag, but is not reset at the end. + * @param legacyText the original text + * @return the text fully stroked through + */ + public static String forceStrikethrough(String legacyText) { + return forceFormat(legacyText, ChatColor.STRIKETHROUGH); + } + + /** + * Force a text to be obfuscated, while keeping other formatting and colors. + * The text is prefixed with the MAGIC tag, but is not reset at the end. + * @param legacyText the original text + * @return the text fully obfuscated + */ + public static String forceObfuscated(String legacyText) { + return forceFormat(legacyText, ChatColor.MAGIC); + } + + + + private static String forceFormat(String legacyText, ChatColor format) { + return format + legacyText + .replace(format.toString(), "") // remove previous tag to make the result cleaner + .replaceAll("§([a-frA-FR0-9])", "§$1" + format); + } + + + + + + /** + * Replace the RESET tag of the input string to the specified color tag. + * @param legacyText the original text + * @param color the color to used to replace the RESET tag + * (can be a combination of a color tag followed by multiple format tag) + * @return the resulting text + */ + public static String resetToColor(String legacyText, String color) { + return legacyText.replace(ChatColor.RESET.toString(), color); + } + + + + + + + + + + + /** + * Generate a tree view based on the tree structure {@code node}. + * + * Each element in the returned array represent 1 line of the tree view. + * Thus, the caller may send each line separately or at once depending of the quantity of data. + * @param node + * @return A array of component, each element being a single line. + */ + public static BaseComponent[] treeView(DisplayTreeNode node, boolean console) { + List ret = treeView_(node, console); + return ret.toArray(new BaseComponent[ret.size()]); + } + + private static final String TREE_MIDDLE_CONNECTED = "├"; + private static final String TREE_END_CONNECTED = "└"; + private static final String TREE_MIDDLE_OPEN = "│§0`§r"; + private static final String TREE_END_OPEN = "§0```§r"; + private static final String TREE_MIDDLE_OPEN_CONSOLE = "│"; + private static final String TREE_END_OPEN_CONSOLE = " "; // nbsp + + private static List treeView_(DisplayTreeNode node, boolean console) { + List ret = new ArrayList<>(); + + TextComponent curr = new TextComponent(); + curr.addExtra(node.component); + curr.setText(""); + + ret.add(curr); + + for (int i = 0; i < node.children.size(); i++) { + List childComponents = treeView_(node.children.get(i), console); + boolean last = i == node.children.size() - 1; + for (int j = 0; j < childComponents.size(); j++) { + TextComponent cComp = childComponents.get(j); + String prefix = last ? (j == 0 ? TREE_END_CONNECTED : (console ? TREE_END_OPEN_CONSOLE : TREE_END_OPEN)) + : (j == 0 ? TREE_MIDDLE_CONNECTED : (console ? TREE_MIDDLE_OPEN_CONSOLE : TREE_MIDDLE_OPEN)); + cComp.setText(prefix + cComp.getText()); + ret.add(cComp); + } + } + + + return ret; + } + + + + + + public static class DisplayTreeNode { + public final BaseComponent component; + public final List children = new ArrayList<>(); + + public DisplayTreeNode(BaseComponent cmp) { + component = cmp; + } + + public DisplayTreeNode addChild(DisplayTreeNode child) { + children.add(child); + return this; + } + } }