From 28dc9b9e55262d4c89f8a77f1db7e9cffe4beede Mon Sep 17 00:00:00 2001 From: carm Date: Sun, 16 Feb 2025 23:46:31 +0800 Subject: [PATCH] feat(text): Implement the ConfiguredText for simply text/msg functions. --- features/text/pom.xml | 60 ++++ .../value/text/ConfiguredText.java | 173 ++++++++++ .../value/text/PreparedText.java | 17 + .../value/text/data/TextContents.java | 160 ++++++++++ .../value/text/function/TextCompiler.java | 50 +++ .../value/text/function/TextDispatcher.java | 65 ++++ .../value/text/function/TextParser.java | 298 ++++++++++++++++++ .../value/text/tests/ConfigTest.java | 47 +++ .../value/text/tests/ParseTest.java | 49 +++ .../value/text/tests/conf/AppMessages.java | 28 ++ .../value/text/tests/conf/ConfiguredMsg.java | 47 +++ pom.xml | 1 + 12 files changed, 995 insertions(+) create mode 100644 features/text/pom.xml create mode 100644 features/text/src/main/java/cc/carm/lib/configuration/value/text/ConfiguredText.java create mode 100644 features/text/src/main/java/cc/carm/lib/configuration/value/text/PreparedText.java create mode 100644 features/text/src/main/java/cc/carm/lib/configuration/value/text/data/TextContents.java create mode 100644 features/text/src/main/java/cc/carm/lib/configuration/value/text/function/TextCompiler.java create mode 100644 features/text/src/main/java/cc/carm/lib/configuration/value/text/function/TextDispatcher.java create mode 100644 features/text/src/main/java/cc/carm/lib/configuration/value/text/function/TextParser.java create mode 100644 features/text/src/test/java/cc/carm/lib/configuration/value/text/tests/ConfigTest.java create mode 100644 features/text/src/test/java/cc/carm/lib/configuration/value/text/tests/ParseTest.java create mode 100644 features/text/src/test/java/cc/carm/lib/configuration/value/text/tests/conf/AppMessages.java create mode 100644 features/text/src/test/java/cc/carm/lib/configuration/value/text/tests/conf/ConfiguredMsg.java diff --git a/features/text/pom.xml b/features/text/pom.xml new file mode 100644 index 0000000..f5ca255 --- /dev/null +++ b/features/text/pom.xml @@ -0,0 +1,60 @@ + + + 4.0.0 + + cc.carm.lib + easyconfiguration-parent + 4.0.0 + ../../pom.xml + + + ${project.jdk.version} + ${project.jdk.version} + UTF-8 + UTF-8 + + + easyconfiguration-feature-text + jar + + + + + ${project.groupId} + easyconfiguration-core + ${project.version} + + + + ${project.groupId} + easyconfiguration-yaml + ${project.version} + test + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + org.apache.maven.plugins + maven-jar-plugin + + + org.apache.maven.plugins + maven-source-plugin + + + org.apache.maven.plugins + maven-javadoc-plugin + + + + + \ No newline at end of file diff --git a/features/text/src/main/java/cc/carm/lib/configuration/value/text/ConfiguredText.java b/features/text/src/main/java/cc/carm/lib/configuration/value/text/ConfiguredText.java new file mode 100644 index 0000000..11a2e3b --- /dev/null +++ b/features/text/src/main/java/cc/carm/lib/configuration/value/text/ConfiguredText.java @@ -0,0 +1,173 @@ +package cc.carm.lib.configuration.value.text; + +import cc.carm.lib.configuration.adapter.ValueAdapter; +import cc.carm.lib.configuration.adapter.ValueType; +import cc.carm.lib.configuration.builder.AbstractConfigBuilder; +import cc.carm.lib.configuration.source.ConfigurationHolder; +import cc.carm.lib.configuration.value.ValueManifest; +import cc.carm.lib.configuration.value.standard.ConfiguredValue; +import cc.carm.lib.configuration.value.text.data.TextContents; +import cc.carm.lib.configuration.value.text.function.TextDispatcher; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Consumer; + +/** + * @param The type of the message + * @param The type of the receiver + */ +public class ConfiguredText extends ConfiguredValue { + + public static Builder builder() { + return new StardardBuilder<>(); + } + + public static final ValueType TEXT_TYPE = ValueType.of(TextContents.class); + public static final ValueAdapter TEXT_ADAPTER = new ValueAdapter<>(TEXT_TYPE, + (h, t, d) -> d.serialize(), + (h, t, d) -> TextContents.deserialize(d) + ); + + protected final @NotNull BiFunction parser; + protected final @NotNull BiFunction compiler; + protected final @NotNull BiConsumer> dispatcher; + + protected final @NotNull String[] params; // The parameters of the message. + + public ConfiguredText(@NotNull ValueManifest manifest, + @NotNull BiFunction parser, + @NotNull BiFunction compiler, + @NotNull BiConsumer> dispatcher, + @NotNull String[] params) { + super(manifest, TEXT_ADAPTER); + this.parser = parser; + this.compiler = compiler; + this.dispatcher = dispatcher; + this.params = params; + } + + public TextDispatcher prepare(@NotNull Object... values) { + return new PreparedText(resolve(), this.params) + .parser(this.parser).compiler(this.compiler) + .dispatcher(this.dispatcher).placeholders(values); + } + + /** + * Compile the message for the receiver. + * + * @param receiver The receiver of the message. + * @param values The values to replace the placeholders. + * @return The compiled message. + */ + public List compile(@NotNull RECEIVER receiver, @NotNull Object... values) { + return prepare(values).compile(receiver); + } + + /** + * Compile the message for the receiver and send it. + * + * @param receiver The receiver of the message. + * @param values The values to replace the placeholders. + * @return The compiled message. + */ + public MSG compileLine(@NotNull RECEIVER receiver, @NotNull Object... values) { + return prepare(values).compileLine(receiver); + } + + /** + * Send the message to the receiver. + * + * @param receiver The receiver of the message. + * @param values The values to replace the placeholders. + */ + public void sendTo(@NotNull RECEIVER receiver, @NotNull Object... values) { + prepare(values).to(receiver); + } + + /** + * Send the message to the multiple receivers. + * + * @param receivers The receivers of the message. + * @param values The values to replace the placeholders. + */ + public void sendToAll(@NotNull Iterable receivers, @NotNull Object... values) { + prepare(values).to(receivers); + } + + public abstract static class Builder> + extends AbstractConfigBuilder, ConfigurationHolder, SELF> { + protected @NotNull TextContents.Builder defaultBuilder = TextContents.builder(); + protected @NotNull String[] params = new String[0]; + + protected @NotNull BiFunction parser = (r, s) -> s; + protected @NotNull BiFunction compiler = (r, s) -> { + throw new IllegalStateException("Compiler not supplied."); + }; + protected @NotNull BiConsumer> dispatcher = (r, l) -> { + throw new IllegalStateException("Dispatcher not supplied."); + }; + + protected Builder() { + super(ConfigurationHolder.class, TEXT_TYPE); + defaults(() -> defaultBuilder.build()); // Set the default value from the default builder. + } + + public @NotNull SELF parser(@NotNull BiFunction parser) { + this.parser = parser; + return self(); + } + + public @NotNull SELF compiler(@NotNull BiFunction compiler) { + this.compiler = compiler; + return self(); + } + + public @NotNull SELF dispatcher(@NotNull BiConsumer> dispatcher) { + this.dispatcher = dispatcher; + return self(); + } + + public @NotNull SELF params(@NotNull String... params) { + this.params = params; + return self(); + } + + public @NotNull SELF defaults(@NotNull Consumer consumer) { + consumer.accept(this.defaultBuilder); + return self(); + } + + public @NotNull SELF defaults(@NotNull String... contents) { + return defaults(builder -> builder.set(contents)); + } + + public @NotNull SELF defaults(@NotNull Iterable contents) { + return defaults(builder -> builder.set(contents)); + } + + public @NotNull SELF optional(@NotNull String id, @NotNull String... lines) { + return defaults(builder -> builder.optional(id, lines)); + } + + public @NotNull SELF optional(@NotNull String id, @NotNull Iterable lines) { + return defaults(builder -> builder.optional(id, lines)); + } + + @Override + public @NotNull ConfiguredText build() { + return new ConfiguredText<>(buildManifest(), this.parser, this.compiler, this.dispatcher, this.params); + } + + } + + public static class StardardBuilder extends Builder> { + @Override + protected @NotNull StardardBuilder self() { + return this; + } + } + +} diff --git a/features/text/src/main/java/cc/carm/lib/configuration/value/text/PreparedText.java b/features/text/src/main/java/cc/carm/lib/configuration/value/text/PreparedText.java new file mode 100644 index 0000000..15b7e69 --- /dev/null +++ b/features/text/src/main/java/cc/carm/lib/configuration/value/text/PreparedText.java @@ -0,0 +1,17 @@ +package cc.carm.lib.configuration.value.text; + +import cc.carm.lib.configuration.value.text.data.TextContents; +import cc.carm.lib.configuration.value.text.function.TextDispatcher; +import org.jetbrains.annotations.NotNull; + +public class PreparedText extends TextDispatcher> { + + public PreparedText(@NotNull TextContents texts, @NotNull String... params) { + super(texts, params); + } + + @Override + public PreparedText self() { + return this; + } +} diff --git a/features/text/src/main/java/cc/carm/lib/configuration/value/text/data/TextContents.java b/features/text/src/main/java/cc/carm/lib/configuration/value/text/data/TextContents.java new file mode 100644 index 0000000..ca3af0f --- /dev/null +++ b/features/text/src/main/java/cc/carm/lib/configuration/value/text/data/TextContents.java @@ -0,0 +1,160 @@ +package cc.carm.lib.configuration.value.text.data; + +import cc.carm.lib.configuration.source.section.ConfigureSection; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; + +public class TextContents { + + public static Builder builder() { + return new Builder(); + } + + public static TextContents of(@NotNull List lines, @NotNull Map> optional) { + return new TextContents(lines, optional); + } + + protected final @NotNull List lines; + protected final @NotNull Map> optional; + + public TextContents(@NotNull List lines, @NotNull Map> optional) { + this.lines = lines; + this.optional = optional; + } + + public boolean isEmpty() { + return lines.isEmpty() || lines.stream().allMatch(String::isEmpty); + } + + public @NotNull List lines() { + return lines; + } + + public @NotNull Map> optionalLines() { + return optional; + } + + public @Nullable Object serialize() { + if (optional.isEmpty()) { + if (lines.isEmpty()) return null; + else if (lines.size() == 1) return lines.get(0); + else return lines; + } else { + Map map = new LinkedHashMap<>(); + map.put("contents", lines); + map.put("optional", optional); + return map; + } + } + + public static @Nullable TextContents deserialize(@NotNull Object data) { + Builder builder = builder(); + if (data instanceof String) { + return builder.set((String) data).build(); + } else if (data instanceof List) { + ((List) data).stream().map(Object::toString).forEach(builder::add); + return builder.build(); + } else if (data instanceof ConfigureSection) { + ConfigureSection section = (ConfigureSection) data; + builder.set(section.getStringList("contents")); + + ConfigureSection optionalSection = section.getSection("optional"); + if (optionalSection != null) { + for (String key : optionalSection.getKeys(false)) { + builder.optional(key, optionalSection.getStringList(key)); + } + } + return builder.build(); + } + return null; + } + + public static class Builder { + + protected List lines = new ArrayList<>(); + protected Map> optional = new HashMap<>(); + + /** + * Add lines to the contents + * + * @param lines lines to add + * @return this builder + */ + public Builder add(@NotNull String... lines) { + this.lines.addAll(Arrays.asList(lines)); + return this; + } + + /** + * Add lines to the contents + * + * @param lines lines to add + * @return this builder + */ + public Builder add(@NotNull Iterable lines) { + lines.forEach(this.lines::add); + return this; + } + + /** + * Set the lines of the contents + * + * @param lines lines to set + * @return this builder + */ + public Builder set(@NotNull String... lines) { + this.lines = Arrays.asList(lines); + return this; + } + + /** + * Set the lines of the contents + * + * @param lines lines to set + * @return this builder + */ + public Builder set(@NotNull Iterable lines) { + this.lines = new ArrayList<>(); + lines.forEach(this.lines::add); + return this; + } + + /** + * Add optional lines to the contents + * + * @param key key of the optional lines + * @param lines lines to add + * @return this builder + */ + public Builder optional(@NotNull String key, @NotNull String... lines) { + optional.put(key, Arrays.asList(lines)); + return this; + } + + /** + * Add optional lines to the contents + * + * @param key key of the optional lines + * @param lines lines to add + * @return this builder + */ + public Builder optional(@NotNull String key, @NotNull Iterable lines) { + List list = new ArrayList<>(); + lines.forEach(list::add); + optional.put(key, list); + return this; + } + + /** + * @return The built TextContents + */ + public TextContents build() { + return of(lines, optional); + } + + } + + +} diff --git a/features/text/src/main/java/cc/carm/lib/configuration/value/text/function/TextCompiler.java b/features/text/src/main/java/cc/carm/lib/configuration/value/text/function/TextCompiler.java new file mode 100644 index 0000000..549ad80 --- /dev/null +++ b/features/text/src/main/java/cc/carm/lib/configuration/value/text/function/TextCompiler.java @@ -0,0 +1,50 @@ +package cc.carm.lib.configuration.value.text.function; + +import cc.carm.lib.configuration.value.text.data.TextContents; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.function.BiFunction; + +public abstract class TextCompiler> extends TextParser { + + protected BiFunction compiler = (receiver, value) -> null; + + protected TextCompiler(@NotNull TextContents texts, @NotNull String... params) { + super(texts, params); + } + + /** + * Set the text compiler. + * + * @param compiler The text compiler. + * @return The current {@link TextCompiler} instance. + */ + public SELF compiler(@NotNull BiFunction compiler) { + this.compiler = compiler; + return self(); + } + + /** + * Compile the text for specific receiver. + * + * @param receiver The receiver. + * @return The compiled text. + * @see #parse(Object, BiFunction) + */ + public @NotNull List compile(@Nullable RECEIVER receiver) { + return parse(receiver, this.compiler); + } + + /** + * Compile the singleton text for specific receiver. + * + * @param receiver The receiver. + * @return The compiled text. + * @see #parseLine(Object, BiFunction) + */ + public @Nullable MSG compileLine(@Nullable RECEIVER receiver) { + return parseLine(receiver, this.compiler); + } +} diff --git a/features/text/src/main/java/cc/carm/lib/configuration/value/text/function/TextDispatcher.java b/features/text/src/main/java/cc/carm/lib/configuration/value/text/function/TextDispatcher.java new file mode 100644 index 0000000..9e15b6b --- /dev/null +++ b/features/text/src/main/java/cc/carm/lib/configuration/value/text/function/TextDispatcher.java @@ -0,0 +1,65 @@ +package cc.carm.lib.configuration.value.text.function; + +import cc.carm.lib.configuration.value.text.data.TextContents; +import org.jetbrains.annotations.NotNull; + +import java.util.Arrays; +import java.util.List; +import java.util.function.BiConsumer; +import java.util.function.Supplier; + +public abstract class TextDispatcher> extends TextCompiler { + + protected @NotNull BiConsumer> dispatcher = (receiver, msg) -> { + }; + + protected TextDispatcher(@NotNull TextContents texts, @NotNull String... params) { + super(texts, params); + } + + /** + * Set the dispatcher to send the message to the receiver + * + * @param dispatcher the dispatcher + * @return {@link TextDispatcher} + */ + public SELF dispatcher(@NotNull BiConsumer> dispatcher) { + this.dispatcher = dispatcher; + return self(); + } + + + /** + * Dispatch the message to the receiver + * + * @param receivers the receivers + */ + @SafeVarargs + public final void to(@NotNull RECEIVER... receivers) { + if (receivers.length == 0) return; + to(Arrays.asList(receivers)); + } + + /** + * Dispatch the message to the receiver + * + * @param receivers the receivers + */ + public void to(@NotNull Iterable receivers) { + for (RECEIVER receiver : receivers) { + List msg = compile(receiver); + if (msg.isEmpty()) return; + dispatcher.accept(receiver, msg); + } + } + + /** + * Dispatch the message to the receiver + * + * @param receivers the receivers + */ + public void to(@NotNull Supplier> receivers) { + to(receivers.get()); + } + +} diff --git a/features/text/src/main/java/cc/carm/lib/configuration/value/text/function/TextParser.java b/features/text/src/main/java/cc/carm/lib/configuration/value/text/function/TextParser.java new file mode 100644 index 0000000..e6acd39 --- /dev/null +++ b/features/text/src/main/java/cc/carm/lib/configuration/value/text/function/TextParser.java @@ -0,0 +1,298 @@ +package cc.carm.lib.configuration.value.text.function; + +import cc.carm.lib.configuration.value.text.data.TextContents; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.UnaryOperator; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.IntStream; + +public abstract class TextParser> { + + /** + * Used to match the message insertion + *

+ * format: + *
- to insert parsed line {prefix}#content-id#{offset-above,offset-down} + *
- to insert original line {prefix}@content-id@{offset-above,offset-down} + *
example: + *

    + *
  • {- }#content-id#{1,1}
  • + *
  • @content-id@{1,1}
  • + *
+ */ + public static final @NotNull Pattern INSERT_PATTERN = Pattern.compile( + "^(?:\\{(?.*)})?(?[#@])(?.*)[#@](?:\\{(?-?\\d+)(?:,(?-?\\d+))?})?$" + ); + + protected final @NotNull TextContents texts; + + protected BiFunction parser = (receiver, value) -> value; + protected String lineSeparator = System.lineSeparator(); + + /** + * Used to store the placeholders of the message + */ + protected @NotNull Map placeholders = new HashMap<>(); + protected @NotNull UnaryOperator paramBuilder = s -> "%(" + s + ")"; + protected @NotNull String[] params; + + /** + * Used to store the insertion of the message + */ + protected @NotNull Map> insertion = new HashMap<>(); + protected boolean disableInsertion = false; + + protected TextParser(@NotNull TextContents texts, @NotNull String... params) { + this.texts = texts; + this.params = params; + } + + public abstract SELF self(); + + /** + * Disable the insertion of the text. + *
If the insertion is disabled, the text will be parsed directly. + * + * @return the current {@link TextParser} instance + */ + public SELF disableInsertion() { + this.disableInsertion = true; + return self(); + } + + /** + * Enable the insertion of the text. + * + * @return the current {@link TextParser} instance + */ + public SELF enableInsertion() { + this.disableInsertion = false; + return self(); + } + + /** + * Set the line separator for the text. + * + * @param lineSeparator the line separator, default is {@link System#lineSeparator()} + * @return the current {@link TextParser} instance + */ + public SELF lineSeparator(@NotNull String lineSeparator) { + this.lineSeparator = lineSeparator; + return self(); + } + + /** + * Set all the placeholders for the text. + *
Will override the previous placeholders modifications. + * + * @param placeholders the placeholders + * @return the current {@link TextParser} instance + */ + public SELF placeholders(@NotNull Map placeholders) { + this.placeholders = placeholders; + return self(); + } + + /** + * Set the placeholders for the text. + * + * @param consumer the placeholders + * @return the current {@link TextParser} instance + */ + public SELF placeholders(@NotNull Consumer> consumer) { + consumer.accept(this.placeholders); + return self(); + } + + /** + * Set the placeholders for the text. + * + * @param values the values of the placeholders + * @return the current {@link TextParser} instance + */ + public SELF placeholders(@Nullable Object... values) { + return placeholders(map -> map.putAll(buildParams(this.paramBuilder, this.params, values))); + } + + /** + * Set the placeholder for the text. + * + * @param key the key of the placeholder + * @param value the value of the placeholder + * @return the current {@link TextParser} instance + */ + public SELF placeholder(@NotNull String key, @Nullable Object value) { + this.placeholders.put(paramBuilder.apply(key), value); + return self(); + } + + /** + * Set the params for the text, + * used for {@link #placeholders(Object...)} to build the placeholders. + * + * @param params the params + * @return the current {@link TextParser} instance + */ + public SELF params(@NotNull String... params) { + this.params = params; + return self(); + } + + /** + * Insert the specific contents by the id. + * + * @param id the id of the insertion text + * @param lines the lines to insert + * @return the current {@link TextParser} instance + */ + public SELF insert(@NotNull String id, @NotNull String... lines) { + this.insertion.put(id, Arrays.asList(lines)); + return self(); + } + + /** + * Insert the specific contents by the id. + * + * @param id the id of the insertion text + * @param lines the lines to insert + * @return the current {@link TextParser} instance + */ + public SELF insert(@NotNull String id, @NotNull List lines) { + this.insertion.put(id, lines); + return self(); + } + + /** + * Insert the specific contents by the id. + *
If the text not found in {@link TextContents#optionalLines()}, + * nothing will happen. + * + * @param id the id of the insertion text + * @return the current {@link TextParser} instance + */ + public SELF insert(@NotNull String id) { + List lines = this.texts.optionalLines().get(id); + if (lines == null) return self(); + else return insert(id, lines); + } + + /** + * Set the parser for the text. + * + * @param parser The parser + * @return The current {@link TextParser} instance + */ + public SELF parser(@NotNull BiFunction parser) { + this.parser = parser; + return self(); + } + + /** + * Parse the texts for the receiver. + * + * @param receiver the receiver + * @param compiler the compiler + * @param the type of the message + * @return the parsed line + */ + public List parse(@Nullable RECEIVER receiver, @NotNull BiFunction compiler) { + List result = new ArrayList<>(); + handleTexts(receiver, s -> result.add(compiler.apply(receiver, s))); + return result; + } + + /** + * Parse the texts as a single line for the receiver. + * + * @param receiver the receiver + * @param compiler the compiler + * @param the type of the message + * @return the parsed line + */ + public @Nullable V parseLine(@Nullable RECEIVER receiver, @NotNull BiFunction compiler) { + if (this.texts.isEmpty()) return null; + StringBuilder builder = new StringBuilder(); + handleTexts(receiver, s -> builder.append(s).append(this.lineSeparator)); + // Remove the last line separator, if it exists + if (builder.length() > 0) builder.delete(builder.length() - this.lineSeparator.length(), builder.length()); + return compiler.apply(receiver, builder.toString()); + } + + + /** + * Parse the supplied single text for the receiver. + * + * @param receiver the receiver + * @param text the text to parse + * @return the parsed text + */ + protected @Nullable String parseText(@Nullable RECEIVER receiver, @NotNull String text) { + return this.parser.apply(receiver, setPlaceholders(text, this.placeholders)); + } + + + public void handleTexts(@Nullable RECEIVER receiver, @NotNull Consumer lineConsumer) { + if (this.texts.isEmpty()) return; // Nothing to parse + + if (this.disableInsertion) { + this.texts.lines().forEach(line -> lineConsumer.accept(parseText(receiver, line))); + return; // Simple parsed + } + + for (String line : this.texts.lines()) { + Matcher matcher = INSERT_PATTERN.matcher(line); + if (!matcher.matches()) { + lineConsumer.accept(parseText(receiver, line)); + continue; + } + + String id = matcher.group("id"); + List values = this.insertion.get(id); + if (values == null) continue; + + String prefix = matcher.group("prefix"); + String type = matcher.group("type"); + boolean original = type.equals("@"); + int offsetAbove = Optional.ofNullable(matcher.group("above")) + .map(Integer::parseInt).orElse(0); + int offsetDown = Optional.ofNullable(matcher.group("down")) + .map(Integer::parseInt).orElse(offsetAbove); // If offsetDown is not set, use offsetAbove + + IntStream.range(0, Math.max(0, offsetAbove)).mapToObj(i -> "").forEach(lineConsumer); + String prefixContent = Optional.ofNullable(prefix).map(p -> parseText(receiver, p)).orElse(""); + if (original) { + values.stream().map(value -> prefixContent + value).forEach(lineConsumer); + } else { + values.stream().map(value -> prefixContent + parseText(receiver, value)).forEach(lineConsumer); + } + IntStream.range(0, Math.max(0, offsetDown)).mapToObj(i -> "").forEach(lineConsumer); + } + } + + public static String setPlaceholders(@NotNull String messages, + @NotNull Map placeholders) { + if (messages.isEmpty()) return messages; + String parsed = messages; + for (Map.Entry entry : placeholders.entrySet()) { + parsed = parsed.replace(entry.getKey(), entry.getValue() == null ? "" : entry.getValue().toString()); + } + return parsed; + } + + public static Map buildParams(@NotNull UnaryOperator paramBuilder, + @Nullable String[] params, @Nullable Object[] values) { + Map map = new HashMap<>(); + if (params == null || params.length == 0) return map; + for (int i = 0; i < params.length; i++) { + map.put(paramBuilder.apply(params[i]), (values != null && values.length > i) ? values[i] : "?"); + } + return map; + } + +} diff --git a/features/text/src/test/java/cc/carm/lib/configuration/value/text/tests/ConfigTest.java b/features/text/src/test/java/cc/carm/lib/configuration/value/text/tests/ConfigTest.java new file mode 100644 index 0000000..719a460 --- /dev/null +++ b/features/text/src/test/java/cc/carm/lib/configuration/value/text/tests/ConfigTest.java @@ -0,0 +1,47 @@ +package cc.carm.lib.configuration.value.text.tests; + + +import cc.carm.lib.configuration.source.ConfigurationHolder; +import cc.carm.lib.configuration.source.yaml.YAMLConfigFactory; +import cc.carm.lib.configuration.value.text.tests.conf.AppMessages; +import org.junit.Test; + +public class ConfigTest { + + + public static final String[] WEBSITES = new String[]{ + "https://carm.cc", + "https://www.baidu.com", + "https://www.google.com" + }; + + + @Test + public void test() { + + ConfigurationHolder holder = YAMLConfigFactory.from("target/messages.yml").build(); + + holder.initialize(AppMessages.class); + + System.out.println("--------------------------"); + + AppMessages.WELCOME.prepare() + .placeholders("Carm") + .insert("guidance") + .insert("websites", WEBSITES) + .to(System.out); + + System.out.println("--------------------------"); + + AppMessages.NOT_AVAILABLE.sendTo(System.out); + + System.out.println("--------------------------"); + + AppMessages.NO_PERMISSION.sendTo(System.out); + + System.out.println("--------------------------"); + + } + + +} diff --git a/features/text/src/test/java/cc/carm/lib/configuration/value/text/tests/ParseTest.java b/features/text/src/test/java/cc/carm/lib/configuration/value/text/tests/ParseTest.java new file mode 100644 index 0000000..d1541a1 --- /dev/null +++ b/features/text/src/test/java/cc/carm/lib/configuration/value/text/tests/ParseTest.java @@ -0,0 +1,49 @@ +package cc.carm.lib.configuration.value.text.tests; + +import cc.carm.lib.configuration.value.text.PreparedText; +import cc.carm.lib.configuration.value.text.data.TextContents; +import org.junit.Test; + +import java.io.PrintStream; +import java.util.*; + +public class ParseTest { + + + @Test + public void test() { + + List lines = new ArrayList<>(); + lines.add("Hello, %(name)"); + lines.add("#more-creating#{1}"); + lines.add("This is a test message"); + lines.add("#guidance#"); + lines.add("{- }#websites#{0,1}"); + lines.add("Thanks for your reading!"); + + + Map> optional = new HashMap<>(); + optional.put("guidance", Arrays.asList("To get more information for %(name), see:")); + optional.put("websites", Arrays.asList("https://www.baidu.com", "https://www.google.com")); + + TextContents textContents = new TextContents(lines, optional); + + PreparedText msg = new PreparedText(textContents) + .dispatcher((p, s) -> s.forEach(p::println)) + .parser((p, s) -> s) + .compiler((p, s) -> s); + + msg.placeholder("name", "Carm") + .insert("guidance") + .insert("websites", "Baidu", "Bilibili", "Google"); + + System.out.println("----------------------------"); + msg.to(System.out); + System.out.println("----------------------------"); + System.out.println(msg.compileLine(System.out)); + System.out.println("----------------------------"); + + } + + +} diff --git a/features/text/src/test/java/cc/carm/lib/configuration/value/text/tests/conf/AppMessages.java b/features/text/src/test/java/cc/carm/lib/configuration/value/text/tests/conf/AppMessages.java new file mode 100644 index 0000000..71cf455 --- /dev/null +++ b/features/text/src/test/java/cc/carm/lib/configuration/value/text/tests/conf/AppMessages.java @@ -0,0 +1,28 @@ +package cc.carm.lib.configuration.value.text.tests.conf; + +import cc.carm.lib.configuration.Configuration; +import cc.carm.lib.configuration.annotation.ConfigPath; + +@ConfigPath(root = true) +public interface AppMessages extends Configuration { + + ConfiguredMsg WELCOME = ConfiguredMsg.builder() + .defaults( + "Hello, %(name)", + "#more-creating#{1}", + "This is a test message", + "#guidance#", + "{- }#websites#{0,1}", + "Thanks for your reading!") + .optional("guidance", "To get more information for %(name), see:") + .params("name").build(); + + ConfiguredMsg NO_PERMISSION = ConfiguredMsg.builder() + .defaults("Sorry! But you don't have permissions to do this.") + .build(); + + ConfiguredMsg NOT_AVAILABLE = ConfiguredMsg.builder() + .defaults("Error! Service is not available now.", "Please contact your system manager.") + .build(); + +} diff --git a/features/text/src/test/java/cc/carm/lib/configuration/value/text/tests/conf/ConfiguredMsg.java b/features/text/src/test/java/cc/carm/lib/configuration/value/text/tests/conf/ConfiguredMsg.java new file mode 100644 index 0000000..3c61fff --- /dev/null +++ b/features/text/src/test/java/cc/carm/lib/configuration/value/text/tests/conf/ConfiguredMsg.java @@ -0,0 +1,47 @@ +package cc.carm.lib.configuration.value.text.tests.conf; + +import cc.carm.lib.configuration.value.ValueManifest; +import cc.carm.lib.configuration.value.text.ConfiguredText; +import cc.carm.lib.configuration.value.text.data.TextContents; +import org.jetbrains.annotations.NotNull; + +import java.io.PrintStream; + +public class ConfiguredMsg extends ConfiguredText { + + public static @NotNull MsgBuilder builder() { + return new MsgBuilder(); + } + + public static @NotNull ConfiguredMsg of(@NotNull String... text) { + return builder().defaults(text).build(); + } + + public ConfiguredMsg(@NotNull ValueManifest manifest, @NotNull String[] params) { + super( + manifest, + (p, s) -> s, + (p, s) -> s, + (p, s) -> s.forEach(p::println), + params + ); + } + + public void print(@NotNull Object... values) { + sendTo(System.out, values); + } + + public static class MsgBuilder extends Builder { + + @Override + protected @NotNull MsgBuilder self() { + return this; + } + + @Override + public @NotNull ConfiguredMsg build() { + return new ConfiguredMsg(buildManifest(), params); + + } + } +} diff --git a/pom.xml b/pom.xml index 979f228..afc1dd4 100644 --- a/pom.xml +++ b/pom.xml @@ -22,6 +22,7 @@ features/file features/commentable features/versioned + features/text providers/yaml providers/gson