diff --git a/text-minimessage/src/main/java/net/kyori/adventure/text/minimessage/tag/resolver/Formatter.java b/text-minimessage/src/main/java/net/kyori/adventure/text/minimessage/tag/resolver/Formatter.java new file mode 100644 index 000000000..7ea83bf68 --- /dev/null +++ b/text-minimessage/src/main/java/net/kyori/adventure/text/minimessage/tag/resolver/Formatter.java @@ -0,0 +1,96 @@ +/* + * This file is part of adventure, licensed under the MIT License. + * + * Copyright (c) 2017-2022 KyoriPowered + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package net.kyori.adventure.text.minimessage.tag.resolver; + +import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; +import java.time.format.DateTimeFormatter; +import java.time.temporal.TemporalAccessor; +import java.util.Locale; +import net.kyori.adventure.text.minimessage.tag.Tag; +import org.jetbrains.annotations.NotNull; + +/** + * Tag resolvers producing tags that insert formatted values. + * + *
These are effectively placeholders.
+ * + * @since 4.10.0 + */ +public final class Formatter { + private Formatter() { + } + + /** + * Creates a replacement that inserts a number as a component. The component will be formatted by the provided DecimalFormat. + * + *This tag expects a format as attribute. Refer to {@link DecimalFormat} for usable patterns.
+ * + *You can specify the decimal and grouping separator by adding those as another attribute.
+ * + *This replacement is auto-closing, so its style will not influence the style of following components.
+ * + * @param key the key + * @param number the number + * @return the placeholder + * @since 4.10.0 + */ + public static TagResolver formatNumber(final @NotNull String key, final Number number) { + return TagResolver.resolver(key, (argumentQueue, context) -> { + final String format = argumentQueue.popOr("Format expected").value(); + final DecimalFormat decimalFormat = new DecimalFormat(format); + final DecimalFormatSymbols formatSymbols = new DecimalFormatSymbols(Locale.ROOT); + if (argumentQueue.hasNext()) { + final String symbols = argumentQueue.pop().value(); + if (symbols.length() >= 1) { + formatSymbols.setDecimalSeparator(symbols.charAt(0)); + } + if (symbols.length() >= 2) { + formatSymbols.setGroupingSeparator(symbols.charAt(1)); + } + } + decimalFormat.setDecimalFormatSymbols(formatSymbols); + return Tag.selfClosingInserting(context.deserialize(decimalFormat.format(number))); + }); + } + + /** + * Creates a replacement that inserts a date or a time as a component. The component will be formatted by the provided Date Format. + * + *This tag expects a format as attribute. Refer to {@link DateTimeFormatter} for usable patterns.
+ * + *This replacement is auto-closing, so its style will not influence the style of following components.
+ * + * @param key the key + * @param time the time + * @return the placeholder + * @since 4.10.0 + */ + public static TagResolver formatDate(final @NotNull String key, final TemporalAccessor time) { + return TagResolver.resolver(key, (argumentQueue, context) -> { + final String format = argumentQueue.popOr("Format expected.").value(); + return Tag.selfClosingInserting(context.deserialize(DateTimeFormatter.ofPattern(format).format(time))); + }); + } +} diff --git a/text-minimessage/src/main/java/net/kyori/adventure/text/minimessage/tag/resolver/Placeholder.java b/text-minimessage/src/main/java/net/kyori/adventure/text/minimessage/tag/resolver/Placeholder.java index 1cf93eaf2..6ba2444ab 100644 --- a/text-minimessage/src/main/java/net/kyori/adventure/text/minimessage/tag/resolver/Placeholder.java +++ b/text-minimessage/src/main/java/net/kyori/adventure/text/minimessage/tag/resolver/Placeholder.java @@ -23,11 +23,6 @@ */ package net.kyori.adventure.text.minimessage.tag.resolver; -import java.text.DecimalFormat; -import java.text.DecimalFormatSymbols; -import java.time.format.DateTimeFormatter; -import java.time.temporal.TemporalAccessor; -import java.util.Locale; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.ComponentLike; import net.kyori.adventure.text.minimessage.tag.Tag; @@ -86,42 +81,4 @@ private Placeholder() { public static TagResolver.@NotNull Single component(final @NotNull String key, final @NotNull ComponentLike value) { return TagResolver.resolver(key, Tag.selfClosingInserting(value)); } - - /** - * Creates a replacement that inserts a number as a component. The component will be formatted by the provided DecimalFormat. - * - *Refer to {@link DecimalFormat} for usable patterns.
- * - *This replacement is auto-closing, so its style will not influence the style of following components.
- * - * @param key the key - * @param number the number - * @return the placeholder - * @since 4.10.0 - */ - public static TagResolver formatNumber(final @NotNull String key, final Number number) { - return TagResolver.resolver(key, (argumentQueue, context) -> { - final String format = argumentQueue.popOr("Format expected.").value(); - return Tag.selfClosingInserting(Component.text(new DecimalFormat(format, new DecimalFormatSymbols(Locale.ROOT)).format(number))); - }); - } - - /** - * Creates a replacement that inserts a date or a time as a component. The component will be formatted by the provided Date Format. - * - *Refer to {@link DateTimeFormatter} for usable patterns.
- * - *This replacement is auto-closing, so its style will not influence the style of following components.
- * - * @param key the key - * @param time the time - * @return the placeholder - * @since 4.10.0 - */ - public static TagResolver formatDate(final @NotNull String key, final TemporalAccessor time) { - return TagResolver.resolver(key, (argumentQueue, context) -> { - final String format = argumentQueue.popOr("Format expected.").value(); - return Tag.selfClosingInserting(Component.text(DateTimeFormatter.ofPattern(format).format(time))); - }); - } } diff --git a/text-minimessage/src/test/java/net/kyori/adventure/text/minimessage/tag/FormatterTest.java b/text-minimessage/src/test/java/net/kyori/adventure/text/minimessage/tag/FormatterTest.java new file mode 100644 index 000000000..efb62f9b1 --- /dev/null +++ b/text-minimessage/src/test/java/net/kyori/adventure/text/minimessage/tag/FormatterTest.java @@ -0,0 +1,91 @@ +/* + * This file is part of adventure, licensed under the MIT License. + * + * Copyright (c) 2017-2022 KyoriPowered + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package net.kyori.adventure.text.minimessage.tag; + +import java.time.LocalDateTime; +import java.time.Month; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.minimessage.AbstractTest; +import org.junit.jupiter.api.Test; + +import static net.kyori.adventure.text.Component.text; +import static net.kyori.adventure.text.minimessage.tag.resolver.Formatter.formatDate; +import static net.kyori.adventure.text.minimessage.tag.resolver.Formatter.formatNumber; + +public class FormatterTest extends AbstractTest { + @Test + void testNumberFormatter() { + final String input = "