Aditionnal methods in DisplayUtil + some refactoring

This commit is contained in:
Marc Baloup 2020-06-08 16:16:18 +02:00
parent e65c402684
commit 34e015cb01
6 changed files with 302 additions and 81 deletions

View File

@ -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();
}
}

View File

@ -3,11 +3,13 @@ package fr.pandacube.util;
import java.io.File; import java.io.File;
public class DirUtils { public class DirUtils {
public static void delete(final File target) { public static void delete(final File target) {
if (target.isDirectory()) if (target.isDirectory())
for (final File child : target.listFiles()) for (File child : target.listFiles())
delete(child); delete(child);
target.delete(); target.delete();
} }
} }

View File

@ -12,10 +12,10 @@ import java.util.stream.Stream;
import fr.pandacube.util.ListUtil; import fr.pandacube.util.ListUtil;
@FunctionalInterface @FunctionalInterface
public interface Suggestions<S> { public interface SuggestionsSupplier<S> {
/** /**
* 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; public static int VISIBLE_SUGGESTION_COUNT = 10;
@ -45,33 +45,33 @@ public interface Suggestions<S> {
public static <S> Suggestions<S> empty() { return (s, ti, t, a) -> Collections.emptyList(); } public static <S> SuggestionsSupplier<S> empty() { return (s, ti, t, a) -> Collections.emptyList(); }
public static <S> Suggestions<S> fromCollection(Collection<String> suggestions) { public static <S> SuggestionsSupplier<S> fromCollection(Collection<String> suggestions) {
return (s, ti, token, a) -> collectFilteredStream(suggestions.stream(), token); return (s, ti, token, a) -> collectFilteredStream(suggestions.stream(), token);
} }
public static <S> Suggestions<S> fromArray(String... suggestions) { public static <S> SuggestionsSupplier<S> fromArray(String... suggestions) {
return (s, ti, token, a) -> collectFilteredStream(Arrays.stream(suggestions), token); return (s, ti, token, a) -> collectFilteredStream(Arrays.stream(suggestions), token);
} }
public static <E extends Enum<E>, S> Suggestions<S> fromEnum(Class<E> enumClass) { public static <E extends Enum<E>, S> SuggestionsSupplier<S> fromEnum(Class<E> enumClass) {
return fromEnumValues(enumClass.getEnumConstants()); return fromEnumValues(enumClass.getEnumConstants());
} }
public static <E extends Enum<E>, S> Suggestions<S> fromEnum(Class<E> enumClass, boolean lowerCase) { public static <E extends Enum<E>, S> SuggestionsSupplier<S> fromEnum(Class<E> enumClass, boolean lowerCase) {
return fromEnumValues(lowerCase, enumClass.getEnumConstants()); return fromEnumValues(lowerCase, enumClass.getEnumConstants());
} }
@SafeVarargs @SafeVarargs
public static <E extends Enum<E>, S> Suggestions<S> fromEnumValues(E... enumValues) { public static <E extends Enum<E>, S> SuggestionsSupplier<S> fromEnumValues(E... enumValues) {
return fromEnumValues(false, enumValues); return fromEnumValues(false, enumValues);
} }
@SafeVarargs @SafeVarargs
public static <E extends Enum<E>, S> Suggestions<S> fromEnumValues(boolean lowerCase, E... enumValues) { public static <E extends Enum<E>, S> SuggestionsSupplier<S> fromEnumValues(boolean lowerCase, E... enumValues) {
return (s, ti, token, a) -> { return (s, ti, token, a) -> {
Stream<String> st = Arrays.stream(enumValues).map(Enum::name); Stream<String> st = Arrays.stream(enumValues).map(Enum::name);
if (lowerCase) if (lowerCase)
@ -82,14 +82,14 @@ public interface Suggestions<S> {
/** /**
* 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. * The current implementation only support range that include either -1 or 1.
* @param min * @param min
* @param max * @param max
* @return * @return
*/ */
public static <S> Suggestions<S> fromIntRange(int min, int max) { public static <S> SuggestionsSupplier<S> fromIntRange(int min, int max) {
return fromLongRange(min, max); return fromLongRange(min, max);
} }
@ -97,14 +97,14 @@ public interface Suggestions<S> {
/** /**
* 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. * The current implementation only support range that include either -1 or 1.
* @param min * @param min
* @param max * @param max
* @return * @return
*/ */
public static <S> Suggestions<S> fromLongRange(long min, long max) { public static <S> SuggestionsSupplier<S> fromLongRange(long min, long max) {
if (max < min) { if (max < min) {
throw new IllegalArgumentException("min should be less or equals than max"); throw new IllegalArgumentException("min should be less or equals than max");
} }
@ -168,12 +168,12 @@ public interface Suggestions<S> {
} }
/** /**
* 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 args all the arguments currently in the buffer
* @param index the index of the first argument of the greedy string argument * @param index the index of the first argument of the greedy string argument
* @return * @return
*/ */
public default Suggestions<S> greedyString(int index) { public default SuggestionsSupplier<S> greedyString(int index) {
return (s, ti, token, args) -> { return (s, ti, token, args) -> {
@ -206,7 +206,7 @@ public interface Suggestions<S> {
public default Suggestions<S> requires(Predicate<S> check) { public default SuggestionsSupplier<S> requires(Predicate<S> check) {
return (s, ti, to, a) -> { return (s, ti, to, a) -> {
return check.test(s) ? getSuggestions(s, ti, to, a) : Collections.emptyList(); return check.test(s) ? getSuggestions(s, ti, to, a) : Collections.emptyList();
}; };

View File

@ -1,5 +1,10 @@
package fr.pandacube.util.measurement; 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 class TimeUtil {
public static String durationToString(long msec_time, boolean dec_seconde) { public static String durationToString(long msec_time, boolean dec_seconde) {
boolean neg = msec_time < 0; boolean neg = msec_time < 0;
@ -37,5 +42,57 @@ public class TimeUtil {
public static String durationToString(long msec_time) { public static String durationToString(long msec_time) {
return durationToString(msec_time, false); 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();
}
} }

View File

@ -108,6 +108,9 @@ public class Display {
* @return this * @return this
*/ */
public Display next(BaseComponent[] components) { public Display next(BaseComponent[] components) {
if (components != null && components.length == 1)
return next(components[0]);
BaseComponent bc = new TextComponent(); BaseComponent bc = new TextComponent();
for (BaseComponent c : components) for (BaseComponent c : components)
bc.addExtra(c); bc.addExtra(c);
@ -256,6 +259,16 @@ public class Display {
return this; 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. * 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. * 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() { public BaseComponent get() {
finalizeCurrentComponent(); finalizeCurrentComponent();
if (!root.hasFormatting() && root.getExtra() != null && root.getExtra().size() == 1)
return root.getExtra().get(0);
return root; return root;
} }

View File

@ -4,6 +4,8 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
@ -17,11 +19,12 @@ public class DisplayUtil {
public static final int DEFAULT_CHAR_SIZE = 6; public static final int DEFAULT_CHAR_SIZE = 6;
public static final Map<Integer, String> CHARS_SIZE = new ImmutableMap.Builder<Integer, String>() public static final Map<Integer, String> CHARS_SIZE = new ImmutableMap.Builder<Integer, String>()
.put(-6, "§") .put(-6, "§")
.put(2, "!.,:;i|¡") .put(2, "!.,:;i|¡'")
.put(3, "'`lìí") .put(3, "`lìí")
.put(4, " I[]tï×") .put(4, " I[]tï×")
.put(5, "\"()*<>fk{}") .put(5, "\"()*<>fk{}")
.put(7, "@~®") .put(7, "@~®©«»")
.put(9, "├└")
.build(); .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<Integer> 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 // 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<TextComponent> 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<TextComponent> treeView_(DisplayTreeNode node, boolean console) {
List<TextComponent> 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<TextComponent> 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<DisplayTreeNode> children = new ArrayList<>();
public DisplayTreeNode(BaseComponent cmp) {
component = cmp;
}
public DisplayTreeNode addChild(DisplayTreeNode child) {
children.add(child);
return this;
}
}
} }