Pretty big commit with lot of unrelated changes
This commit is contained in:
parent
34e015cb01
commit
ea39a7a84a
@ -1,13 +1,97 @@
|
|||||||
package fr.pandacube;
|
package fr.pandacube;
|
||||||
|
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.TimeZone;
|
||||||
|
|
||||||
|
import fr.pandacube.util.text_display.Chat;
|
||||||
|
import net.md_5.bungee.api.ChatColor;
|
||||||
|
|
||||||
public class Pandacube {
|
public class Pandacube {
|
||||||
|
|
||||||
|
public static final Locale LOCALE = Locale.FRANCE;
|
||||||
|
|
||||||
|
public static final TimeZone TIMEZONE = TimeZone.getTimeZone("Europe/Paris");
|
||||||
|
|
||||||
public static final Charset NETWORK_CHARSET = Charset.forName("UTF-8");
|
public static final Charset NETWORK_CHARSET = Charset.forName("UTF-8");
|
||||||
|
|
||||||
public static final int NETWORK_TCP_BUFFER_SIZE = 1024 * 1024;
|
public static final int NETWORK_TCP_BUFFER_SIZE = 1024 * 1024;
|
||||||
|
|
||||||
public static final int NETWORK_TIMEOUT = 0; // no timeout (milli-seconds)
|
public static final int NETWORK_TIMEOUT = 0; // no timeout (milli-seconds)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//public static final ChatColor CHAT_GREEN_1_NORMAL = ChatColor.of("#5f9765"); // h=126 s=23 l=48
|
||||||
|
|
||||||
|
public static final ChatColor CHAT_GREEN_1_NORMAL = ChatColor.of("#3db849"); // h=126 s=50 l=48
|
||||||
|
public static final ChatColor CHAT_GREEN_2 = ChatColor.of("#5ec969"); // h=126 s=50 l=58
|
||||||
|
public static final ChatColor CHAT_GREEN_3 = ChatColor.of("#85d68d"); // h=126 s=50 l=68
|
||||||
|
public static final ChatColor CHAT_GREEN_4 = ChatColor.of("#abe3b0"); // h=126 s=50 l=78
|
||||||
|
|
||||||
|
public static final ChatColor CHAT_GREEN_SATMAX = ChatColor.of("#00ff19"); // h=126 s=100 l=50
|
||||||
|
public static final ChatColor CHAT_GREEN_1_SAT = ChatColor.of("#20d532"); // h=126 s=50 l=48
|
||||||
|
public static final ChatColor CHAT_GREEN_2_SAT = ChatColor.of("#45e354"); // h=126 s=50 l=58
|
||||||
|
public static final ChatColor CHAT_GREEN_3_SAT = ChatColor.of("#71ea7d"); // h=126 s=50 l=68
|
||||||
|
public static final ChatColor CHAT_GREEN_4_SAT = ChatColor.of("#9df0a6"); // h=126 s=50 l=78
|
||||||
|
|
||||||
|
public static final ChatColor CHAT_BROWN_1 = ChatColor.of("#b26d3a"); // h=26 s=51 l=46
|
||||||
|
public static final ChatColor CHAT_BROWN_2 = ChatColor.of("#cd9265"); // h=26 s=51 l=60
|
||||||
|
public static final ChatColor CHAT_BROWN_3 = ChatColor.of("#e0bb9f"); // h=26 s=51 l=75
|
||||||
|
|
||||||
|
public static final ChatColor CHAT_BROWN_1_SAT = ChatColor.of("#b35c19"); // h=26 s=75 l=40
|
||||||
|
public static final ChatColor CHAT_BROWN_2_SAT = ChatColor.of("#e28136"); // h=26 s=51 l=55
|
||||||
|
public static final ChatColor CHAT_BROWN_3_SAT = ChatColor.of("#ecab79"); // h=26 s=51 l=70
|
||||||
|
|
||||||
|
public static final ChatColor CHAT_GRAY_MID = ChatColor.of("#888888");
|
||||||
|
|
||||||
|
public static final ChatColor CHAT_BROADCAST_COLOR = ChatColor.YELLOW;
|
||||||
|
|
||||||
|
|
||||||
|
public static final ChatColor CHAT_DECORATION_COLOR = CHAT_GREEN_1_NORMAL;
|
||||||
|
public static final char CHAT_DECORATION_CHAR = '-';
|
||||||
|
public static final ChatColor CHAT_URL_COLOR = CHAT_GREEN_1_NORMAL;
|
||||||
|
public static final ChatColor CHAT_COMMAND_COLOR = CHAT_GRAY_MID;
|
||||||
|
public static final ChatColor CHAT_COMMAND_HIGHLIGHTED_COLOR = ChatColor.WHITE;
|
||||||
|
public static final ChatColor CHAT_INFO_COLOR = CHAT_GREEN_4;
|
||||||
|
public static final ChatColor CHAT_SUCCESS_COLOR = CHAT_GREEN_SATMAX;
|
||||||
|
public static final ChatColor CHAT_FAILURE_COLOR = ChatColor.of("#ff3333");
|
||||||
|
public static final ChatColor CHAT_DATA_COLOR = CHAT_GRAY_MID;
|
||||||
|
|
||||||
|
|
||||||
|
public static final ChatColor CHAT_PM_PREFIX_DECORATION = Pandacube.CHAT_BROWN_2_SAT;
|
||||||
|
public static final ChatColor CHAT_PM_SELF_MESSAGE = Pandacube.CHAT_GREEN_2;
|
||||||
|
public static final ChatColor CHAT_PM_OTHER_MESSAGE = Pandacube.CHAT_GREEN_4;
|
||||||
|
|
||||||
|
|
||||||
|
public static final Chat CHAT_MESSAGE_PREFIX() {
|
||||||
|
return Chat.text("[")
|
||||||
|
.color(CHAT_BROADCAST_COLOR)
|
||||||
|
.thenDecoration("Pandacube")
|
||||||
|
.thenText("] ");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Number of decoration character to put between the text and the border of
|
||||||
|
* the line for left and right aligned text.
|
||||||
|
*/
|
||||||
|
public static final int CHAT_NB_CHAR_MARGIN = 1;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static {
|
||||||
|
// initialize class to avoid NCDFE when updating the plugin
|
||||||
|
@SuppressWarnings({ "deprecation", "unused" })
|
||||||
|
Class<?>
|
||||||
|
c1 = fr.pandacube.util.network_api.server.RequestAnalyser.class,
|
||||||
|
c2 = fr.pandacube.util.network_api.server.RequestAnalyser.BadRequestException.class,
|
||||||
|
c3 = fr.pandacube.util.network_api.server.Response.class,
|
||||||
|
c4 = fr.pandacube.util.text_display.ChatUtil.class;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
57
src/main/java/fr/pandacube/util/AmountPerTimeLimiter.java
Normal file
57
src/main/java/fr/pandacube/util/AmountPerTimeLimiter.java
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package fr.pandacube.util;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
public class AmountPerTimeLimiter {
|
||||||
|
private final double maxAmount;
|
||||||
|
private final long duration;
|
||||||
|
|
||||||
|
private List<Entry> entries = new ArrayList<>();
|
||||||
|
|
||||||
|
public AmountPerTimeLimiter(double a, long d) {
|
||||||
|
maxAmount = a;
|
||||||
|
duration = d;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private class Entry {
|
||||||
|
private final long time;
|
||||||
|
private double amount;
|
||||||
|
public Entry(long t, double a) {
|
||||||
|
time = t; amount = a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public synchronized double getAmountSinceDuration() {
|
||||||
|
long currT = System.currentTimeMillis();
|
||||||
|
entries = entries.stream()
|
||||||
|
.filter(e -> e.time > currT - duration)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
return entries.stream()
|
||||||
|
.mapToDouble(e -> e.amount)
|
||||||
|
.sum();
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void add(double amount) {
|
||||||
|
long currT = System.currentTimeMillis();
|
||||||
|
if (!entries.isEmpty() && entries.get(entries.size()-1).time > currT - 1000)
|
||||||
|
entries.get(entries.size()-1).amount += amount;
|
||||||
|
else
|
||||||
|
entries.add(new Entry(System.currentTimeMillis(), amount));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean canAdd(double amount) {
|
||||||
|
return getAmountSinceDuration() + amount < maxAmount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getRemainingForNow() {
|
||||||
|
return Math.max(0, maxAmount - getAmountSinceDuration());
|
||||||
|
}
|
||||||
|
}
|
53
src/main/java/fr/pandacube/util/MappedListView.java
Normal file
53
src/main/java/fr/pandacube/util/MappedListView.java
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
package fr.pandacube.util;
|
||||||
|
|
||||||
|
import java.util.AbstractList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
public class MappedListView<S, T> extends AbstractList<T> {
|
||||||
|
|
||||||
|
private final List<S> backend;
|
||||||
|
private final Function<S, T> getter;
|
||||||
|
private final Function<T, S> setter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param backend the list backing this list
|
||||||
|
* @param getter the function converting the value from the backing list to the value of this list.
|
||||||
|
* It is used for every operation involving reading data from the backing list and comparing existing data.
|
||||||
|
* For instance, {@link #indexOf(Object)} iterate through the backing list, converting all the values
|
||||||
|
* with {@code getter} before comparing them with the parameter of {@link #indexOf(Object)}.
|
||||||
|
* before comparing
|
||||||
|
* @param setter used for modification of the data in the list (typically {@code add} and {@code set} methods)
|
||||||
|
*/
|
||||||
|
public MappedListView(List<S> backend, Function<S, T> getter, Function<T, S> setter) {
|
||||||
|
this.backend = backend;
|
||||||
|
this.getter = getter;
|
||||||
|
this.setter = setter;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() { return backend.size(); }
|
||||||
|
@Override
|
||||||
|
public T get(int index) { return getter.apply(backend.get(index)); }
|
||||||
|
@Override
|
||||||
|
public T set(int index, T element) { return getter.apply(backend.set(index, setter.apply(element))); }
|
||||||
|
@Override
|
||||||
|
public void add(int index, T element) { backend.add(index, setter.apply(element)); }
|
||||||
|
@Override
|
||||||
|
public T remove(int index) { return getter.apply(backend.remove(index)); }
|
||||||
|
@Override
|
||||||
|
public void clear() { backend.clear(); }
|
||||||
|
@Override
|
||||||
|
public List<T> subList(int fromIndex, int toIndex) {
|
||||||
|
return new MappedListView<S, T>(backend.subList(fromIndex, toIndex), getter, setter);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
protected void removeRange(int fromIndex, int toIndex) {
|
||||||
|
backend.subList(fromIndex, toIndex).clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -34,7 +34,12 @@ public enum MinecraftVersion {
|
|||||||
v1_14_4(498, "1.14.4"),
|
v1_14_4(498, "1.14.4"),
|
||||||
v1_15(573, "1.15"),
|
v1_15(573, "1.15"),
|
||||||
v1_15_1(575, "1.15.1"),
|
v1_15_1(575, "1.15.1"),
|
||||||
v1_15_2(578, "1.15.2");
|
v1_15_2(578, "1.15.2"),
|
||||||
|
v1_16(735, "1.16"),
|
||||||
|
v1_16_1(736, "1.16.1"),
|
||||||
|
v1_16_2(751, "1.16.2"),
|
||||||
|
v1_16_3(753, "1.16.3"),
|
||||||
|
v1_16_4(754, "1.16.4");
|
||||||
// IMPORTANT: don't forget to update the versionMergeDisplay value when adding a new version;
|
// IMPORTANT: don't forget to update the versionMergeDisplay value when adding a new version;
|
||||||
|
|
||||||
private static Map<EnumSet<MinecraftVersion>, List<String>> versionMergeDisplay;
|
private static Map<EnumSet<MinecraftVersion>, List<String>> versionMergeDisplay;
|
||||||
@ -102,14 +107,35 @@ public enum MinecraftVersion {
|
|||||||
ImmutableList.of("1.15", "1.15.1"));
|
ImmutableList.of("1.15", "1.15.1"));
|
||||||
versionMergeDisplay.put(EnumSet.of(v1_15_1, v1_15_2),
|
versionMergeDisplay.put(EnumSet.of(v1_15_1, v1_15_2),
|
||||||
ImmutableList.of("1.15.1", "1.15.2"));
|
ImmutableList.of("1.15.1", "1.15.2"));
|
||||||
|
|
||||||
|
versionMergeDisplay.put(EnumSet.of(v1_16, v1_16_1, v1_16_2, v1_16_3, v1_16_4),
|
||||||
|
ImmutableList.of("1.16.x"));
|
||||||
|
versionMergeDisplay.put(EnumSet.of(v1_16, v1_16_1, v1_16_2, v1_16_3),
|
||||||
|
ImmutableList.of("1.16-1.16.3"));
|
||||||
|
versionMergeDisplay.put(EnumSet.of(v1_16_1, v1_16_2, v1_16_3, v1_16_4),
|
||||||
|
ImmutableList.of("1.16.1-1.16.4"));
|
||||||
|
versionMergeDisplay.put(EnumSet.of(v1_16, v1_16_1, v1_16_2),
|
||||||
|
ImmutableList.of("1.16-1.16.2"));
|
||||||
|
versionMergeDisplay.put(EnumSet.of(v1_16_1, v1_16_2, v1_16_3),
|
||||||
|
ImmutableList.of("1.16.1-1.16.3"));
|
||||||
|
versionMergeDisplay.put(EnumSet.of(v1_16_2, v1_16_3, v1_16_4),
|
||||||
|
ImmutableList.of("1.16.2-1.16.4"));
|
||||||
|
versionMergeDisplay.put(EnumSet.of(v1_16, v1_16_1),
|
||||||
|
ImmutableList.of("1.16", "1.16.1"));
|
||||||
|
versionMergeDisplay.put(EnumSet.of(v1_16_1, v1_16_2),
|
||||||
|
ImmutableList.of("1.16.1", "1.16.2"));
|
||||||
|
versionMergeDisplay.put(EnumSet.of(v1_16_2, v1_16_3),
|
||||||
|
ImmutableList.of("1.16.2", "1.16.3"));
|
||||||
|
versionMergeDisplay.put(EnumSet.of(v1_16_3, v1_16_4),
|
||||||
|
ImmutableList.of("1.16.3", "1.16.4"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public final int versionNumber;
|
public final int id;
|
||||||
public final List<String> versionDisplay;
|
public final List<String> versionDisplay;
|
||||||
|
|
||||||
private MinecraftVersion(int v, String... d) {
|
private MinecraftVersion(int v, String... d) {
|
||||||
versionNumber = v;
|
id = v;
|
||||||
versionDisplay = Arrays.asList(d);
|
versionDisplay = Arrays.asList(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,19 +146,25 @@ public enum MinecraftVersion {
|
|||||||
|
|
||||||
public static MinecraftVersion getVersion(int v) {
|
public static MinecraftVersion getVersion(int v) {
|
||||||
for (MinecraftVersion mcV : values())
|
for (MinecraftVersion mcV : values())
|
||||||
if (mcV.versionNumber == v) return mcV;
|
if (mcV.id == v) return mcV;
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static String displayOptimizedListOfVersions(List<MinecraftVersion> versions) {
|
public static String displayOptimizedListOfVersionsAnd(List<MinecraftVersion> versions) {
|
||||||
|
return StringUtil.joinGrammatically(", ", " et ", getVersionsDisplayList(versions));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String displayOptimizedListOfVersionsOr(List<MinecraftVersion> versions) {
|
||||||
return StringUtil.joinGrammatically(", ", " et ", getVersionsDisplayList(versions));
|
return StringUtil.joinGrammatically(", ", " et ", getVersionsDisplayList(versions));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static final List<String> getVersionsDisplayList(List<MinecraftVersion> vList) {
|
public static final List<String> getVersionsDisplayList(List<MinecraftVersion> vList) {
|
||||||
|
if (vList == null)
|
||||||
|
return new ArrayList<>();
|
||||||
Set<MinecraftVersion> vSet = EnumSet.copyOf(vList);
|
Set<MinecraftVersion> vSet = EnumSet.copyOf(vList);
|
||||||
|
|
||||||
List<String> ret = new ArrayList<>();
|
List<String> ret = new ArrayList<>();
|
||||||
|
@ -17,11 +17,11 @@ public class RandomUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static <T> T arrayElement(T[] arr) {
|
public static <T> T arrayElement(T[] arr) {
|
||||||
return arr[rand.nextInt(arr.length)];
|
return (arr == null || arr.length == 0) ? null : arr[rand.nextInt(arr.length)];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <T> T listElement(List<T> arr) {
|
public static <T> T listElement(List<T> arr) {
|
||||||
return arr.get(rand.nextInt(arr.size()));
|
return (arr == null || arr.isEmpty()) ? null : arr.get(rand.nextInt(arr.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -35,6 +35,21 @@ public class StringUtil {
|
|||||||
|
|
||||||
|
|
||||||
public static String repeat(String base, int count) {
|
public static String repeat(String base, int count) {
|
||||||
return new String(new char[count]).replace("\0", base);
|
int baseLength = base.length();
|
||||||
|
char[] baseChars = base.toCharArray();
|
||||||
|
char[] chars = new char[baseLength * count];
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
System.arraycopy(baseChars, 0, chars, i * baseLength, baseLength);
|
||||||
|
}
|
||||||
|
return new String(chars);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static String repeat(char base, int count) {
|
||||||
|
char[] chars = new char[count];
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
chars[i] = base;
|
||||||
|
}
|
||||||
|
return new String(chars);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,9 @@ package fr.pandacube.util.commands;
|
|||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
public class AbstractCommand {
|
import fr.pandacube.util.text_display.ChatStatic;
|
||||||
|
|
||||||
|
public class AbstractCommand extends ChatStatic {
|
||||||
|
|
||||||
public final String commandName;
|
public final String commandName;
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ import java.util.Collection;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
import java.util.function.Supplier;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
@ -47,13 +48,20 @@ public interface SuggestionsSupplier<S> {
|
|||||||
|
|
||||||
public static <S> SuggestionsSupplier<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> SuggestionsSupplier<S> fromCollectionsSupplier(Supplier<Collection<String>> streamSupplier) {
|
||||||
|
return (s, ti, token, a) -> collectFilteredStream(streamSupplier.get().stream(), token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <S> SuggestionsSupplier<S> fromStreamSupplier(Supplier<Stream<String>> streamSupplier) {
|
||||||
|
return (s, ti, token, a) -> collectFilteredStream(streamSupplier.get(), token);
|
||||||
|
}
|
||||||
|
|
||||||
public static <S> SuggestionsSupplier<S> fromCollection(Collection<String> suggestions) {
|
public static <S> SuggestionsSupplier<S> fromCollection(Collection<String> suggestions) {
|
||||||
return (s, ti, token, a) -> collectFilteredStream(suggestions.stream(), token);
|
return fromStreamSupplier(suggestions::stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <S> SuggestionsSupplier<S> fromArray(String... suggestions) {
|
public static <S> SuggestionsSupplier<S> fromArray(String... suggestions) {
|
||||||
return (s, ti, token, a) -> collectFilteredStream(Arrays.stream(suggestions), token);
|
return fromStreamSupplier(() -> Arrays.stream(suggestions));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -81,6 +89,15 @@ public interface SuggestionsSupplier<S> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static <S> SuggestionsSupplier<S> booleanValues() {
|
||||||
|
return fromCollection(Arrays.asList("true", "false"));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a {@link SuggestionsSupplier} that suggest numbers according to the provided range.
|
* Create a {@link SuggestionsSupplier} that suggest numbers according to the provided range.
|
||||||
*
|
*
|
||||||
@ -167,9 +184,11 @@ public interface SuggestionsSupplier<S> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a {@link SuggestionsSupplier} that support greedy strings argument using the suggestion from this {@link SuggestionsSupplier}.
|
* 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
|
* @param index the index of the first argument of the greedy string argument
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@ -206,6 +225,84 @@ public interface SuggestionsSupplier<S> {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public default SuggestionsSupplier<S> quotableString() {
|
||||||
|
return (s, ti, token, a) -> {
|
||||||
|
boolean startWithQuote = token.length() > 0 && (token.charAt(0) == '"' || token.charAt(0) == '\'');
|
||||||
|
String realToken = startWithQuote ? unescapeBrigadierQuotable(token.substring(1), token.charAt(0)) : token;
|
||||||
|
String[] argsCopy = Arrays.copyOf(a, a.length);
|
||||||
|
argsCopy[a.length - 1] = realToken;
|
||||||
|
List<String> rawResults = getSuggestions(s, ti, realToken, argsCopy);
|
||||||
|
|
||||||
|
boolean needsQuotes = false;
|
||||||
|
for (String res : rawResults) {
|
||||||
|
if (!isAllowedInBrigadierUnquotedString(res)) {
|
||||||
|
needsQuotes = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return needsQuotes
|
||||||
|
? rawResults.stream().map(SuggestionsSupplier::escapeBrigadierQuotable).collect(Collectors.toList())
|
||||||
|
: rawResults;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// inspired from com.mojang.brigadier.StringReader#readQuotedString()
|
||||||
|
static String unescapeBrigadierQuotable(String input, char quote) {
|
||||||
|
StringBuilder builder = new StringBuilder(input.length());
|
||||||
|
boolean escaped = false;
|
||||||
|
for (char c : input.toCharArray()) {
|
||||||
|
if (escaped) {
|
||||||
|
if (c == quote || c == '\\') {
|
||||||
|
escaped = false;
|
||||||
|
} else {
|
||||||
|
builder.append('\\');
|
||||||
|
}
|
||||||
|
builder.append(c);
|
||||||
|
} else if (c == '\\') {
|
||||||
|
escaped = true;
|
||||||
|
} else if (c == quote) {
|
||||||
|
return builder.toString();
|
||||||
|
} else {
|
||||||
|
builder.append(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// from com.mojang.brigadier.StringReader#isAllowedInUnquotedString(char)
|
||||||
|
static boolean isAllowedInBrigadierUnquotedString(char c) {
|
||||||
|
return c >= '0' && c <= '9' || c >= 'A' && c <= 'Z' || c >= 'a' && c <= 'z'
|
||||||
|
|| c == '_' || c == '-' || c == '.' || c == '+';
|
||||||
|
}
|
||||||
|
static boolean isAllowedInBrigadierUnquotedString(String s) {
|
||||||
|
for (char c : s.toCharArray())
|
||||||
|
if (!isAllowedInBrigadierUnquotedString(c))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static String escapeBrigadierQuotable(final String input) {
|
||||||
|
final StringBuilder result = new StringBuilder("\"");
|
||||||
|
|
||||||
|
for (int i = 0; i < input.length(); i++) {
|
||||||
|
final char c = input.charAt(i);
|
||||||
|
if (c == '\\' || c == '"') {
|
||||||
|
result.append('\\');
|
||||||
|
}
|
||||||
|
result.append(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
result.append("\"");
|
||||||
|
return result.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public default SuggestionsSupplier<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();
|
||||||
@ -213,4 +310,36 @@ public interface SuggestionsSupplier<S> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new {@link SuggestionsSupplier} containing all the element of this instance then the element of the provided one,
|
||||||
|
* with all duplicated values removed using {@link Stream#distinct()}.
|
||||||
|
* @param other
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public default SuggestionsSupplier<S> merge(SuggestionsSupplier<S> other) {
|
||||||
|
return (s, ti, to, a) -> {
|
||||||
|
List<String> l1 = getSuggestions(s, ti, to, a);
|
||||||
|
List<String> l2 = other.getSuggestions(s, ti, to, a);
|
||||||
|
return Stream.concat(l1.stream(), l2.stream())
|
||||||
|
.distinct()
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new {@link SuggestionsSupplier} containing all the suggestions of this instance,
|
||||||
|
* but if this list is still empty, returns the suggestions from the provided one.
|
||||||
|
* @param other
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public default SuggestionsSupplier<S> orIfEmpty(SuggestionsSupplier<S> other) {
|
||||||
|
return (s, ti, to, a) -> {
|
||||||
|
List<String> l1 = getSuggestions(s, ti, to, a);
|
||||||
|
return !l1.isEmpty() ? l1 : other.getSuggestions(s, ti, to, a);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
@ -10,7 +10,7 @@ import java.util.Collections;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import fr.pandacube.util.Log;
|
import fr.pandacube.util.Log;
|
||||||
import net.md_5.bungee.api.ChatColor;
|
import fr.pandacube.util.text_display.ChatColorUtil;
|
||||||
/**
|
/**
|
||||||
* Classe chargeant en mémoire un fichier de configuration ou un dossier donné
|
* Classe chargeant en mémoire un fichier de configuration ou un dossier donné
|
||||||
* @author Marc Baloup
|
* @author Marc Baloup
|
||||||
@ -125,7 +125,7 @@ public abstract class AbstractConfig {
|
|||||||
|
|
||||||
|
|
||||||
public static String getTranslatedColorCode(String string) {
|
public static String getTranslatedColorCode(String string) {
|
||||||
return ChatColor.translateAlternateColorCodes('&', string);
|
return ChatColorUtil.translateAlternateColorCodes('&', string);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,54 +1,262 @@
|
|||||||
package fr.pandacube.util.measurement;
|
package fr.pandacube.util.measurement;
|
||||||
|
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.GregorianCalendar;
|
import java.util.GregorianCalendar;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import fr.pandacube.Pandacube;
|
||||||
|
import fr.pandacube.util.StringUtil;
|
||||||
|
import fr.pandacube.util.commands.SuggestionsSupplier;
|
||||||
|
|
||||||
public class TimeUtil {
|
public class TimeUtil {
|
||||||
public static String durationToString(long msec_time, boolean dec_seconde) {
|
|
||||||
boolean neg = msec_time < 0;
|
|
||||||
msec_time = Math.abs(msec_time);
|
|
||||||
int j = 0, h = 0, m = 0, s = 0;
|
|
||||||
long msec = msec_time;
|
|
||||||
|
|
||||||
j = (int) (msec / (1000 * 60 * 60 * 24));
|
|
||||||
msec -= (long) (1000 * 60 * 60 * 24) * j;
|
|
||||||
h = (int) (msec / (1000 * 60 * 60));
|
|
||||||
msec -= (long) (1000 * 60 * 60) * h;
|
|
||||||
m = (int) (msec / (1000 * 60));
|
|
||||||
msec -= (long) (1000 * 60) * m;
|
|
||||||
s = (int) (msec / 1000);
|
|
||||||
msec -= (long) 1000 * s;
|
|
||||||
|
|
||||||
String result = "";
|
private static DateTimeFormatter cmpDayOfWeekFormatter = DateTimeFormatter.ofPattern("EEE", Pandacube.LOCALE);
|
||||||
if (j > 0) result = result.concat(j + "j ");
|
private static DateTimeFormatter dayOfWeekFormatter = DateTimeFormatter.ofPattern("EEEE", Pandacube.LOCALE);
|
||||||
if (h > 0) result = result.concat(h + "h ");
|
private static DateTimeFormatter dayOfMonthFormatter = DateTimeFormatter.ofPattern("d", Pandacube.LOCALE);
|
||||||
if (m > 0) result = result.concat(m + "m ");
|
private static DateTimeFormatter cmpMonthFormatter = DateTimeFormatter.ofPattern("MMM", Pandacube.LOCALE);
|
||||||
if (s > 0 && !dec_seconde) result = result.concat(s + "s");
|
private static DateTimeFormatter monthFormatter = DateTimeFormatter.ofPattern("MMMM", Pandacube.LOCALE);
|
||||||
else if (dec_seconde && (s > 0 || msec > 0)) {
|
private static DateTimeFormatter yearFormatter = DateTimeFormatter.ofPattern("uuuu", Pandacube.LOCALE);
|
||||||
msec += s * 1000;
|
|
||||||
result = result.concat((msec / 1000D) + "s");
|
private static DateTimeFormatter HMSFormatter = DateTimeFormatter.ofPattern("HH:mm:ss", Pandacube.LOCALE);
|
||||||
|
private static DateTimeFormatter HMFormatter = DateTimeFormatter.ofPattern("HH:mm", Pandacube.LOCALE);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static String relativeDateFr(long displayTime, boolean showSeconds, boolean compactWords) {
|
||||||
|
long currentTime = System.currentTimeMillis();
|
||||||
|
|
||||||
|
|
||||||
|
LocalDateTime displayDateTime = toLocalDateTime(displayTime);
|
||||||
|
LocalDateTime currentDateTime = toLocalDateTime(currentTime);
|
||||||
|
|
||||||
|
|
||||||
|
long timeDiff = currentTime - displayTime;
|
||||||
|
long timeDiffSec = timeDiff / 1000;
|
||||||
|
|
||||||
|
if (timeDiffSec < -1) {
|
||||||
|
// in the future
|
||||||
|
if (timeDiffSec > -60) {
|
||||||
|
if (showSeconds)
|
||||||
|
return "dans " + (-timeDiffSec) + " secondes";
|
||||||
|
else
|
||||||
|
return "dans moins d’une minute";
|
||||||
|
}
|
||||||
|
if (timeDiffSec > -60*2) // dans 2 min
|
||||||
|
return "dans ̈" + (int)Math.floor((-timeDiffSec)/60) + " minute";
|
||||||
|
if (timeDiffSec > -3600) // dans 1h
|
||||||
|
return "dans " + (int)Math.floor((-timeDiffSec)/60) + " minutes";
|
||||||
|
if (timeDiffSec > -3600*2) // dans 2h
|
||||||
|
return "dans " + (int)Math.floor((-timeDiffSec)/(3600)) + " heure";
|
||||||
|
if (timeDiffSec > -3600*12) // dans 12h
|
||||||
|
return "dans " + (int)Math.floor((-timeDiffSec)/(3600)) + " heures";
|
||||||
|
|
||||||
|
LocalDateTime nextMidnight = LocalDateTime.of(currentDateTime.getYear(), currentDateTime.getMonth(), currentDateTime.getDayOfMonth(), 0, 0).plusDays(1);
|
||||||
|
if (displayDateTime.isBefore(nextMidnight)) // aujourd'hui
|
||||||
|
return "aujourd’hui à " + (showSeconds ? HMSFormatter : HMFormatter).format(displayDateTime);
|
||||||
|
if (displayDateTime.isBefore(nextMidnight.plusDays(1))) // demain
|
||||||
|
return "demain à " + (showSeconds ? HMSFormatter : HMFormatter).format(displayDateTime);
|
||||||
|
if (displayDateTime.isBefore(nextMidnight.plusDays(5))) // dans moins d'1 semaine
|
||||||
|
return (compactWords ? cmpDayOfWeekFormatter : dayOfWeekFormatter).format(displayDateTime) + " "
|
||||||
|
+ dayOfMonthFormatter.format(displayDateTime) + " à "
|
||||||
|
+ (showSeconds ? HMSFormatter : HMFormatter).format(displayDateTime);
|
||||||
|
|
||||||
|
return fullDateFr(displayTime, showSeconds, true, compactWords);
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// present and past
|
||||||
|
if (timeDiffSec <= 1)
|
||||||
|
return "maintenant";
|
||||||
|
if (timeDiffSec < 60) { // ya moins d'1 min
|
||||||
|
if (showSeconds)
|
||||||
|
return "il y a " + timeDiffSec + " secondes";
|
||||||
|
else
|
||||||
|
return "il y a moins d’une minute";
|
||||||
|
}
|
||||||
|
if (timeDiffSec < 60*2) // ya moins de 2 min
|
||||||
|
return "il y a " + (int)Math.floor((timeDiffSec)/60) + " minute";
|
||||||
|
if (timeDiffSec < 3600) // ya moins d'1h
|
||||||
|
return "il y a " + (int)Math.floor((timeDiffSec)/60) + " minutes";
|
||||||
|
if (timeDiffSec < 3600*2) // ya moins de 2h
|
||||||
|
return "il y a " + (int)Math.floor((timeDiffSec)/(3600)) + " heure";
|
||||||
|
if (timeDiffSec < 3600*12) // ya moins de 12h
|
||||||
|
return "il y a " + (int)Math.floor((timeDiffSec)/(3600)) + " heures";
|
||||||
|
|
||||||
|
LocalDateTime lastMidnight = LocalDateTime.of(currentDateTime.getYear(), currentDateTime.getMonth(), currentDateTime.getDayOfMonth(), 0, 0);
|
||||||
|
|
||||||
|
if (!displayDateTime.isBefore(lastMidnight)) // aujourd'hui
|
||||||
|
return "aujourd’hui à " + (showSeconds ? HMSFormatter : HMFormatter).format(displayDateTime);
|
||||||
|
if (!displayDateTime.isBefore(lastMidnight.minusDays(1))) // hier
|
||||||
|
return "hier à " + (showSeconds ? HMSFormatter : HMFormatter).format(displayDateTime);
|
||||||
|
if (!displayDateTime.isBefore(lastMidnight.minusDays(6))) // ya moins d'1 semaine
|
||||||
|
return (compactWords ? cmpDayOfWeekFormatter : dayOfWeekFormatter).format(displayDateTime) + " dernier à "
|
||||||
|
+ (showSeconds ? HMSFormatter : HMFormatter).format(displayDateTime);
|
||||||
|
|
||||||
|
return fullDateFr(displayTime, showSeconds, true, compactWords);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.equals("")) result = "0";
|
|
||||||
result = result.trim();
|
|
||||||
if (neg)
|
|
||||||
result = "-" + result;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String durationToString(long msec_time) {
|
public static String fullDateFr(long displayTime, boolean showSeconds, boolean showWeekday, boolean compactWords) {
|
||||||
return durationToString(msec_time, false);
|
LocalDateTime displayDateTime = toLocalDateTime(displayTime);
|
||||||
|
return (showWeekday ? ((compactWords ? cmpDayOfWeekFormatter : dayOfWeekFormatter).format(displayDateTime) + " ") : "")
|
||||||
|
+ dayOfMonthFormatter.format(displayDateTime) + " "
|
||||||
|
+ (compactWords ? cmpMonthFormatter : monthFormatter).format(displayDateTime) + " "
|
||||||
|
+ yearFormatter.format(displayDateTime) + " à "
|
||||||
|
+ (showSeconds ? HMSFormatter : HMFormatter).format(displayDateTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static LocalDateTime toLocalDateTime(long msTime) {
|
||||||
|
return Instant.ofEpochMilli(msTime).atZone(Pandacube.TIMEZONE.toZoneId()).toLocalDateTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static String durationToLongString(long msDuration, TimeUnit hUnit, TimeUnit lUnit, boolean spaces, boolean fr, boolean leadingZeros) {
|
||||||
|
if (lUnit.compareTo(hUnit) > 0) {
|
||||||
|
TimeUnit tmp = lUnit;
|
||||||
|
lUnit = hUnit;
|
||||||
|
hUnit = tmp;
|
||||||
|
}
|
||||||
|
if (lUnit.compareTo(TimeUnit.MILLISECONDS) < 0)
|
||||||
|
lUnit = TimeUnit.MILLISECONDS;
|
||||||
|
if (hUnit.compareTo(TimeUnit.MILLISECONDS) < 0)
|
||||||
|
hUnit = TimeUnit.MILLISECONDS;
|
||||||
|
|
||||||
|
|
||||||
|
AtomicLong remainingTime = new AtomicLong(msDuration);
|
||||||
|
AtomicBoolean oneDisplayed = new AtomicBoolean(false);
|
||||||
|
final TimeUnit fLUnit = lUnit, fHUnit = hUnit;
|
||||||
|
|
||||||
|
String ret = Arrays.stream(TimeUnit.values())
|
||||||
|
.sequential()
|
||||||
|
.filter(u -> u.compareTo(fLUnit) >= 0 && u.compareTo(fHUnit) <= 0)
|
||||||
|
.sorted((u1, u2) -> u2.compareTo(u1))
|
||||||
|
.filter(u -> {
|
||||||
|
if (u.convert(remainingTime.get(), TimeUnit.MILLISECONDS) == 0 && !oneDisplayed.get())
|
||||||
|
return false;
|
||||||
|
oneDisplayed.set(true);
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.map(u -> {
|
||||||
|
long v = u.convert(remainingTime.get(), TimeUnit.MILLISECONDS);
|
||||||
|
remainingTime.addAndGet(TimeUnit.MILLISECONDS.convert(-v, u));
|
||||||
|
return toString(v, leadingZeros ? timeUnitToLeftPadLength(u) : 1) + timeUnitToSuffix(u, fr);
|
||||||
|
})
|
||||||
|
.collect(Collectors.joining(spaces ? " " : ""));
|
||||||
|
// ensure there is at least something to display (for instance : "0s")
|
||||||
|
return oneDisplayed.get() ? ret : (toString(0, leadingZeros ? timeUnitToLeftPadLength(lUnit) : 1) + timeUnitToSuffix(lUnit, fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static String timeUnitToSuffix(TimeUnit u, boolean fr) {
|
||||||
|
switch (u) {
|
||||||
|
case DAYS:
|
||||||
|
return fr ? "j" : "d";
|
||||||
|
case HOURS:
|
||||||
|
return "h";
|
||||||
|
case MINUTES:
|
||||||
|
return "m";
|
||||||
|
case SECONDS:
|
||||||
|
return "s";
|
||||||
|
case MILLISECONDS:
|
||||||
|
return "ms";
|
||||||
|
case MICROSECONDS:
|
||||||
|
return "μs";
|
||||||
|
case NANOSECONDS:
|
||||||
|
return "ns";
|
||||||
|
default:
|
||||||
|
throw new IllegalArgumentException("Invalid TimeUnit: " + Objects.toString(u));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int timeUnitToLeftPadLength(TimeUnit u) {
|
||||||
|
switch (u) {
|
||||||
|
case NANOSECONDS:
|
||||||
|
case MICROSECONDS:
|
||||||
|
case MILLISECONDS:
|
||||||
|
return 3;
|
||||||
|
case SECONDS:
|
||||||
|
case MINUTES:
|
||||||
|
case HOURS:
|
||||||
|
return 2;
|
||||||
|
case DAYS:
|
||||||
|
default:
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String toString(long value, int leftPad) {
|
||||||
|
String valueStr = Long.toString(value);
|
||||||
|
int padding = leftPad - valueStr.length();
|
||||||
|
if (padding <= 0)
|
||||||
|
return valueStr;
|
||||||
|
return StringUtil.repeat("0", padding) + valueStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Equivalent to {@link #durationToLongString(long, TimeUnit, TimeUnit, boolean, boolean, boolean) TimeUnit.durationToLongString(msDuration, TimeUnit.DAYS, milliseconds ? TimeUnit.MILLISECONDS : TimeUnit.SECONDS, true, true, false)}
|
||||||
|
* @param msDuration the duration in ms
|
||||||
|
* @param milliseconds if the milliseconds are displayed or not
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String durationToString(long msDuration, boolean milliseconds) {
|
||||||
|
return durationToLongString(msDuration, TimeUnit.DAYS, milliseconds ? TimeUnit.MILLISECONDS : TimeUnit.SECONDS, true, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Equivalent to {@link #durationToLongString(long, TimeUnit, TimeUnit, boolean, boolean, boolean) TimeUnit.durationToLongString(msDuration, TimeUnit.DAYS, TimeUnit.SECONDS, true, true, false)}
|
||||||
|
* @param msDuration
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String durationToString(long msDuration) {
|
||||||
|
return durationToLongString(msDuration, TimeUnit.DAYS, TimeUnit.SECONDS, true, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Equivalent to {@link #durationToLongString(long, TimeUnit, TimeUnit, boolean, boolean, boolean) TimeUnit.durationToLongString(msDuration, TimeUnit.DAYS, TimeUnit.SECONDS, false, false, false)}
|
||||||
|
* @param msDuration
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String durationToParsableString(long msDuration) {
|
||||||
|
return durationToLongString(msDuration, TimeUnit.DAYS, TimeUnit.SECONDS, false, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see {@link com.earth2me.essentials.utils.DateUtil#parseDateDiff(String, boolean)}
|
* @see {@link com.earth2me.essentials.utils.DateUtil#parseDuration(String, boolean)}
|
||||||
*/
|
*/
|
||||||
public static long parseDateDiff(String time, boolean future) throws Exception {
|
public static long parseDuration(String time, boolean future) throws Exception {
|
||||||
Pattern timePattern = Pattern.compile("(?:([0-9]+)\\s*y[a-z]*[,\\s]*)?" + "(?:([0-9]+)\\s*mo[a-z]*[,\\s]*)?"
|
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*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*h[a-z]*[,\\s]*)?" + "(?:([0-9]+)\\s*m[a-z]*[,\\s]*)?"
|
||||||
@ -95,4 +303,51 @@ public class TimeUtil {
|
|||||||
return c.getTimeInMillis();
|
return c.getTimeInMillis();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static <S> SuggestionsSupplier<S> suggestDuration() {
|
||||||
|
return (s, ti, token, args) -> {
|
||||||
|
if (token.isEmpty()) {
|
||||||
|
return emptyTokenSuggestions;
|
||||||
|
}
|
||||||
|
List<String> remainingSuffixes = new ArrayList<>(allSuffixes);
|
||||||
|
char[] tokenChars = token.toCharArray();
|
||||||
|
String accSuffix = "";
|
||||||
|
for (int i = 0; i < tokenChars.length; i++) {
|
||||||
|
char c = tokenChars[i];
|
||||||
|
if (Character.isDigit(c)) {
|
||||||
|
scanAndRemovePastSuffixes(remainingSuffixes, accSuffix);
|
||||||
|
accSuffix = "";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else if (Character.isLetter(c)) {
|
||||||
|
accSuffix += c;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
String prefixToken = token.substring(0, token.length() - accSuffix.length());
|
||||||
|
return SuggestionsSupplier.collectFilteredStream(remainingSuffixes.stream(), accSuffix)
|
||||||
|
.stream()
|
||||||
|
.map(str -> prefixToken + str)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<String> allSuffixes = Arrays.asList("y", "mo", "w", "d", "h", "m", "s");
|
||||||
|
private static List<String> emptyTokenSuggestions = allSuffixes.stream().map(p -> "1" + p).collect(Collectors.toList());
|
||||||
|
private static void scanAndRemovePastSuffixes(List<String> suffixes, String foundSuffix) {
|
||||||
|
for (int i = 0; i < suffixes.size(); i++) {
|
||||||
|
if (foundSuffix.startsWith(suffixes.get(i))) {
|
||||||
|
for (int j = i; j >= 0; j--) {
|
||||||
|
suffixes.remove(j);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -3,9 +3,6 @@ package fr.pandacube.util.orm;
|
|||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author Marc
|
|
||||||
*
|
|
||||||
* @param <IT> intermediate type, the type of the value transmitted to the JDBC
|
* @param <IT> intermediate type, the type of the value transmitted to the JDBC
|
||||||
* @param <JT> Java type
|
* @param <JT> Java type
|
||||||
*/
|
*/
|
||||||
@ -15,18 +12,14 @@ public class SQLCustomType<IT, JT> extends SQLType<JT> {
|
|||||||
public final Function<IT, JT> dbToJavaConv;
|
public final Function<IT, JT> dbToJavaConv;
|
||||||
public final Function<JT, IT> javaToDbConv;
|
public final Function<JT, IT> javaToDbConv;
|
||||||
|
|
||||||
protected SQLCustomType(SQLType<IT> type, Class<JT> javaT, Function<IT, JT> dbToJava, Function<JT, IT> javaToDb) {
|
/* package */ SQLCustomType(SQLType<IT> type, Class<JT> javaT, Function<IT, JT> dbToJava, Function<JT, IT> javaToDb) {
|
||||||
this(type.sqlDeclaration, type.getJavaType(), javaT, dbToJava, javaToDb);
|
this(type.sqlDeclaration, type.getJavaType(), javaT, dbToJava, javaToDb);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected SQLCustomType(String sqlD, Class<IT> intermediateJavaT, Class<JT> javaT, Function<IT, JT> dbToJava, Function<JT, IT> javaToDb) {
|
/* package */ SQLCustomType(String sqlD, Class<IT> intermediateJavaT, Class<JT> javaT, Function<IT, JT> dbToJava, Function<JT, IT> javaToDb) {
|
||||||
super(sqlD, javaT);
|
super(sqlD, javaT);
|
||||||
intermediateJavaType = intermediateJavaT;
|
intermediateJavaType = intermediateJavaT;
|
||||||
dbToJavaConv = dbToJava;
|
dbToJavaConv = dbToJava;
|
||||||
javaToDbConv = javaToDb;
|
javaToDbConv = javaToDb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// TODO tester en local
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package fr.pandacube.util.orm;
|
package fr.pandacube.util.orm;
|
||||||
|
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.sql.Date;
|
||||||
import java.sql.PreparedStatement;
|
import java.sql.PreparedStatement;
|
||||||
import java.sql.ResultSet;
|
import java.sql.ResultSet;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
@ -14,12 +15,14 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
import org.apache.commons.lang.builder.ToStringBuilder;
|
import org.apache.commons.lang.builder.ToStringBuilder;
|
||||||
|
|
||||||
import com.google.gson.Gson;
|
import com.google.gson.Gson;
|
||||||
import com.google.gson.JsonObject;
|
import com.google.gson.JsonObject;
|
||||||
|
|
||||||
|
import fr.pandacube.util.EnumUtil;
|
||||||
import fr.pandacube.util.Log;
|
import fr.pandacube.util.Log;
|
||||||
|
|
||||||
public abstract class SQLElement<E extends SQLElement<E>> {
|
public abstract class SQLElement<E extends SQLElement<E>> {
|
||||||
@ -49,7 +52,7 @@ public abstract class SQLElement<E extends SQLElement<E>> {
|
|||||||
fields = new SQLFieldMap<>((Class<E>)getClass());
|
fields = new SQLFieldMap<>((Class<E>)getClass());
|
||||||
|
|
||||||
// le champ id commun à toutes les tables
|
// le champ id commun à toutes les tables
|
||||||
SQLField<E, Integer> idF = new SQLField<>(SQLType.INT, false, true, 0);
|
SQLField<E, Integer> idF = new SQLField<>(INT, false, true, 0);
|
||||||
idF.setName("id");
|
idF.setName("id");
|
||||||
fields.addField(idF);
|
fields.addField(idF);
|
||||||
|
|
||||||
@ -133,8 +136,10 @@ public abstract class SQLElement<E extends SQLElement<E>> {
|
|||||||
return Collections.unmodifiableMap(values);
|
return Collections.unmodifiableMap(values);
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T> void set(SQLField<E, T> field, T value) {
|
@SuppressWarnings("unchecked")
|
||||||
|
public <T> E set(SQLField<E, T> field, T value) {
|
||||||
set(field, value, true);
|
set(field, value, true);
|
||||||
|
return (E) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* package */ <T> void set(SQLField<E, T> sqlField, T value, boolean setModified) {
|
/* package */ <T> void set(SQLField<E, T> sqlField, T value, boolean setModified) {
|
||||||
@ -220,7 +225,7 @@ public abstract class SQLElement<E extends SQLElement<E>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public void save() throws ORMException {
|
public E save() throws ORMException {
|
||||||
if (!isValidForSave())
|
if (!isValidForSave())
|
||||||
throw new IllegalStateException(toString() + " has at least one undefined value and can't be saved.");
|
throw new IllegalStateException(toString() + " has at least one undefined value and can't be saved.");
|
||||||
|
|
||||||
@ -236,7 +241,7 @@ public abstract class SQLElement<E extends SQLElement<E>> {
|
|||||||
modifiedSinceLastSave.remove("id");
|
modifiedSinceLastSave.remove("id");
|
||||||
Map<SQLField<E, ?>, Object> modifiedValues = getOnlyModifiedValues();
|
Map<SQLField<E, ?>, Object> modifiedValues = getOnlyModifiedValues();
|
||||||
|
|
||||||
if (modifiedValues.isEmpty()) return;
|
if (modifiedValues.isEmpty()) return (E) this;
|
||||||
|
|
||||||
ORM.update((Class<E>)getClass(), getFieldId().eq(getId()), modifiedValues);
|
ORM.update((Class<E>)getClass(), getFieldId().eq(getId()), modifiedValues);
|
||||||
}
|
}
|
||||||
@ -284,6 +289,7 @@ public abstract class SQLElement<E extends SQLElement<E>> {
|
|||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
throw new ORMException("Error while saving data", e);
|
throw new ORMException("Error while saving data", e);
|
||||||
}
|
}
|
||||||
|
return (E) this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -397,4 +403,99 @@ public abstract class SQLElement<E extends SQLElement<E>> {
|
|||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
protected static <E extends SQLElement<E>, T> SQLField<E, T> field(SQLType<T> t, boolean nul, boolean autoIncr, T deflt) {
|
||||||
|
return new SQLField<>(t, nul, autoIncr, deflt);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static <E extends SQLElement<E>, T> SQLField<E, T> field(SQLType<T> t, boolean nul) {
|
||||||
|
return new SQLField<>(t, nul);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static <E extends SQLElement<E>, T> SQLField<E, T> field(SQLType<T> t, boolean nul, boolean autoIncr) {
|
||||||
|
return new SQLField<>(t, nul, autoIncr);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static <E extends SQLElement<E>, T> SQLField<E, T> field(SQLType<T> t, boolean nul, T deflt) {
|
||||||
|
return new SQLField<>(t, nul, deflt);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected static <E extends SQLElement<E>, F extends SQLElement<F>> SQLFKField<E, Integer, F> foreignKeyId(boolean nul, Class<F> fkEl) {
|
||||||
|
return SQLFKField.idFK(nul, fkEl);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static <E extends SQLElement<E>, F extends SQLElement<F>> SQLFKField<E, Integer, F> foreignKeyId(boolean nul, Integer deflt, Class<F> fkEl) {
|
||||||
|
return SQLFKField.idFK(nul, deflt, fkEl);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static <E extends SQLElement<E>, T, F extends SQLElement<F>> SQLFKField<E, T, F> foreignKey(boolean nul, Class<F> fkEl, SQLField<F, T> fkF) {
|
||||||
|
return SQLFKField.customFK(nul, fkEl, fkF);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static <E extends SQLElement<E>, T, F extends SQLElement<F>> SQLFKField<E, T, F> foreignKey(boolean nul, T deflt, Class<F> fkEl, SQLField<F, T> fkF) {
|
||||||
|
return SQLFKField.customFK(nul, deflt, fkEl, fkF);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static final SQLType<Boolean> BOOLEAN = new SQLType<>("BOOLEAN", Boolean.class);
|
||||||
|
|
||||||
|
public static final SQLType<Integer> TINYINT = new SQLType<>("TINYINT", Integer.class); // can’t be Byte due to MYSQL JDBC Connector limitations
|
||||||
|
public static final SQLType<Integer> BYTE = TINYINT;
|
||||||
|
|
||||||
|
public static final SQLType<Integer> SMALLINT = new SQLType<>("SMALLINT", Integer.class); // can’t be Short due to MYSQL JDBC Connector limitations
|
||||||
|
public static final SQLType<Integer> SHORT = SMALLINT;
|
||||||
|
|
||||||
|
public static final SQLType<Integer> INT = new SQLType<>("INT", Integer.class);
|
||||||
|
public static final SQLType<Integer> INTEGER = INT;
|
||||||
|
|
||||||
|
public static final SQLType<Long> BIGINT = new SQLType<>("BIGINT", Long.class);
|
||||||
|
public static final SQLType<Long> LONG = BIGINT;
|
||||||
|
|
||||||
|
public static final SQLType<Date> DATE = new SQLType<>("DATE", Date.class);
|
||||||
|
|
||||||
|
public static final SQLType<Float> FLOAT = new SQLType<>("FLOAT", Float.class);
|
||||||
|
|
||||||
|
public static final SQLType<Double> DOUBLE = new SQLType<>("DOUBLE", Double.class);
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public static final SQLType<String> CHAR(int charCount) {
|
||||||
|
if (charCount <= 0) throw new IllegalArgumentException("charCount must be positive.");
|
||||||
|
return new SQLType<>("CHAR(" + charCount + ")", String.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final SQLType<String> VARCHAR(int charCount) {
|
||||||
|
if (charCount <= 0) throw new IllegalArgumentException("charCount must be positive.");
|
||||||
|
return new SQLType<>("VARCHAR(" + charCount + ")", String.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final SQLType<String> TEXT = new SQLType<>("TEXT", String.class);
|
||||||
|
public static final SQLType<String> STRING = TEXT;
|
||||||
|
|
||||||
|
public static final <T extends Enum<T>> SQLType<T> ENUM(Class<T> enumType) {
|
||||||
|
if (enumType == null) throw new IllegalArgumentException("enumType can't be null.");
|
||||||
|
String enumStr = "'";
|
||||||
|
boolean first = true;
|
||||||
|
for (T el : enumType.getEnumConstants()) {
|
||||||
|
if (!first) enumStr += "', '";
|
||||||
|
first = false;
|
||||||
|
enumStr += el.name();
|
||||||
|
|
||||||
|
}
|
||||||
|
enumStr += "'";
|
||||||
|
|
||||||
|
return new SQLCustomType<>("VARCHAR(" + enumStr + ")", String.class, enumType, s -> EnumUtil.searchEnum(enumType, s), Enum::name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final SQLType<UUID> CHAR36_UUID = new SQLCustomType<>(CHAR(36), UUID.class, UUID::fromString, UUID::toString);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package fr.pandacube.util.orm;
|
package fr.pandacube.util.orm;
|
||||||
|
|
||||||
import java.sql.PreparedStatement;
|
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -106,31 +105,25 @@ public class SQLElementList<E extends SQLElement<E>> extends ArrayList<E> {
|
|||||||
return stream().filter(SQLElement::isStored).collect(Collectors.toCollection(() -> new ArrayList<>()));
|
return stream().filter(SQLElement::isStored).collect(Collectors.toCollection(() -> new ArrayList<>()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated please use {@link ORM#delete(Class, SQLWhere)} instead,
|
||||||
|
* except if you really want to fetch the data before removing them from database.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
public synchronized void removeFromDB() {
|
public synchronized void removeFromDB() {
|
||||||
List<E> storedEl = getStoredEl();
|
List<E> storedEl = getStoredEl();
|
||||||
if (storedEl.isEmpty()) return;
|
if (storedEl.isEmpty()) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
Class<E> classEl = (Class<E>)storedEl.get(0).getClass();
|
||||||
|
|
||||||
String sqlWhere = "";
|
ORM.delete(classEl,
|
||||||
boolean first = true;
|
storedEl.get(0).getFieldId().in(storedEl.stream().map(SQLElement::getId).collect(Collectors.toList()))
|
||||||
for (E el : storedEl) {
|
);
|
||||||
if (!first) sqlWhere += " OR ";
|
for (E el : storedEl)
|
||||||
first = false;
|
el.markAsNotStored();
|
||||||
sqlWhere += "id = " + el.getId();
|
} catch (ORMException e) {
|
||||||
}
|
|
||||||
|
|
||||||
try (PreparedStatement st = ORM.getConnection().getNativeConnection()
|
|
||||||
.prepareStatement("DELETE FROM " + storedEl.get(0).tableName() + " WHERE " + sqlWhere)) {
|
|
||||||
Log.debug(st.toString());
|
|
||||||
st.executeUpdate();
|
|
||||||
|
|
||||||
for (E el : storedEl)
|
|
||||||
el.markAsNotStored();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (SQLException e) {
|
|
||||||
Log.severe(e);
|
Log.severe(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,11 +143,7 @@ public class SQLElementList<E extends SQLElement<E>> extends ArrayList<E> {
|
|||||||
return new SQLElementList<>();
|
return new SQLElementList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
SQLWhereOr<P> where = SQLWhere.or();
|
return ORM.getAll(foreignKey.getForeignElementClass(), foreignKey.getPrimaryField().in(values), orderBy, null, null);
|
||||||
values.forEach(v -> where.or(foreignKey.getPrimaryField().eq(v)));
|
|
||||||
|
|
||||||
|
|
||||||
return ORM.getAll(foreignKey.getForeignElementClass(), where, orderBy, null, null);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,10 +170,7 @@ public class SQLElementList<E extends SQLElement<E>> extends ArrayList<E> {
|
|||||||
return new SQLElementList<>();
|
return new SQLElementList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
SQLWhereOr<F> where = SQLWhere.or();
|
return ORM.getAll(foreignKey.getSQLElementType(), foreignKey.in(values), orderBy, limit, offset);
|
||||||
values.forEach(v -> where.or(foreignKey.eq(v)));
|
|
||||||
|
|
||||||
return ORM.getAll(foreignKey.getSQLElementType(), where, orderBy, limit, offset);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,11 +20,11 @@ public class SQLFKField<F extends SQLElement<F>, T, P extends SQLElement<P>> ext
|
|||||||
construct(fkEl, fkF);
|
construct(fkEl, fkF);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <E extends SQLElement<E>, F extends SQLElement<F>> SQLFKField<E, Integer, F> idFK(boolean nul, Class<F> fkEl) {
|
/* package */ static <E extends SQLElement<E>, F extends SQLElement<F>> SQLFKField<E, Integer, F> idFK(boolean nul, Class<F> fkEl) {
|
||||||
return idFK(nul, null, fkEl);
|
return idFK(nul, null, fkEl);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <E extends SQLElement<E>, F extends SQLElement<F>> SQLFKField<E, Integer, F> idFK(boolean nul, Integer deflt, Class<F> fkEl) {
|
/* package */ static <E extends SQLElement<E>, F extends SQLElement<F>> SQLFKField<E, Integer, F> idFK(boolean nul, Integer deflt, Class<F> fkEl) {
|
||||||
if (fkEl == null) throw new IllegalArgumentException("foreignKeyElement can't be null");
|
if (fkEl == null) throw new IllegalArgumentException("foreignKeyElement can't be null");
|
||||||
try {
|
try {
|
||||||
SQLField<F, Integer> f = ORM.getSQLIdField(fkEl);
|
SQLField<F, Integer> f = ORM.getSQLIdField(fkEl);
|
||||||
@ -35,11 +35,11 @@ public class SQLFKField<F extends SQLElement<F>, T, P extends SQLElement<P>> ext
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <E extends SQLElement<E>, T, F extends SQLElement<F>> SQLFKField<E, T, F> customFK(boolean nul, Class<F> fkEl, SQLField<F, T> fkF) {
|
/* package */ static <E extends SQLElement<E>, T, F extends SQLElement<F>> SQLFKField<E, T, F> customFK(boolean nul, Class<F> fkEl, SQLField<F, T> fkF) {
|
||||||
return customFK(nul, null, fkEl, fkF);
|
return customFK(nul, null, fkEl, fkF);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <E extends SQLElement<E>, T, F extends SQLElement<F>> SQLFKField<E, T, F> customFK(boolean nul, T deflt, Class<F> fkEl, SQLField<F, T> fkF) {
|
/* package */ static <E extends SQLElement<E>, T, F extends SQLElement<F>> SQLFKField<E, T, F> customFK(boolean nul, T deflt, Class<F> fkEl, SQLField<F, T> fkF) {
|
||||||
if (fkEl == null) throw new IllegalArgumentException("foreignKeyElement can't be null");
|
if (fkEl == null) throw new IllegalArgumentException("foreignKeyElement can't be null");
|
||||||
return new SQLFKField<>(fkF.type, nul, deflt, fkEl, fkF);
|
return new SQLFKField<>(fkF.type, nul, deflt, fkEl, fkF);
|
||||||
}
|
}
|
||||||
|
@ -17,22 +17,22 @@ public class SQLField<E extends SQLElement<E>, T> {
|
|||||||
public final boolean autoIncrement;
|
public final boolean autoIncrement;
|
||||||
/* package */ final T defaultValue;
|
/* package */ final T defaultValue;
|
||||||
|
|
||||||
public SQLField(SQLType<T> t, boolean nul, boolean autoIncr, T deflt) {
|
/* package */ SQLField(SQLType<T> t, boolean nul, boolean autoIncr, T deflt) {
|
||||||
type = t;
|
type = t;
|
||||||
canBeNull = nul;
|
canBeNull = nul;
|
||||||
autoIncrement = autoIncr;
|
autoIncrement = autoIncr;
|
||||||
defaultValue = deflt;
|
defaultValue = deflt;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SQLField(SQLType<T> t, boolean nul) {
|
/* package */ SQLField(SQLType<T> t, boolean nul) {
|
||||||
this(t, nul, false, null);
|
this(t, nul, false, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SQLField(SQLType<T> t, boolean nul, boolean autoIncr) {
|
/* package */ SQLField(SQLType<T> t, boolean nul, boolean autoIncr) {
|
||||||
this(t, nul, autoIncr, null);
|
this(t, nul, autoIncr, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SQLField(SQLType<T> t, boolean nul, T deflt) {
|
/* package */ SQLField(SQLType<T> t, boolean nul, T deflt) {
|
||||||
this(t, nul, false, deflt);
|
this(t, nul, false, deflt);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,10 +112,17 @@ public class SQLField<E extends SQLElement<E>, T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private SQLWhere<E> comp(SQLComparator c, T r) {
|
private SQLWhere<E> comp(SQLComparator c, T r) {
|
||||||
|
if (r == null)
|
||||||
|
throw new IllegalArgumentException("The value cannot be null. Use SQLField#isNull(value) or SQLField#isNotNull(value) to check for null values");
|
||||||
return new SQLWhereComp<>(this, c, r);
|
return new SQLWhereComp<>(this, c, r);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public SQLWhere<E> like(String like) {
|
||||||
|
return new SQLWhereLike<>(this, like);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public SQLWhere<E> in(Collection<T> v) {
|
public SQLWhere<E> in(Collection<T> v) {
|
||||||
return new SQLWhereIn<>(this, v);
|
return new SQLWhereIn<>(this, v);
|
||||||
|
@ -1,16 +1,11 @@
|
|||||||
package fr.pandacube.util.orm;
|
package fr.pandacube.util.orm;
|
||||||
|
|
||||||
import java.sql.Date;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
import fr.pandacube.util.EnumUtil;
|
|
||||||
|
|
||||||
public class SQLType<T> {
|
public class SQLType<T> {
|
||||||
|
|
||||||
protected final String sqlDeclaration;
|
protected final String sqlDeclaration;
|
||||||
private final Class<T> javaTypes;
|
private final Class<T> javaTypes;
|
||||||
|
|
||||||
protected SQLType(String sqlD, Class<T> javaT) {
|
/* package */ SQLType(String sqlD, Class<T> javaT) {
|
||||||
sqlDeclaration = sqlD;
|
sqlDeclaration = sqlD;
|
||||||
javaTypes = javaT;
|
javaTypes = javaT;
|
||||||
}
|
}
|
||||||
@ -40,56 +35,5 @@ public class SQLType<T> {
|
|||||||
return javaTypes;
|
return javaTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final SQLType<Boolean> BOOLEAN = new SQLType<>("BOOLEAN", Boolean.class);
|
|
||||||
|
|
||||||
public static final SQLType<Byte> TINYINT = new SQLType<>("TINYINT", Byte.class);
|
|
||||||
public static final SQLType<Byte> BYTE = TINYINT;
|
|
||||||
|
|
||||||
public static final SQLType<Short> SMALLINT = new SQLType<>("SMALLINT", Short.class);
|
|
||||||
public static final SQLType<Short> SHORT = SMALLINT;
|
|
||||||
|
|
||||||
public static final SQLType<Integer> INT = new SQLType<>("INT", Integer.class);
|
|
||||||
public static final SQLType<Integer> INTEGER = INT;
|
|
||||||
|
|
||||||
public static final SQLType<Long> BIGINT = new SQLType<>("BIGINT", Long.class);
|
|
||||||
public static final SQLType<Long> LONG = BIGINT;
|
|
||||||
|
|
||||||
public static final SQLType<Date> DATE = new SQLType<>("DATE", Date.class);
|
|
||||||
|
|
||||||
public static final SQLType<Float> FLOAT = new SQLType<>("FLOAT", Float.class);
|
|
||||||
|
|
||||||
public static final SQLType<Double> DOUBLE = new SQLType<>("DOUBLE", Double.class);
|
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
public static final SQLType<String> CHAR(int charCount) {
|
|
||||||
if (charCount <= 0) throw new IllegalArgumentException("charCount must be positive.");
|
|
||||||
return new SQLType<>("CHAR(" + charCount + ")", String.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final SQLType<String> VARCHAR(int charCount) {
|
|
||||||
if (charCount <= 0) throw new IllegalArgumentException("charCount must be positive.");
|
|
||||||
return new SQLType<>("VARCHAR(" + charCount + ")", String.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final SQLType<String> TEXT = new SQLType<>("TEXT", String.class);
|
|
||||||
public static final SQLType<String> STRING = TEXT;
|
|
||||||
|
|
||||||
public static final <T extends Enum<T>> SQLType<T> ENUM(Class<T> enumType) {
|
|
||||||
if (enumType == null) throw new IllegalArgumentException("enumType can't be null.");
|
|
||||||
String enumStr = "'";
|
|
||||||
boolean first = true;
|
|
||||||
for (T el : enumType.getEnumConstants()) {
|
|
||||||
if (!first) enumStr += "', '";
|
|
||||||
first = false;
|
|
||||||
enumStr += el.name();
|
|
||||||
|
|
||||||
}
|
|
||||||
enumStr += "'";
|
|
||||||
|
|
||||||
return new SQLCustomType<>("VARCHAR(" + enumStr + ")", String.class, enumType, s -> EnumUtil.searchEnum(enumType, s), Enum::name);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static final SQLType<UUID> CHAR36_UUID = new SQLCustomType<>(SQLType.CHAR(36), UUID.class, UUID::fromString, UUID::toString);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -36,10 +36,8 @@ public abstract class SQLWhere<E extends SQLElement<E>> {
|
|||||||
return new SQLWhereOr<>();
|
return new SQLWhereOr<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String escapeLike(String str) {
|
||||||
|
return str.replace("\\", "\\\\").replace("_", "\\_").replace("%", "\\%");
|
||||||
public static <E extends SQLElement<E>> SQLWhere<E> like(SQLField<E, String> f, String like) {
|
|
||||||
return new SQLWhereLike<E>(f, like);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ import org.javatuples.Pair;
|
|||||||
|
|
||||||
/* package */ class SQLWhereComp<E extends SQLElement<E>> extends SQLWhere<E> {
|
/* package */ class SQLWhereComp<E extends SQLElement<E>> extends SQLWhere<E> {
|
||||||
|
|
||||||
private SQLField<?, ?> left;
|
private SQLField<E, ?> left;
|
||||||
private SQLComparator comp;
|
private SQLComparator comp;
|
||||||
private Object right;
|
private Object right;
|
||||||
|
|
||||||
@ -18,7 +18,7 @@ import org.javatuples.Pair;
|
|||||||
* @param c the comparison operator, can't be null
|
* @param c the comparison operator, can't be null
|
||||||
* @param r the value at right of the comparison operator. Can't be null
|
* @param r the value at right of the comparison operator. Can't be null
|
||||||
*/
|
*/
|
||||||
/* package */ <T> SQLWhereComp(SQLField<?, T> l, SQLComparator c, T r) {
|
/* package */ <T> SQLWhereComp(SQLField<E, T> l, SQLComparator c, T r) {
|
||||||
if (l == null || r == null || c == null)
|
if (l == null || r == null || c == null)
|
||||||
throw new IllegalArgumentException("All arguments for SQLWhereComp constructor can't be null");
|
throw new IllegalArgumentException("All arguments for SQLWhereComp constructor can't be null");
|
||||||
left = l;
|
left = l;
|
||||||
|
@ -7,7 +7,7 @@ import org.javatuples.Pair;
|
|||||||
|
|
||||||
/* package */ class SQLWhereLike<E extends SQLElement<E>> extends SQLWhere<E> {
|
/* package */ class SQLWhereLike<E extends SQLElement<E>> extends SQLWhere<E> {
|
||||||
|
|
||||||
private SQLField<E, String> field;
|
private SQLField<E, ?> field;
|
||||||
private String likeExpr;
|
private String likeExpr;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -16,7 +16,7 @@ import org.javatuples.Pair;
|
|||||||
* @param f the field at left of the LIKE keyword. Can't be null
|
* @param f the field at left of the LIKE keyword. Can't be null
|
||||||
* @param like the like expression.
|
* @param like the like expression.
|
||||||
*/
|
*/
|
||||||
/* package */ SQLWhereLike(SQLField<E, String> f, String like) {
|
/* package */ SQLWhereLike(SQLField<E, ?> f, String like) {
|
||||||
if (f == null || like == null)
|
if (f == null || like == null)
|
||||||
throw new IllegalArgumentException("All arguments for SQLWhereLike constructor can't be null");
|
throw new IllegalArgumentException("All arguments for SQLWhereLike constructor can't be null");
|
||||||
field = f;
|
field = f;
|
||||||
|
309
src/main/java/fr/pandacube/util/text_display/Chat.java
Normal file
309
src/main/java/fr/pandacube/util/text_display/Chat.java
Normal file
@ -0,0 +1,309 @@
|
|||||||
|
package fr.pandacube.util.text_display;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import fr.pandacube.Pandacube;
|
||||||
|
import net.md_5.bungee.api.ChatColor;
|
||||||
|
import net.md_5.bungee.api.chat.BaseComponent;
|
||||||
|
import net.md_5.bungee.api.chat.ClickEvent;
|
||||||
|
import net.md_5.bungee.api.chat.HoverEvent;
|
||||||
|
import net.md_5.bungee.api.chat.ItemTag;
|
||||||
|
import net.md_5.bungee.api.chat.TextComponent;
|
||||||
|
import net.md_5.bungee.api.chat.hover.content.Content;
|
||||||
|
import net.md_5.bungee.api.chat.hover.content.Entity;
|
||||||
|
import net.md_5.bungee.api.chat.hover.content.Item;
|
||||||
|
import net.md_5.bungee.api.chat.hover.content.Text;
|
||||||
|
|
||||||
|
public abstract class Chat extends ChatStatic {
|
||||||
|
|
||||||
|
protected BaseComponent component;
|
||||||
|
protected boolean console = false;
|
||||||
|
|
||||||
|
public Chat(BaseComponent c) {
|
||||||
|
component = c;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BaseComponent get() {
|
||||||
|
return component;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BaseComponent[] getAsArray() {
|
||||||
|
return new BaseComponent[] { component };
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getLegacyText() {
|
||||||
|
return component.toLegacyText();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public Chat then(BaseComponent subComponent) {
|
||||||
|
// here are some optimizations to avoid unnecessary component nesting
|
||||||
|
if (subComponent instanceof TextComponent) {
|
||||||
|
TextComponent txtComp = (TextComponent) subComponent;
|
||||||
|
if (!txtComp.hasFormatting() && (txtComp.getText() == null || txtComp.getText().isEmpty())) {
|
||||||
|
// no need to add the provided component to the current component.
|
||||||
|
// but eventual child component must be added
|
||||||
|
if (txtComp.getExtra() != null) {
|
||||||
|
for (BaseComponent child : txtComp.getExtra())
|
||||||
|
then(child);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
component.addExtra(subComponent);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public Chat then(Chat comp) { return then(comp.get()); }
|
||||||
|
public Chat then(BaseComponent[] components) {
|
||||||
|
if (components != null) {
|
||||||
|
for (BaseComponent c : components) {
|
||||||
|
then(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Chat thenText(Object plainText) { return then(text(plainText)); }
|
||||||
|
|
||||||
|
public Chat thenInfo(Object plainText) { return then(infoText(plainText)); }
|
||||||
|
|
||||||
|
public Chat thenSuccess(Object plainText) { return then(successText(plainText)); }
|
||||||
|
|
||||||
|
public Chat thenFailure(Object plainText) { return then(failureText(plainText)); }
|
||||||
|
|
||||||
|
public Chat thenData(Object plainText) { return then(dataText(plainText)); }
|
||||||
|
|
||||||
|
public Chat thenDecoration(Object plainText) { return then(decorationText(plainText)); }
|
||||||
|
|
||||||
|
public Chat thenPlayerName(String legacyText) { return then(playerNameText(legacyText)); }
|
||||||
|
|
||||||
|
public Chat thenNewLine() { return thenText("\n"); }
|
||||||
|
|
||||||
|
public Chat thenLegacyText(Object legacyText) { return then(legacyText(legacyText)); }
|
||||||
|
|
||||||
|
public Chat thenTranslation(String key, Object... with) { return then(translation(key, with)); }
|
||||||
|
|
||||||
|
public Chat thenKeyBind(String key) { return then(keybind(key)); }
|
||||||
|
|
||||||
|
public Chat thenScore(String name, String objective, String value) { return then(score(name, objective, value)); }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public Chat thenURLLink(Chat inner, String url, Chat hover) { return then(ChatUtil.createURLLink(inner, url, hover)); }
|
||||||
|
public Chat thenURLLink(Chat inner, String url) { return thenURLLink(inner, url, null); }
|
||||||
|
public Chat thenURLLink(String url, Chat hover) { return thenURLLink(text(url), url, hover); }
|
||||||
|
public Chat thenURLLink(String url) { return thenURLLink(text(url), url); }
|
||||||
|
|
||||||
|
public Chat thenCommandLink(Chat inner, String cmdWithSlash, Chat hover) { return then(ChatUtil.createCommandLink(inner, cmdWithSlash, hover)); }
|
||||||
|
public Chat thenCommandLink(Chat inner, String cmdWithSlash) { return thenCommandLink(inner, cmdWithSlash, null); }
|
||||||
|
public Chat thenCommandLink(String cmdWithSlash, Chat hover) { return thenCommandLink(text(cmdWithSlash), cmdWithSlash, hover); }
|
||||||
|
public Chat thenCommandLink(String cmdWithSlash) { return thenCommandLink(text(cmdWithSlash), cmdWithSlash); }
|
||||||
|
|
||||||
|
public Chat thenCommandSuggest(Chat inner, String cmdWithSlash, Chat hover) { return then(ChatUtil.createCommandSuggest(inner, cmdWithSlash, hover)); }
|
||||||
|
public Chat thenCommandSuggest(Chat inner, String cmdWithSlash) { return thenCommandSuggest(inner, cmdWithSlash, null); }
|
||||||
|
public Chat thenCommandSuggest(String cmdWithSlash, Chat hover) { return thenCommandSuggest(text(cmdWithSlash), cmdWithSlash, hover); }
|
||||||
|
public Chat thenCommandSuggest(String cmdWithSlash) { return thenCommandSuggest(text(cmdWithSlash), cmdWithSlash); }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws a full line with the default decoration char, colored with the default decoration color.
|
||||||
|
* @return this, for method chaining
|
||||||
|
*/
|
||||||
|
public Chat thenEmptyCharLine() {
|
||||||
|
return then(ChatUtil.emptyLine(Pandacube.CHAT_DECORATION_CHAR, Pandacube.CHAT_DECORATION_COLOR, console));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws a full line with the default decoration char, colored with the default decoration color,
|
||||||
|
* and with the provided Chat left aligned on the line, default to the decoration color, and surrounded with 1 space on each side.
|
||||||
|
* @return this, for method chaining
|
||||||
|
*/
|
||||||
|
public Chat thenLeftTextCharLine(Chat leftText) {
|
||||||
|
return then(ChatUtil.leftText(chat().decorationColor().thenText(" ").then(leftText).thenText(" ").get(), Pandacube.CHAT_DECORATION_CHAR,
|
||||||
|
Pandacube.CHAT_DECORATION_COLOR, Pandacube.CHAT_NB_CHAR_MARGIN, console));
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Draws a full line with the default decoration char, colored with the default decoration color,
|
||||||
|
* and with the provided component left aligned on the line, default to the decoration color, and surrounded with 1 space on each side.
|
||||||
|
* @return this, for method chaining
|
||||||
|
*/
|
||||||
|
public Chat thenLeftTextCharLine(BaseComponent leftText) {
|
||||||
|
return thenLeftTextCharLine(chatComponent(leftText));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws a full line with the default decoration char, colored with the default decoration color,
|
||||||
|
* and with the provided Chat right aligned on the line, default to the decoration color, and surrounded with 1 space on each side.
|
||||||
|
* @return this, for method chaining
|
||||||
|
*/
|
||||||
|
public Chat thenRightTextCharLine(Chat rightText) {
|
||||||
|
return then(ChatUtil.rightText(chat().decorationColor().thenText(" ").then(rightText).thenText(" ").get(), Pandacube.CHAT_DECORATION_CHAR,
|
||||||
|
Pandacube.CHAT_DECORATION_COLOR, Pandacube.CHAT_NB_CHAR_MARGIN, console));
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Draws a full line with the default decoration char, colored with the default decoration color,
|
||||||
|
* and with the provided component right aligned on the line, default to the decoration color, and surrounded with 1 space on each side.
|
||||||
|
* @return this, for method chaining
|
||||||
|
*/
|
||||||
|
public Chat thenRightTextCharLine(BaseComponent leftText) {
|
||||||
|
return thenRightTextCharLine(chatComponent(leftText));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws a full line with the default decoration char, colored with the default decoration color,
|
||||||
|
* and with the provided Chat centered on the line, default to the decoration color, and surrounded with 1 space on each side.
|
||||||
|
* @return this, for method chaining
|
||||||
|
*/
|
||||||
|
public Chat thenCenterTextCharLine(Chat centerText) {
|
||||||
|
return then(ChatUtil.centerText(chat().decorationColor().thenText(" ").then(centerText).thenText(" ").get(), Pandacube.CHAT_DECORATION_CHAR,
|
||||||
|
Pandacube.CHAT_DECORATION_COLOR, console));
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Draws a full line with the default decoration char, colored with the default decoration color,
|
||||||
|
* and with the provided component centered on the line, default to the decoration color, and surrounded with 1 space on each side.
|
||||||
|
* @return this, for method chaining
|
||||||
|
*/
|
||||||
|
public Chat thenCenterTextCharLine(BaseComponent leftText) {
|
||||||
|
return thenCenterTextCharLine(chatComponent(leftText));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static class FormatableChat extends Chat {
|
||||||
|
public FormatableChat(BaseComponent c) {
|
||||||
|
super(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FormatableChat console(boolean c) { console = c; return this; }
|
||||||
|
|
||||||
|
public FormatableChat color(ChatColor c) { component.setColor(c); return this; }
|
||||||
|
public FormatableChat color(Color c) { return color(ChatColor.of(c)); }
|
||||||
|
public FormatableChat color(String c) { return color(ChatColor.of(c)); }
|
||||||
|
|
||||||
|
public FormatableChat black() { return color(ChatColor.BLACK); }
|
||||||
|
public FormatableChat darkBlue() { return color(ChatColor.DARK_BLUE); }
|
||||||
|
public FormatableChat darkGreen() { return color(ChatColor.DARK_GREEN); }
|
||||||
|
public FormatableChat darkAqua() { return color(ChatColor.DARK_AQUA); }
|
||||||
|
public FormatableChat darkRed() { return color(ChatColor.DARK_RED); }
|
||||||
|
public FormatableChat darkPurple() { return color(ChatColor.DARK_PURPLE); }
|
||||||
|
public FormatableChat gold() { return color(ChatColor.GOLD); }
|
||||||
|
public FormatableChat gray() { return color(ChatColor.GRAY); }
|
||||||
|
public FormatableChat darkGray() { return color(ChatColor.DARK_GRAY); }
|
||||||
|
public FormatableChat blue() { return color(ChatColor.BLUE); }
|
||||||
|
public FormatableChat green() { return color(ChatColor.GREEN); }
|
||||||
|
public FormatableChat aqua() { return color(ChatColor.AQUA); }
|
||||||
|
public FormatableChat red() { return color(ChatColor.RED); }
|
||||||
|
public FormatableChat lightPurple() { return color(ChatColor.LIGHT_PURPLE); }
|
||||||
|
public FormatableChat yellow() { return color(ChatColor.YELLOW); }
|
||||||
|
public FormatableChat white() { return color(ChatColor.WHITE); }
|
||||||
|
|
||||||
|
public FormatableChat successColor() { return color(Pandacube.CHAT_SUCCESS_COLOR); }
|
||||||
|
public FormatableChat failureColor() { return color(Pandacube.CHAT_FAILURE_COLOR); }
|
||||||
|
public FormatableChat infoColor() { return color(Pandacube.CHAT_INFO_COLOR); }
|
||||||
|
public FormatableChat dataColor() { return color(Pandacube.CHAT_DATA_COLOR); }
|
||||||
|
public FormatableChat decorationColor() { return color(Pandacube.CHAT_DECORATION_COLOR); }
|
||||||
|
|
||||||
|
public FormatableChat font(String f) { component.setFont(f); return this; }
|
||||||
|
|
||||||
|
public FormatableChat bold(Boolean b) { component.setBold(b); return this; }
|
||||||
|
public FormatableChat bold() { return bold(true); }
|
||||||
|
|
||||||
|
public FormatableChat italic(Boolean i) { component.setItalic(i); return this; }
|
||||||
|
public FormatableChat italic() { return italic(true); }
|
||||||
|
|
||||||
|
public FormatableChat underlined(Boolean u) { component.setUnderlined(u); return this; }
|
||||||
|
public FormatableChat underlined() { return underlined(true); }
|
||||||
|
|
||||||
|
public FormatableChat strikethrough(Boolean s) { component.setStrikethrough(s); return this; }
|
||||||
|
public FormatableChat strikethrough() { return strikethrough(true); }
|
||||||
|
|
||||||
|
public FormatableChat obfuscated(Boolean o) { component.setObfuscated(o); return this; }
|
||||||
|
public FormatableChat obfuscated() { return obfuscated(true); }
|
||||||
|
|
||||||
|
public FormatableChat shiftClickInsertion(String i) { component.setInsertion(i); return this; }
|
||||||
|
|
||||||
|
private FormatableChat clickEvent(ClickEvent e) { component.setClickEvent(e); return this; }
|
||||||
|
private FormatableChat clickEvent(ClickEvent.Action a, String v) { return clickEvent(new ClickEvent(a, v)); }
|
||||||
|
public FormatableChat clickCommand(String cmdWithSlash) { return clickEvent(ClickEvent.Action.RUN_COMMAND, cmdWithSlash); }
|
||||||
|
public FormatableChat clickSuggest(String cmdWithSlash) { return clickEvent(ClickEvent.Action.SUGGEST_COMMAND, cmdWithSlash); }
|
||||||
|
public FormatableChat clickClipboard(String value) { return clickEvent(ClickEvent.Action.COPY_TO_CLIPBOARD, value); }
|
||||||
|
public FormatableChat clickURL(String url) { return clickEvent(ClickEvent.Action.OPEN_URL, url); }
|
||||||
|
public FormatableChat clickBookPage(int page) { return clickEvent(ClickEvent.Action.CHANGE_PAGE, Integer.toString(page)); }
|
||||||
|
|
||||||
|
private FormatableChat hoverEvent(HoverEvent e) { component.setHoverEvent(e); return this; }
|
||||||
|
private FormatableChat hoverEvent(HoverEvent.Action a, Content v) { return hoverEvent(new HoverEvent(a, v)); }
|
||||||
|
private FormatableChat hoverText(Text v) { return hoverEvent(HoverEvent.Action.SHOW_TEXT, v); }
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
|
public FormatableChat hoverText(BaseComponent v) {
|
||||||
|
try {
|
||||||
|
return hoverText(new Text( new BaseComponent[] {v}));
|
||||||
|
} catch (NoSuchMethodError e) {
|
||||||
|
return hoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, new BaseComponent[] {v}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public FormatableChat hoverText(Chat v) { return hoverText(v.get()); }
|
||||||
|
public FormatableChat hoverText(String legacyText) { return hoverText(legacyText(legacyText)); }
|
||||||
|
private FormatableChat hoverItem(Item v) { return hoverEvent(HoverEvent.Action.SHOW_ITEM, v); }
|
||||||
|
/** @param id namespaced item id */
|
||||||
|
public FormatableChat hoverItem(String id, int stackSize, ItemTag tag) { return hoverItem(new Item(id, stackSize, tag)); }
|
||||||
|
/** @param id namespaced item id */
|
||||||
|
public FormatableChat hoverItem(String id, int stackSize) { return hoverItem(id, stackSize, null); }
|
||||||
|
/** @param id namespaced item id */
|
||||||
|
public FormatableChat hoverItem(String id, ItemTag tag) { return hoverItem(id, -1, tag); }
|
||||||
|
/** @param id namespaced item id */
|
||||||
|
public FormatableChat hoverItem(String id) { return hoverItem(id, -1, null); }
|
||||||
|
public FormatableChat hoverEntity(Entity e) { return hoverEvent(HoverEvent.Action.SHOW_ENTITY, e); }
|
||||||
|
/** @param type namespaced entity type
|
||||||
|
* @param id cannot be null */
|
||||||
|
public FormatableChat hoverEntity(String type, UUID id, BaseComponent displayName) { return hoverEntity(new Entity(type, id.toString(), displayName)); }
|
||||||
|
/** @param type namespaced entity type
|
||||||
|
* @param id cannot be null */
|
||||||
|
public FormatableChat hoverEntity(String type, UUID id) { return hoverEntity(type, id, null); }
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* package */ static Object[] filterChatToBaseComponent(Object[] values) {
|
||||||
|
if (values == null)
|
||||||
|
return null;
|
||||||
|
for (int i = 0; i < values.length; i++) {
|
||||||
|
Object v = values[i];
|
||||||
|
if (v instanceof Chat)
|
||||||
|
values[i] = ((Chat) v).get();
|
||||||
|
}
|
||||||
|
return values;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
293
src/main/java/fr/pandacube/util/text_display/ChatColorUtil.java
Normal file
293
src/main/java/fr/pandacube/util/text_display/ChatColorUtil.java
Normal file
@ -0,0 +1,293 @@
|
|||||||
|
package fr.pandacube.util.text_display;
|
||||||
|
|
||||||
|
import java.awt.Color;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import org.javatuples.Pair;
|
||||||
|
|
||||||
|
import net.md_5.bungee.api.ChatColor;
|
||||||
|
|
||||||
|
public class ChatColorUtil {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static final String ALL_CODES = "0123456789AaBbCcDdEeFfKkLlMmNnOoPpRr";
|
||||||
|
public static final String ALL_COLORS = "0123456789AaBbCcDdEeFf";
|
||||||
|
|
||||||
|
|
||||||
|
private static Pattern HEX_COLOR_PATTERN = Pattern.compile("§x(?>§[0-9a-f]){6}", Pattern.CASE_INSENSITIVE);
|
||||||
|
private static Pattern ESS_COLOR_PATTERN = Pattern.compile("§#[0-9a-f]{6}", Pattern.CASE_INSENSITIVE);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the legacy format needed to reproduce the format at the end of the provided legacy text.
|
||||||
|
* Supports standard chat colors and formats, BungeeCord Chat rgb format and EssentialsX rgb format.
|
||||||
|
* The RGB value from EssentialsX format is converted to BungeeCord Chat when included in the returned value.
|
||||||
|
* @param legacyText
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String getLastColors(String legacyText) {
|
||||||
|
String result = "";
|
||||||
|
int length = legacyText.length();
|
||||||
|
|
||||||
|
for (int index = length - 2; index >= 0; index--) {
|
||||||
|
if (legacyText.charAt(index) == ChatColor.COLOR_CHAR) {
|
||||||
|
|
||||||
|
// detection of rgb color §x§0§1§2§3§4§5
|
||||||
|
String rgb;
|
||||||
|
if (index > 11
|
||||||
|
&& legacyText.charAt(index - 12) == ChatColor.COLOR_CHAR
|
||||||
|
&& (legacyText.charAt(index - 11) == 'x'
|
||||||
|
|| legacyText.charAt(index - 11) == 'X')
|
||||||
|
&& HEX_COLOR_PATTERN.matcher(rgb = legacyText.substring(index - 12, index + 2)).matches()) {
|
||||||
|
result = rgb + result;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// detection of rgb color §#012345 (and converting it to bungee chat format)
|
||||||
|
if (index < length - 7
|
||||||
|
&& legacyText.charAt(index + 1) == '#'
|
||||||
|
&& ESS_COLOR_PATTERN.matcher(rgb = legacyText.substring(index, index + 8)).matches()) {
|
||||||
|
rgb = "§x§" + rgb.charAt(2) + "§" + rgb.charAt(3)
|
||||||
|
+ "§" + rgb.charAt(4) + "§" + rgb.charAt(5)
|
||||||
|
+ "§" + rgb.charAt(6) + "§" + rgb.charAt(7);
|
||||||
|
result = rgb + result;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// try detect non-rgb format
|
||||||
|
char colorChar = legacyText.charAt(index + 1);
|
||||||
|
ChatColor legacyColor = getChatColorByChar(colorChar);
|
||||||
|
|
||||||
|
if (legacyColor != null) {
|
||||||
|
result = legacyColor.toString() + result;
|
||||||
|
|
||||||
|
// Once we find a color or reset we can stop searching
|
||||||
|
char col = legacyColor.toString().charAt(1);
|
||||||
|
if ((col >= '0' && col <= '9')
|
||||||
|
|| (col >= 'a' && col <= 'f')
|
||||||
|
|| col == 'r') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ChatColor getChatColorByChar(char code) {
|
||||||
|
return ChatColor.getByChar(Character.toLowerCase(code));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translate the color code of the provided string, that uses the the color char, to
|
||||||
|
* the {@code §} color code format.
|
||||||
|
* <p>
|
||||||
|
* This method is the improved version of {@link ChatColor#translateAlternateColorCodes(char, String)},
|
||||||
|
* because it takes into account essentials RGB color code, and {@code altColorChar} escaping (by doubling it).
|
||||||
|
* Essentials RGB color code are converted to Bungee chat RGB format, so the returned string can be converted
|
||||||
|
* to component (see {@link Chat#legacyText(Object)}).
|
||||||
|
* <p>
|
||||||
|
* This method should be used for user input (no permission check) or string configuration, but not string
|
||||||
|
* from another API or containing URLs.
|
||||||
|
*/
|
||||||
|
public static String translateAlternateColorCodes(char altColorChar, String textToTranslate)
|
||||||
|
{
|
||||||
|
char colorChar = ChatColor.COLOR_CHAR;
|
||||||
|
StringBuilder acc = new StringBuilder();
|
||||||
|
char[] b = textToTranslate.toCharArray();
|
||||||
|
for ( int i = 0; i < b.length; i++ )
|
||||||
|
{
|
||||||
|
if (i < b.length - 1 // legacy chat format
|
||||||
|
&& b[i] == altColorChar && ALL_CODES.indexOf(b[i + 1]) > -1)
|
||||||
|
{
|
||||||
|
acc.append(colorChar);
|
||||||
|
acc.append(lowerCase(b[i + 1]));
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
else if (i < b.length - 13 // bungee chat RGB format
|
||||||
|
&& b[i] == altColorChar
|
||||||
|
&& lowerCase(b[i + 1]) == 'x'
|
||||||
|
&& b[i + 2] == altColorChar && ALL_COLORS.indexOf(b[i + 3]) > -1
|
||||||
|
&& b[i + 4] == altColorChar && ALL_COLORS.indexOf(b[i + 5]) > -1
|
||||||
|
&& b[i + 6] == altColorChar && ALL_COLORS.indexOf(b[i + 7]) > -1
|
||||||
|
&& b[i + 8] == altColorChar && ALL_COLORS.indexOf(b[i + 9]) > -1
|
||||||
|
&& b[i + 10] == altColorChar && ALL_COLORS.indexOf(b[i + 11]) > -1
|
||||||
|
&& b[i + 12] == altColorChar && ALL_COLORS.indexOf(b[i + 13]) > -1) {
|
||||||
|
acc.append(colorChar).append(lowerCase(b[i + 1]));
|
||||||
|
acc.append(colorChar).append(lowerCase(b[i + 3]));
|
||||||
|
acc.append(colorChar).append(lowerCase(b[i + 5]));
|
||||||
|
acc.append(colorChar).append(lowerCase(b[i + 7]));
|
||||||
|
acc.append(colorChar).append(lowerCase(b[i + 9]));
|
||||||
|
acc.append(colorChar).append(lowerCase(b[i + 11]));
|
||||||
|
acc.append(colorChar).append(lowerCase(b[i + 13]));
|
||||||
|
i+=13;
|
||||||
|
}
|
||||||
|
else if (i < b.length - 7 // Essentials chat RGB format
|
||||||
|
&& b[i] == altColorChar
|
||||||
|
&& b[i + 1] == '#'
|
||||||
|
&& ALL_COLORS.indexOf(b[i + 2]) > -1 && ALL_COLORS.indexOf(b[i + 3]) > -1
|
||||||
|
&& ALL_COLORS.indexOf(b[i + 4]) > -1 && ALL_COLORS.indexOf(b[i + 5]) > -1
|
||||||
|
&& ALL_COLORS.indexOf(b[i + 6]) > -1 && ALL_COLORS.indexOf(b[i + 7]) > -1) {
|
||||||
|
acc.append(colorChar).append('x');
|
||||||
|
acc.append(colorChar).append(lowerCase(b[i + 2]));
|
||||||
|
acc.append(colorChar).append(lowerCase(b[i + 3]));
|
||||||
|
acc.append(colorChar).append(lowerCase(b[i + 4]));
|
||||||
|
acc.append(colorChar).append(lowerCase(b[i + 5]));
|
||||||
|
acc.append(colorChar).append(lowerCase(b[i + 6]));
|
||||||
|
acc.append(colorChar).append(lowerCase(b[i + 7]));
|
||||||
|
i+=7;
|
||||||
|
}
|
||||||
|
else if (i < b.length - 1 && b[i] == altColorChar && b[i + 1] == altColorChar) {
|
||||||
|
acc.append(altColorChar);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
acc.append(b[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return acc.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static char lowerCase(char c) { return Character.toLowerCase(c); }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static ChatColor interpolateColor(float v0, float v1, float v, ChatColor cc0, ChatColor cc1) {
|
||||||
|
Color c0 = cc0.getColor(), c1 = cc1.getColor();
|
||||||
|
int r0 = c0.getRed(), g0 = c0.getGreen(), b0 = c0.getBlue(),
|
||||||
|
r1 = c1.getRed(), g1 = c1.getGreen(), b1 = c1.getBlue();
|
||||||
|
float normV = (v - v0) / (v1 - v0);
|
||||||
|
return ChatColor.of(new Color(
|
||||||
|
(int) (r0 + (r1 - r0) * normV),
|
||||||
|
(int) (g0 + (g1 - g0) * normV),
|
||||||
|
(int) (b0 + (b1 - b0) * normV)));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static class ChatValueGradient {
|
||||||
|
List<Pair<Float, ChatColor>> colors = new ArrayList<>();
|
||||||
|
|
||||||
|
public synchronized ChatValueGradient add(float v, ChatColor col) {
|
||||||
|
colors.add(Pair.with(v, col));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized ChatColor pickColorAt(float v) {
|
||||||
|
if (colors.isEmpty())
|
||||||
|
throw new IllegalStateException("Must define at least one color in this ChatValueGradient instance.");
|
||||||
|
if (colors.size() == 1)
|
||||||
|
return colors.get(0).getValue1();
|
||||||
|
|
||||||
|
colors.sort((p1, p2) -> Float.compare(p1.getValue0(), p2.getValue0()));
|
||||||
|
|
||||||
|
if (v <= colors.get(0).getValue0())
|
||||||
|
return colors.get(0).getValue1();
|
||||||
|
if (v >= colors.get(colors.size() - 1).getValue0())
|
||||||
|
return colors.get(colors.size() - 1).getValue1();
|
||||||
|
|
||||||
|
int p1 = 1;
|
||||||
|
for (; p1 < colors.size(); p1++) {
|
||||||
|
if (colors.get(p1).getValue0() >= v)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
int p0 = p1 - 1;
|
||||||
|
float v0 = colors.get(p0).getValue0(), v1 = colors.get(p1).getValue0();
|
||||||
|
ChatColor cc0 = colors.get(p0).getValue1(), cc1 = colors.get(p1).getValue1();
|
||||||
|
|
||||||
|
return interpolateColor(v0, v1, v, cc0, cc1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
73
src/main/java/fr/pandacube/util/text_display/ChatStatic.java
Normal file
73
src/main/java/fr/pandacube/util/text_display/ChatStatic.java
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
package fr.pandacube.util.text_display;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import fr.pandacube.util.text_display.Chat.FormatableChat;
|
||||||
|
import net.md_5.bungee.api.chat.BaseComponent;
|
||||||
|
import net.md_5.bungee.api.chat.KeybindComponent;
|
||||||
|
import net.md_5.bungee.api.chat.Keybinds;
|
||||||
|
import net.md_5.bungee.api.chat.ScoreComponent;
|
||||||
|
import net.md_5.bungee.api.chat.TextComponent;
|
||||||
|
import net.md_5.bungee.api.chat.TranslatableComponent;
|
||||||
|
|
||||||
|
public abstract class ChatStatic {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static FormatableChat chatComponent(BaseComponent c) {
|
||||||
|
return new FormatableChat(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FormatableChat chat() {
|
||||||
|
return chatComponent(new TextComponent());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FormatableChat chatComponent(BaseComponent[] c) {
|
||||||
|
return chatComponent(new TextComponent(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FormatableChat text(Object plainText) {
|
||||||
|
return chatComponent(new TextComponent(Objects.toString(plainText)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FormatableChat legacyText(Object legacyText) {
|
||||||
|
return chatComponent(TextComponent.fromLegacyText(Objects.toString(legacyText), null));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FormatableChat infoText(Object plainText) {
|
||||||
|
return text(plainText).infoColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FormatableChat dataText(Object plainText) {
|
||||||
|
return text(plainText).dataColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FormatableChat decorationText(Object plainText) {
|
||||||
|
return text(plainText).decorationColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FormatableChat successText(Object plainText) {
|
||||||
|
return text(plainText).successColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FormatableChat failureText(Object plainText) {
|
||||||
|
return text(plainText).failureColor();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FormatableChat playerNameText(String legacyText) {
|
||||||
|
return legacyText(legacyText).white();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FormatableChat translation(String key, Object... with) {
|
||||||
|
return chatComponent(new TranslatableComponent(key, Chat.filterChatToBaseComponent(with)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @param key one of the values in {@link Keybinds}. */
|
||||||
|
public static FormatableChat keybind(String key) {
|
||||||
|
return chatComponent(new KeybindComponent(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FormatableChat score(String name, String objective, String value) {
|
||||||
|
return chatComponent(new ScoreComponent(name, objective, value));
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,10 @@
|
|||||||
package fr.pandacube.util.text_display;
|
package fr.pandacube.util.text_display;
|
||||||
|
|
||||||
|
import static fr.pandacube.util.text_display.Chat.chatComponent;
|
||||||
|
import static fr.pandacube.util.text_display.ChatStatic.chat;
|
||||||
|
import static fr.pandacube.util.text_display.ChatStatic.legacyText;
|
||||||
|
import static fr.pandacube.util.text_display.ChatStatic.text;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -9,12 +14,14 @@ import java.util.TreeSet;
|
|||||||
|
|
||||||
import com.google.common.collect.ImmutableMap;
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
|
||||||
|
import fr.pandacube.Pandacube;
|
||||||
|
import fr.pandacube.util.text_display.Chat.FormatableChat;
|
||||||
import net.md_5.bungee.api.ChatColor;
|
import net.md_5.bungee.api.ChatColor;
|
||||||
import net.md_5.bungee.api.chat.BaseComponent;
|
import net.md_5.bungee.api.chat.BaseComponent;
|
||||||
import net.md_5.bungee.api.chat.TextComponent;
|
import net.md_5.bungee.api.chat.TextComponent;
|
||||||
import net.md_5.bungee.api.chat.TranslatableComponent;
|
import net.md_5.bungee.api.chat.TranslatableComponent;
|
||||||
|
|
||||||
public class DisplayUtil {
|
public class ChatUtil {
|
||||||
|
|
||||||
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>()
|
||||||
@ -35,32 +42,30 @@ public class DisplayUtil {
|
|||||||
|
|
||||||
public static final int CONSOLE_NB_CHAR_DEFAULT = 50;
|
public static final int CONSOLE_NB_CHAR_DEFAULT = 50;
|
||||||
|
|
||||||
public static final ChatColor COLOR_TITLE = ChatColor.GOLD;
|
|
||||||
public static final ChatColor COLOR_LINK = ChatColor.GREEN;
|
|
||||||
public static final ChatColor COLOR_COMMAND = ChatColor.GRAY;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static BaseComponent createURLLink(String text, String url) {
|
||||||
|
return createURLLink(legacyText(text), url, null);
|
||||||
|
}
|
||||||
|
|
||||||
public static BaseComponent createURLLink(String text, String url, String hoverText) {
|
public static BaseComponent createURLLink(String text, String url, String hoverText) {
|
||||||
return _createURLLink(new Display(text), url, hoverText);
|
return createURLLink(legacyText(text), url, hoverText != null ? legacyText(hoverText) : null);
|
||||||
}
|
|
||||||
public static BaseComponent createURLLink(BaseComponent text, String url, String hoverText) {
|
|
||||||
return _createURLLink(new Display(text), url, hoverText);
|
|
||||||
}
|
|
||||||
public static BaseComponent createURLLink(BaseComponent[] text, String url, String hoverText) {
|
|
||||||
return _createURLLink(new Display(text), url, hoverText);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static BaseComponent _createURLLink(Display d, String url, String hoverText) {
|
/* package */ static BaseComponent createURLLink(Chat element, String url, Chat hover) {
|
||||||
String dispURL = (url.length() > 50) ? (url.substring(0, 48) + "...") : url;
|
String dispURL = (url.length() > 50) ? (url.substring(0, 48) + "...") : url;
|
||||||
return d.clickURL(url)
|
return chat()
|
||||||
.hoverText(ChatColor.GRAY + ((hoverText == null) ? "Cliquez pour accéder au site :" : hoverText) + "\n"
|
.clickURL(url)
|
||||||
+ ChatColor.GRAY + dispURL)
|
.color(Pandacube.CHAT_URL_COLOR)
|
||||||
.color(COLOR_LINK).get();
|
.hoverText(
|
||||||
|
hover != null ? hover : Chat.text(dispURL)
|
||||||
|
)
|
||||||
|
.then(element)
|
||||||
|
.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -71,19 +76,19 @@ public class DisplayUtil {
|
|||||||
|
|
||||||
|
|
||||||
public static BaseComponent createCommandLink(String text, String commandWithSlash, String hoverText) {
|
public static BaseComponent createCommandLink(String text, String commandWithSlash, String hoverText) {
|
||||||
return createCommandLink(text, commandWithSlash, hoverText == null ? null : TextComponent.fromLegacyText(hoverText));
|
return createCommandLink(text, commandWithSlash, hoverText == null ? null : legacyText(hoverText));
|
||||||
}
|
}
|
||||||
public static BaseComponent createCommandLink(String text, String commandWithSlash, BaseComponent hoverText) {
|
public static BaseComponent createCommandLink(String text, String commandWithSlash, Chat hoverText) {
|
||||||
return createCommandLink(text, commandWithSlash, hoverText == null ? null : new BaseComponent[] {hoverText});
|
return createCommandLink(legacyText(text), commandWithSlash, hoverText);
|
||||||
}
|
|
||||||
public static BaseComponent createCommandLink(String text, String commandWithSlash, BaseComponent[] hoverText) {
|
|
||||||
return _createCommandLink(new Display(text), commandWithSlash, hoverText);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static BaseComponent _createCommandLink(Display d, String commandWithSlash, BaseComponent[] hoverText) {
|
/* package */ static BaseComponent createCommandLink(Chat d, String commandWithSlash, Chat hoverText) {
|
||||||
d.clickCommand(commandWithSlash).color(COLOR_COMMAND);
|
FormatableChat c = chat()
|
||||||
if (hoverText != null) d.hoverText(hoverText);
|
.clickCommand(commandWithSlash)
|
||||||
return d.get();
|
.color(Pandacube.CHAT_COMMAND_COLOR);
|
||||||
|
if (hoverText != null)
|
||||||
|
c.hoverText(hoverText);
|
||||||
|
return c.then(d).get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -96,19 +101,19 @@ public class DisplayUtil {
|
|||||||
|
|
||||||
|
|
||||||
public static BaseComponent createCommandSuggest(String text, String commandWithSlash, String hoverText) {
|
public static BaseComponent createCommandSuggest(String text, String commandWithSlash, String hoverText) {
|
||||||
return createCommandSuggest(text, commandWithSlash, hoverText == null ? null : TextComponent.fromLegacyText(hoverText));
|
return createCommandSuggest(text, commandWithSlash, hoverText == null ? null : legacyText(hoverText));
|
||||||
}
|
}
|
||||||
public static BaseComponent createCommandSuggest(String text, String commandWithSlash, BaseComponent hoverText) {
|
public static BaseComponent createCommandSuggest(String text, String commandWithSlash, Chat hoverText) {
|
||||||
return createCommandSuggest(text, commandWithSlash, hoverText == null ? null : new BaseComponent[] {hoverText});
|
return createCommandSuggest(legacyText(text), commandWithSlash, hoverText);
|
||||||
}
|
|
||||||
public static BaseComponent createCommandSuggest(String text, String commandWithSlash, BaseComponent[] hoverText) {
|
|
||||||
return _createCommandSuggest(new Display(text), commandWithSlash, hoverText);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static BaseComponent _createCommandSuggest(Display d, String commandWithSlash, BaseComponent[] hoverText) {
|
/* package */ static BaseComponent createCommandSuggest(Chat d, String commandWithSlash, Chat hoverText) {
|
||||||
d.clickSuggest(commandWithSlash).color(COLOR_COMMAND);
|
FormatableChat c = chat()
|
||||||
if (hoverText != null) d.hoverText(hoverText);
|
.clickSuggest(commandWithSlash)
|
||||||
return d.get();
|
.color(Pandacube.CHAT_COMMAND_COLOR);
|
||||||
|
if (hoverText != null)
|
||||||
|
c.hoverText(hoverText);
|
||||||
|
return c.then(d).get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -134,32 +139,33 @@ public class DisplayUtil {
|
|||||||
pagesToDisplay.add(i);
|
pagesToDisplay.add(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
Display d = new Display(prefix);
|
Chat d = chat().thenLegacyText(prefix);
|
||||||
boolean first = true;
|
boolean first = true;
|
||||||
int previous = 0;
|
int previous = 0;
|
||||||
|
|
||||||
for (int page : pagesToDisplay) {
|
for (int page : pagesToDisplay) {
|
||||||
if (!first) {
|
if (!first) {
|
||||||
if (page == previous + 1) {
|
if (page == previous + 1) {
|
||||||
d.next(" ");
|
d.thenText(" ");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (cmdFormat.endsWith("%d")) {
|
if (cmdFormat.endsWith("%d")) {
|
||||||
d.next(" ");
|
d.thenText(" ");
|
||||||
d.next(createCommandSuggest("...", cmdFormat.substring(0, cmdFormat.length() - 2), "Choisir la page"));
|
d.then(createCommandSuggest("...", cmdFormat.substring(0, cmdFormat.length() - 2), "Choisir la page"));
|
||||||
d.next(" ");
|
d.thenText(" ");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
d.next(" ... ");
|
d.thenText(" ... ");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
first = false;
|
first = false;
|
||||||
|
|
||||||
d.next(createCommandLink(Integer.toString(page), String.format(cmdFormat, page), "Aller à la page " + page));
|
FormatableChat pDisp = chatComponent(createCommandLink(Integer.toString(page), String.format(cmdFormat, page), "Aller à la page " + page));
|
||||||
if (page == currentPage) {
|
if (page == currentPage) {
|
||||||
d.color(ChatColor.WHITE);
|
pDisp.color(Pandacube.CHAT_COMMAND_HIGHLIGHTED_COLOR);
|
||||||
}
|
}
|
||||||
|
d.then(pDisp);
|
||||||
|
|
||||||
previous = page;
|
previous = page;
|
||||||
}
|
}
|
||||||
@ -175,33 +181,31 @@ public class DisplayUtil {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
// TODO refaire les 4 methodes ci-dessous
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static BaseComponent centerText(BaseComponent text, char repeatedChar, ChatColor decorationColor,
|
public static BaseComponent centerText(BaseComponent text, char repeatedChar, ChatColor decorationColor,
|
||||||
boolean console) {
|
boolean console) {
|
||||||
|
|
||||||
int textWidth = strWidth(text.toPlainText(), console, false);
|
int textWidth = componentWidth(text, console);
|
||||||
if (textWidth > ((console) ? CONSOLE_NB_CHAR_DEFAULT : DEFAULT_CHAT_WIDTH)) return text;
|
int maxWidth = (console) ? CONSOLE_NB_CHAR_DEFAULT : DEFAULT_CHAT_WIDTH;
|
||||||
|
|
||||||
String current = text.toPlainText();
|
if (textWidth > maxWidth)
|
||||||
int count = 0;
|
return text;
|
||||||
do {
|
|
||||||
count++;
|
|
||||||
current = repeatedChar + current + repeatedChar;
|
|
||||||
} while (strWidth(current, console, false) <= ((console) ? CONSOLE_NB_CHAR_DEFAULT : DEFAULT_CHAT_WIDTH));
|
|
||||||
count--;
|
|
||||||
|
|
||||||
String finalLeftOrRight = "";
|
int repeatedCharWidth = charW(repeatedChar, console, false);
|
||||||
|
int sideWidth = (maxWidth - textWidth) / 2;
|
||||||
|
int sideNbChar = sideWidth / repeatedCharWidth;
|
||||||
|
|
||||||
for (int i = 0; i < count; i++)
|
if (sideNbChar == 0)
|
||||||
finalLeftOrRight += repeatedChar;
|
return text;
|
||||||
|
|
||||||
Display d = new Display().next(finalLeftOrRight).color(decorationColor).next(text);
|
String sideChars = repeatedChar(repeatedChar, sideNbChar);
|
||||||
|
|
||||||
if (repeatedChar != ' ') d.next(finalLeftOrRight).color(decorationColor);
|
Chat d = Chat.chat()
|
||||||
|
.then(text(sideChars).color(decorationColor))
|
||||||
|
.then(text);
|
||||||
|
if (repeatedChar != ' ')
|
||||||
|
d.then(text(sideChars).color(decorationColor));
|
||||||
|
|
||||||
return d.get();
|
return d.get();
|
||||||
|
|
||||||
@ -210,36 +214,22 @@ public class DisplayUtil {
|
|||||||
public static BaseComponent leftText(BaseComponent text, char repeatedChar, ChatColor decorationColor, int nbLeft,
|
public static BaseComponent leftText(BaseComponent text, char repeatedChar, ChatColor decorationColor, int nbLeft,
|
||||||
boolean console) {
|
boolean console) {
|
||||||
|
|
||||||
int textWidth = strWidth(text.toPlainText(), console, false);
|
int textWidth = componentWidth(text, console);
|
||||||
if (textWidth > ((console) ? CONSOLE_NB_CHAR_DEFAULT : DEFAULT_CHAT_WIDTH) || textWidth
|
int maxWidth = (console) ? CONSOLE_NB_CHAR_DEFAULT : DEFAULT_CHAT_WIDTH;
|
||||||
+ nbLeft * charW(repeatedChar, console, false) > ((console) ? CONSOLE_NB_CHAR_DEFAULT : DEFAULT_CHAT_WIDTH))
|
int repeatedCharWidth = charW(repeatedChar, console, false);
|
||||||
|
int leftWidth = nbLeft * repeatedCharWidth;
|
||||||
|
|
||||||
|
if (textWidth + leftWidth > maxWidth)
|
||||||
return text;
|
return text;
|
||||||
|
|
||||||
Display d = new Display();
|
int rightNbChar = (maxWidth - (textWidth + leftWidth)) / repeatedCharWidth;
|
||||||
|
|
||||||
String finalLeft = "";
|
|
||||||
if (nbLeft > 0) {
|
|
||||||
for (int i = 0; i < nbLeft; i++)
|
|
||||||
finalLeft += repeatedChar;
|
|
||||||
d.next(finalLeft).color(decorationColor);
|
|
||||||
}
|
|
||||||
d.next(text);
|
|
||||||
|
|
||||||
int count = 0;
|
|
||||||
String current = finalLeft + text.toPlainText();
|
|
||||||
do {
|
|
||||||
count++;
|
|
||||||
current += repeatedChar;
|
|
||||||
} while (strWidth(current, console, false) <= ((console) ? CONSOLE_NB_CHAR_DEFAULT : DEFAULT_CHAT_WIDTH));
|
|
||||||
count--;
|
|
||||||
|
|
||||||
|
Chat d = chat()
|
||||||
|
.then(text(repeatedChar(repeatedChar, nbLeft)).color(decorationColor))
|
||||||
|
.then(text);
|
||||||
if (repeatedChar != ' ') {
|
if (repeatedChar != ' ') {
|
||||||
String finalRight = "";
|
d.then(text(repeatedChar(repeatedChar, rightNbChar)).color(decorationColor));
|
||||||
for (int i = 0; i < count; i++)
|
|
||||||
finalRight += repeatedChar;
|
|
||||||
d.next(finalRight).color(decorationColor);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return d.get();
|
return d.get();
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -247,53 +237,36 @@ public class DisplayUtil {
|
|||||||
public static BaseComponent rightText(BaseComponent text, char repeatedChar, ChatColor decorationColor, int nbRight,
|
public static BaseComponent rightText(BaseComponent text, char repeatedChar, ChatColor decorationColor, int nbRight,
|
||||||
boolean console) {
|
boolean console) {
|
||||||
|
|
||||||
int textWidth = strWidth(text.toPlainText(), console, false);
|
int textWidth = componentWidth(text, console);
|
||||||
if (textWidth > ((console) ? CONSOLE_NB_CHAR_DEFAULT : DEFAULT_CHAT_WIDTH) || textWidth
|
int maxWidth = (console) ? CONSOLE_NB_CHAR_DEFAULT : DEFAULT_CHAT_WIDTH;
|
||||||
+ nbRight * charW(repeatedChar, console, false) > ((console) ? CONSOLE_NB_CHAR_DEFAULT : DEFAULT_CHAT_WIDTH))
|
int repeatedCharWidth = charW(repeatedChar, console, false);
|
||||||
|
int rightWidth = nbRight * repeatedCharWidth;
|
||||||
|
|
||||||
|
if (textWidth + rightWidth > maxWidth)
|
||||||
return text;
|
return text;
|
||||||
|
|
||||||
String tempText = text.toPlainText();
|
int leftNbChar = (maxWidth - (textWidth + rightWidth)) / repeatedCharWidth;
|
||||||
if (nbRight > 0) {
|
|
||||||
tempText += decorationColor;
|
|
||||||
for (int i = 0; i < nbRight; i++)
|
|
||||||
tempText += repeatedChar;
|
|
||||||
}
|
|
||||||
|
|
||||||
int count = 0;
|
|
||||||
String current = tempText;
|
|
||||||
do {
|
|
||||||
count++;
|
|
||||||
current = repeatedChar + current;
|
|
||||||
} while (strWidth(current, console, false) <= ((console) ? CONSOLE_NB_CHAR_DEFAULT : DEFAULT_CHAT_WIDTH));
|
|
||||||
count--;
|
|
||||||
|
|
||||||
String finalLeft = "";
|
|
||||||
for (int i = 0; i < count; i++)
|
|
||||||
finalLeft += repeatedChar;
|
|
||||||
|
|
||||||
Display d = new Display().next(finalLeft).color(decorationColor).next(text);
|
|
||||||
|
|
||||||
|
Chat d = chat()
|
||||||
|
.then(text(repeatedChar(repeatedChar, leftNbChar)).color(decorationColor))
|
||||||
|
.then(text);
|
||||||
if (repeatedChar != ' ') {
|
if (repeatedChar != ' ') {
|
||||||
String finalRight = "";
|
d.then(text(repeatedChar(repeatedChar, nbRight)).color(decorationColor));
|
||||||
for (int i = 0; i < nbRight; i++)
|
|
||||||
finalRight += repeatedChar;
|
|
||||||
d.next(finalRight).color(decorationColor);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return d.get();
|
return d.get();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static BaseComponent emptyLine(char repeatedChar, ChatColor decorationColor, boolean console) {
|
public static BaseComponent emptyLine(char repeatedChar, ChatColor decorationColor, boolean console) {
|
||||||
int count = ((console) ? CONSOLE_NB_CHAR_DEFAULT : DEFAULT_CHAT_WIDTH) / charW(repeatedChar, console, false);
|
int count = ((console) ? CONSOLE_NB_CHAR_DEFAULT : DEFAULT_CHAT_WIDTH) / charW(repeatedChar, console, false);
|
||||||
String finalLine = "";
|
return text(repeatedChar(repeatedChar, count)).color(decorationColor).get();
|
||||||
for (int i = 0; i < count; i++)
|
|
||||||
finalLine += repeatedChar;
|
|
||||||
|
|
||||||
return new Display().next(finalLine).color(decorationColor).get();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String repeatedChar(char repeatedChar, int count) {
|
||||||
|
char[] c = new char[count];
|
||||||
|
Arrays.fill(c, repeatedChar);
|
||||||
|
return new String(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -304,13 +277,23 @@ public class DisplayUtil {
|
|||||||
|
|
||||||
|
|
||||||
public static int componentWidth(BaseComponent[] components, boolean console) {
|
public static int componentWidth(BaseComponent[] components, boolean console) {
|
||||||
return Arrays.stream(components).mapToInt(c -> componentWidth(c, console)).sum();
|
if (components == null)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
for (BaseComponent c : components)
|
||||||
|
count += componentWidth(c, console);
|
||||||
|
|
||||||
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int componentWidth(BaseComponent component, boolean console) {
|
public static int componentWidth(BaseComponent component, boolean console) {
|
||||||
|
if (component == null)
|
||||||
|
return 0;
|
||||||
|
|
||||||
int count = 0;
|
int count = 0;
|
||||||
for (BaseComponent c : component.getExtra())
|
|
||||||
count += componentWidth(c, console);
|
|
||||||
if (component instanceof TextComponent) {
|
if (component instanceof TextComponent) {
|
||||||
count += strWidth(((TextComponent)component).getText(), console, component.isBold());
|
count += strWidth(((TextComponent)component).getText(), console, component.isBold());
|
||||||
}
|
}
|
||||||
@ -318,6 +301,11 @@ public class DisplayUtil {
|
|||||||
for (BaseComponent c : ((TranslatableComponent)component).getWith())
|
for (BaseComponent c : ((TranslatableComponent)component).getWith())
|
||||||
count += componentWidth(c, console);
|
count += componentWidth(c, console);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (component.getExtra() != null) {
|
||||||
|
for (BaseComponent c : component.getExtra())
|
||||||
|
count += componentWidth(c, console);
|
||||||
|
}
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -368,14 +356,15 @@ public class DisplayUtil {
|
|||||||
if ((c >= '0' && c <= '9') // reset bold
|
if ((c >= '0' && c <= '9') // reset bold
|
||||||
|| (c >= 'a' && c <= 'f')
|
|| (c >= 'a' && c <= 'f')
|
||||||
|| (c >= 'A' && c <= 'F')
|
|| (c >= 'A' && c <= 'F')
|
||||||
|| c == 'r' || c == 'R')
|
|| c == 'r' || c == 'R'
|
||||||
|
|| c == 'x' || c == 'X')
|
||||||
bold = false;
|
bold = false;
|
||||||
|
|
||||||
}
|
}
|
||||||
else if (c == ' ') {
|
else if (c == ' ') {
|
||||||
if (currentLineSize + currentWordSize > pixelWidth && currentLineSize > 0) { // wrap before word
|
if (currentLineSize + currentWordSize > pixelWidth && currentLineSize > 0) { // wrap before word
|
||||||
lines.add(currentLine);
|
lines.add(currentLine);
|
||||||
String lastStyle = getLastColors(currentLine);
|
String lastStyle = ChatColorUtil.getLastColors(currentLine);
|
||||||
if (currentWord.charAt(0) == ' ') {
|
if (currentWord.charAt(0) == ' ') {
|
||||||
currentWord = currentWord.substring(1);
|
currentWord = currentWord.substring(1);
|
||||||
currentWordSize -= charW(' ', false, firstCharCurrentWorldBold);
|
currentWordSize -= charW(' ', false, firstCharCurrentWorldBold);
|
||||||
@ -394,7 +383,7 @@ public class DisplayUtil {
|
|||||||
else if (c == '\n') {
|
else if (c == '\n') {
|
||||||
if (currentLineSize + currentWordSize > pixelWidth && currentLineSize > 0) { // wrap before word
|
if (currentLineSize + currentWordSize > pixelWidth && currentLineSize > 0) { // wrap before word
|
||||||
lines.add(currentLine);
|
lines.add(currentLine);
|
||||||
String lastStyle = getLastColors(currentLine);
|
String lastStyle = ChatColorUtil.getLastColors(currentLine);
|
||||||
if (currentWord.charAt(0) == ' ') {
|
if (currentWord.charAt(0) == ' ') {
|
||||||
currentWord = currentWord.substring(1);
|
currentWord = currentWord.substring(1);
|
||||||
currentWordSize -= charW(' ', false, firstCharCurrentWorldBold);
|
currentWordSize -= charW(' ', false, firstCharCurrentWorldBold);
|
||||||
@ -408,7 +397,7 @@ public class DisplayUtil {
|
|||||||
}
|
}
|
||||||
// wrap after
|
// wrap after
|
||||||
lines.add(currentLine);
|
lines.add(currentLine);
|
||||||
String lastStyle = getLastColors(currentLine);
|
String lastStyle = ChatColorUtil.getLastColors(currentLine);
|
||||||
|
|
||||||
currentLine = lastStyle.equals("§r") ? "" : lastStyle;
|
currentLine = lastStyle.equals("§r") ? "" : lastStyle;
|
||||||
currentLineSize = 0;
|
currentLineSize = 0;
|
||||||
@ -441,116 +430,41 @@ public class DisplayUtil {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static String getLastColors(String legacyText) {
|
|
||||||
String result = "";
|
|
||||||
int length = legacyText.length();
|
|
||||||
|
|
||||||
// Search backwards from the end as it is faster
|
|
||||||
for (int index = length - 1; index > -1; index--) {
|
|
||||||
char section = legacyText.charAt(index);
|
|
||||||
if (section == ChatColor.COLOR_CHAR && index < length - 1) {
|
|
||||||
char c = legacyText.charAt(index + 1);
|
|
||||||
ChatColor color = getChatColorByChar(c);
|
|
||||||
|
|
||||||
if (color != null) {
|
|
||||||
result = color.toString() + result;
|
|
||||||
|
|
||||||
// Once we find a color or reset we can stop searching
|
|
||||||
char col = color.toString().charAt(1);
|
|
||||||
if ((col >= '0' && col <= '9')
|
|
||||||
|| (col >= 'a' && col <= 'f')
|
|
||||||
|| col == 'r') {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ChatColor getChatColorByChar(char code) {
|
|
||||||
return ChatColor.getByChar(Character.toLowerCase(code));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
public static String truncatePrefix(String prefix, int maxLength) {
|
||||||
|
if (prefix.length() > maxLength) {
|
||||||
|
String lastColor = ChatColorUtil.getLastColors(prefix);
|
||||||
|
prefix = truncateAtLengthWithoutReset(prefix, maxLength);
|
||||||
|
if (!ChatColorUtil.getLastColors(prefix).equals(lastColor))
|
||||||
|
prefix = truncateAtLengthWithoutReset(prefix, maxLength - lastColor.length()) + lastColor;
|
||||||
/**
|
}
|
||||||
* Force a text to be italic, while keeping other formatting and colors.
|
return prefix;
|
||||||
* 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static String truncateAtLengthWithoutReset(String prefix, int l) {
|
||||||
private static String forceFormat(String legacyText, ChatColor format) {
|
if (prefix.length() > l) {
|
||||||
return format + legacyText
|
prefix = prefix.substring(0, l);
|
||||||
.replace(format.toString(), "") // remove previous tag to make the result cleaner
|
if (prefix.endsWith("§"))
|
||||||
.replaceAll("§([a-frA-FR0-9])", "§$1" + format);
|
prefix = prefix.substring(0, prefix.length()-1);
|
||||||
|
}
|
||||||
|
return prefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
public static BaseComponent toUniqueBaseComponent(BaseComponent... baseComponents) {
|
||||||
* (can be a combination of a color tag followed by multiple format tag)
|
if (baseComponents == null || baseComponents.length == 0)
|
||||||
* @return the resulting text
|
return new TextComponent();
|
||||||
*/
|
if (baseComponents.length == 1)
|
||||||
public static String resetToColor(String legacyText, String color) {
|
return baseComponents[0];
|
||||||
return legacyText.replace(ChatColor.RESET.toString(), color);
|
return new TextComponent(baseComponents);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -560,8 +474,6 @@ public class DisplayUtil {
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generate a tree view based on the tree structure {@code node}.
|
* Generate a tree view based on the tree structure {@code node}.
|
||||||
*
|
*
|
@ -1,331 +0,0 @@
|
|||||||
package fr.pandacube.util.text_display;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import net.md_5.bungee.api.ChatColor;
|
|
||||||
import net.md_5.bungee.api.chat.BaseComponent;
|
|
||||||
import net.md_5.bungee.api.chat.ClickEvent;
|
|
||||||
import net.md_5.bungee.api.chat.HoverEvent;
|
|
||||||
import net.md_5.bungee.api.chat.TextComponent;
|
|
||||||
|
|
||||||
public class Display {
|
|
||||||
|
|
||||||
private BaseComponent root = new TextComponent("");
|
|
||||||
|
|
||||||
private BaseComponent current = null;
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* ****************
|
|
||||||
* * Constructors *
|
|
||||||
* ****************
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new instance. The current component is not initialized.
|
|
||||||
*/
|
|
||||||
public Display() {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new instance, with the current component already initialized with the parameter.
|
|
||||||
* @param legacyText a text that will be converted to a component and set to the current compoment.
|
|
||||||
*/
|
|
||||||
public Display(String legacyText) {
|
|
||||||
next(legacyText);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new instance, with the current component already initialized with the parameter.
|
|
||||||
* @param legacyText a list of text that will be joined by a line return followed by ChatColor.RESET,
|
|
||||||
* then converted to a component and set to the current component.
|
|
||||||
*/
|
|
||||||
public Display(List<String> legacyText) {
|
|
||||||
this(String.join("\n"+ChatColor.RESET, legacyText));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new instance, with the current component already initialized with the parameter.
|
|
||||||
* @param legacyText an array of text that will be joined by a line return followed by ChatColor.RESET,
|
|
||||||
* then converted to a component and set to the current component.
|
|
||||||
*/
|
|
||||||
public Display(String[] legacyText) {
|
|
||||||
this(Arrays.asList(legacyText));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new instance, with the current component already initialized with the parameter.
|
|
||||||
* @param firstComponent a component corresponding to the current component.
|
|
||||||
*/
|
|
||||||
public Display(BaseComponent firstComponent) {
|
|
||||||
next(firstComponent);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new instance, with the current component already initialized with the parameter.
|
|
||||||
* @param components an array of component that will be inside the current component.
|
|
||||||
*/
|
|
||||||
public Display(BaseComponent[] components) {
|
|
||||||
if (components == null) throw new IllegalArgumentException("le paramètre ne doit pas être null");
|
|
||||||
next(components);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* ******************
|
|
||||||
* * next() methods *
|
|
||||||
* ******************
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize the current component with the parameter.
|
|
||||||
* The previous component is stored in the root component.
|
|
||||||
* @param cmp a component corresponding to the new component.
|
|
||||||
* @return this
|
|
||||||
*/
|
|
||||||
public Display next(BaseComponent cmp) {
|
|
||||||
if (cmp == null) throw new IllegalArgumentException("le paramètre ne doit pas être null");
|
|
||||||
finalizeCurrentComponent();
|
|
||||||
current = cmp;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize the current component with the parameter.
|
|
||||||
* The previous component is stored in the root component.
|
|
||||||
* @param str a text that will be converted to a component and set to the current compoment.
|
|
||||||
* @return this
|
|
||||||
*/
|
|
||||||
public Display next(String str) {
|
|
||||||
return next(TextComponent.fromLegacyText(str == null ? "" : str));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize the current component with the parameter.
|
|
||||||
* The previous component is stored in the root component.
|
|
||||||
* @param components an array of component that will be inside the current component.
|
|
||||||
* @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);
|
|
||||||
return next(bc);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize the current component with the parameter.
|
|
||||||
* The previous component is stored in the root component.
|
|
||||||
* @param cmp an other instance of Display that the root component become the current component of this instance.
|
|
||||||
* @return this
|
|
||||||
*/
|
|
||||||
public Display next(Display cmp) {
|
|
||||||
if (cmp == null) throw new IllegalArgumentException("le paramètre ne doit pas être null");
|
|
||||||
return next(cmp.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize the current component with the text "\n".
|
|
||||||
* The previous component is stored in the root component.
|
|
||||||
* @return this
|
|
||||||
*/
|
|
||||||
public Display nextLine() {
|
|
||||||
finalizeCurrentComponent();
|
|
||||||
current = new TextComponent("\n");
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* **************************
|
|
||||||
* * Style and behaviour of *
|
|
||||||
* *** current component ****
|
|
||||||
* **************************
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the color of the current component.
|
|
||||||
* @param color the colour. Can be null;
|
|
||||||
* @return this
|
|
||||||
*/
|
|
||||||
public Display color(ChatColor color) {
|
|
||||||
current.setColor(color);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set if the current component is bold.
|
|
||||||
* @param b true if bold, false if not, null if undefined
|
|
||||||
* @return this
|
|
||||||
*/
|
|
||||||
public Display bold(Boolean b) {
|
|
||||||
current.setBold(b);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set if the current component is italic.
|
|
||||||
* @param b true if italic, false if not, null if undefined
|
|
||||||
* @return this
|
|
||||||
*/
|
|
||||||
public Display italic(Boolean i) {
|
|
||||||
current.setItalic(i);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set if the current component is underlined.
|
|
||||||
* @param b true if underlined, false if not, null if undefined
|
|
||||||
* @return this
|
|
||||||
*/
|
|
||||||
public Display underlined(Boolean u) {
|
|
||||||
current.setUnderlined(u);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set if the current component is obfuscated.
|
|
||||||
* In Minecraft user interface, obfuscated text displays randomly generated character in place of the originals. The random text regenerate each frame.
|
|
||||||
* @param b true if obfuscated, false if not, null if undefined
|
|
||||||
* @return this
|
|
||||||
*/
|
|
||||||
public Display obfuscated(Boolean o) {
|
|
||||||
current.setObfuscated(o);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set if the current component is strikethrough.
|
|
||||||
* @param b true if strikethrough, false if not, null if undefined
|
|
||||||
* @return this
|
|
||||||
*/
|
|
||||||
public Display strikethrough(Boolean s) {
|
|
||||||
current.setStrikethrough(s);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set a text displayed as a tooltip when the cursor is hover the current component.
|
|
||||||
* This method is only relevant if this Display is intended to be displayed in the chat or in a book
|
|
||||||
* @param content the text as an array of component.
|
|
||||||
* @return this
|
|
||||||
*/
|
|
||||||
public Display hoverText(BaseComponent[] content) {
|
|
||||||
current.setHoverEvent(new HoverEvent(HoverEvent.Action.SHOW_TEXT, content));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set a text displayed as a tooltip when the cursor is hover the current component.
|
|
||||||
* This method is only relevant if this Display is intended to be displayed in the chat or in a book
|
|
||||||
* @param content the text as a component
|
|
||||||
* @return this
|
|
||||||
*/
|
|
||||||
public Display hoverText(BaseComponent content) {
|
|
||||||
return hoverText(new BaseComponent[] {content});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set a text displayed as a tooltip when the cursor is hover the current component.
|
|
||||||
* This method is only relevant if this Display is intended to be displayed in the chat or in a book
|
|
||||||
* @param content the text as a legacy string.
|
|
||||||
* @return this
|
|
||||||
*/
|
|
||||||
public Display hoverText(String legacyContent) {
|
|
||||||
return hoverText(TextComponent.fromLegacyText(legacyContent));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set a text displayed as a tooltip when the cursor is hover the current component.
|
|
||||||
* This method is only relevant if this Display is intended to be displayed in the chat or in a book
|
|
||||||
* @param content the text as a {@link Display} instance.
|
|
||||||
* @return this
|
|
||||||
*/
|
|
||||||
public Display hoverText(Display content) {
|
|
||||||
return hoverText(content.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allow the player to click on the current component to access to the specified URL.
|
|
||||||
* This method is only relevant if this Display is intended to be displayed in the chat
|
|
||||||
* @param url the URL
|
|
||||||
* @return this
|
|
||||||
*/
|
|
||||||
public Display clickURL(String url) {
|
|
||||||
current.setClickEvent(new ClickEvent(ClickEvent.Action.OPEN_URL, url));
|
|
||||||
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.
|
|
||||||
* On the sign, all the commands are executed in a row when the player click on the sign.
|
|
||||||
* @param cmd the command, with the "/"
|
|
||||||
* @return this
|
|
||||||
*/
|
|
||||||
public Display clickCommand(String cmd) {
|
|
||||||
current.setClickEvent(new ClickEvent(ClickEvent.Action.RUN_COMMAND, cmd));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allow the player to click on the current component to fill the textfield with the specified command.
|
|
||||||
* This method is only relevant if this Display is intended to be displayed in the chat.
|
|
||||||
* @param cmd the command
|
|
||||||
* @return this
|
|
||||||
*/
|
|
||||||
public Display clickSuggest(String cmd) {
|
|
||||||
current.setClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, cmd));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allow the player to shuft-click on the current component to insert the specified string into the textfield (at the cursor location).
|
|
||||||
* This method is only relevant if this Display is intended to be displayed in the chat.
|
|
||||||
* @param str the string
|
|
||||||
* @return this
|
|
||||||
*/
|
|
||||||
public Display clickInsertion(String str) {
|
|
||||||
current.setInsertion(str);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void finalizeCurrentComponent() {
|
|
||||||
if (current != null) root.addExtra(current);
|
|
||||||
current = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add the current compoment into the root component and return the root component.
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public BaseComponent get() {
|
|
||||||
finalizeCurrentComponent();
|
|
||||||
if (!root.hasFormatting() && root.getExtra() != null && root.getExtra().size() == 1)
|
|
||||||
return root.getExtra().get(0);
|
|
||||||
return root;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add the current compoment into the root component and return all the components in an array.
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public BaseComponent[] getArray() {
|
|
||||||
finalizeCurrentComponent();
|
|
||||||
return root.getExtra().toArray(new BaseComponent[root.getExtra().size()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -6,7 +6,6 @@ public class TextProgressBar {
|
|||||||
private static String pattern_start = "[";
|
private static String pattern_start = "[";
|
||||||
private static String pattern_end = "]";
|
private static String pattern_end = "]";
|
||||||
private static ChatColor color_empty = ChatColor.DARK_GRAY;
|
private static ChatColor color_empty = ChatColor.DARK_GRAY;
|
||||||
private static ChatColor color_decoration = ChatColor.GOLD;
|
|
||||||
private static ChatColor color_default = ChatColor.RESET;
|
private static ChatColor color_default = ChatColor.RESET;
|
||||||
private static String pattern_empty = ".";
|
private static String pattern_empty = ".";
|
||||||
private static String pattern_full = "|";
|
private static String pattern_full = "|";
|
||||||
@ -35,7 +34,7 @@ public class TextProgressBar {
|
|||||||
}
|
}
|
||||||
int sum_sizes = 0;
|
int sum_sizes = 0;
|
||||||
|
|
||||||
String bar = color_decoration + pattern_start;
|
String bar = pattern_start;
|
||||||
for (int i = 0; i < sizes.length; i++) {
|
for (int i = 0; i < sizes.length; i++) {
|
||||||
sum_sizes += sizes[i];
|
sum_sizes += sizes[i];
|
||||||
|
|
||||||
@ -52,7 +51,7 @@ public class TextProgressBar {
|
|||||||
for (int j = 0; j < (max_size - sum_sizes); j++)
|
for (int j = 0; j < (max_size - sum_sizes); j++)
|
||||||
bar = bar + pattern_empty;
|
bar = bar + pattern_empty;
|
||||||
|
|
||||||
bar = bar + color_decoration + pattern_end;
|
bar = bar + ChatColor.RESET + pattern_end;
|
||||||
return bar;
|
return bar;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user