Skip to content

Commit

Permalink
text-minimessage: Add Formatter as Placeholder to format numbers and …
Browse files Browse the repository at this point in the history
…dates
  • Loading branch information
Joo200 committed Apr 26, 2022
1 parent 57d5319 commit 1064dc3
Show file tree
Hide file tree
Showing 3 changed files with 220 additions and 0 deletions.
Expand Up @@ -25,6 +25,7 @@

import java.util.Arrays;
import java.util.Locale;
import java.util.Optional;
import java.util.OptionalDouble;
import java.util.OptionalInt;
import java.util.function.Consumer;
Expand Down Expand Up @@ -216,5 +217,21 @@ default boolean isFalse() {
return OptionalDouble.empty();
}
}

/**
* Try to return this argument as a {@code char}.
*
* <p>The optional will only be present if the value is a valid char.</p>
*
* @return an optional providing the value of this argument as an integer
* @since 4.10.0
*/
default @NotNull Optional<Character> asChar() {
if (this.value().length() == 1) {
return Optional.of(this.value().charAt(0));
} else {
return Optional.empty();
}
}
}
}
@@ -0,0 +1,100 @@
/*
* 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.text.NumberFormat;
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.
*
* <p>These are effectively placeholders.</p>
*
* @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.
*
* <p>This tag expects a format as attribute. Refer to {@link DecimalFormat} for usable patterns.</p>
*
* <p>You can specify the decimal and grouping separator by adding those as another attribute.</p>
*
* <p>This replacement is auto-closing, so its style will not influence the style of following components.</p>
*
* @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 NumberFormat decimalFormat;
if (argumentQueue.hasNext()) {
final String locale = argumentQueue.pop().value();
if (argumentQueue.hasNext()) {
final String format = argumentQueue.pop().value();
decimalFormat = new DecimalFormat(format, new DecimalFormatSymbols(Locale.forLanguageTag(locale)));
} else {
if (locale.contains(".")) {
decimalFormat = new DecimalFormat(locale, DecimalFormatSymbols.getInstance());
} else {
decimalFormat = DecimalFormat.getInstance(Locale.forLanguageTag(locale));
}
}
} else {
decimalFormat = DecimalFormat.getInstance();
}
return Tag.inserting(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.
*
* <p>This tag expects a format as attribute. Refer to {@link DateTimeFormatter} for usable patterns.</p>
*
* <p>This replacement is auto-closing, so its style will not influence the style of following components.</p>
*
* @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.inserting(context.deserialize(DateTimeFormatter.ofPattern(format).format(time)));
});
}
}
@@ -0,0 +1,103 @@
/*
* 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 = "<mynumber:'en-EN':'#.00'> is a nice number";
final Component expected = text("20.00 is a nice number");

this.assertParsedEquals(
expected,
input,
formatNumber("mynumber", 20d)
);
}

@Test
void testNumberFormatterLang() {
final String input = "<mynumber:'de-DE':'#.00'> is a nice number";
final Component expected = text("20,00 is a nice number");

this.assertParsedEquals(
expected,
input,
formatNumber("mynumber", 20d)
);
}

@Test
void testNumberFormatterWithDecimal() {
final String input = "<double:'de-DE':'#,##0.00'> is a nice number";
final Component expected = text("2.000,00 is a nice number");

this.assertParsedEquals(
expected,
input,
formatNumber("double", 2000d)
);
}

@Test
void testNumberFormatterNegative() {
final String input = "<double:'en-EN':'<green>#.00;<red>-#.00'><blue> is a nice number";
final Component expectedNegative = text("-5.00", NamedTextColor.RED).append(text(" is a nice number", NamedTextColor.BLUE));
final Component expectedPositive = text("5.00", NamedTextColor.GREEN).append(text(" is a nice number", NamedTextColor.BLUE));

this.assertParsedEquals(
expectedNegative,
input,
formatNumber("double", -5)
);
this.assertParsedEquals(
expectedPositive,
input,
formatNumber("double", 5)
);
}

@Test
void testDateFormatter() {
final String input = "<date:'yyyy-MM-dd HH:mm:ss'> is a date";
final Component expected = text("2022-02-26 21:00:00 is a date");

this.assertParsedEquals(
expected,
input,
formatDate("date", LocalDateTime.of(2022, Month.FEBRUARY, 26, 21, 0, 0))
);
}
}

0 comments on commit 1064dc3

Please sign in to comment.