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 index 4bc9961..93a87a6 100644 --- 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 @@ -6,8 +6,21 @@ import org.jetbrains.annotations.NotNull; public class PreparedText extends TextDispatcher> { + protected final @NotNull TextContents texts; + public PreparedText(@NotNull TextContents texts, @NotNull String... params) { - super(texts, params); + this.params = params; + this.texts = texts; + } + + @Override + public TextContents texts() { + return this.texts; + } + + @Override + public PreparedText self() { + return this; } public PreparedText insert(@NotNull String key, @@ -16,8 +29,4 @@ public class PreparedText extends TextDispatcher message.parse(receiver, values)); } - @Override - public PreparedText self() { - return this; - } } diff --git a/features/text/src/main/java/cc/carm/lib/configuration/value/text/function/ContentHandler.java b/features/text/src/main/java/cc/carm/lib/configuration/value/text/function/ContentHandler.java new file mode 100644 index 0000000..1a2235c --- /dev/null +++ b/features/text/src/main/java/cc/carm/lib/configuration/value/text/function/ContentHandler.java @@ -0,0 +1,259 @@ +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.Function; +import java.util.function.UnaryOperator; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.IntStream; + +public abstract class ContentHandler> { + + /** + * 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+))?})?$" + ); + public static final @NotNull UnaryOperator DEFAULT_PARAM_BUILDER = s -> "%(" + s + ")"; + + 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 paramFormatter = DEFAULT_PARAM_BUILDER; + protected @NotNull String[] params; + + /** + * Used to store the insertion of the message + */ + protected @NotNull Map>> insertion = new HashMap<>(); + protected boolean disableInsertion = false; + + 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 ContentHandler} instance + */ + public SELF disableInsertion() { + this.disableInsertion = true; + return self(); + } + + /** + * Enable the insertion of the text. + * + * @return the current {@link ContentHandler} 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 ContentHandler} 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 ContentHandler} 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 ContentHandler} instance + */ + public SELF placeholders(@NotNull Consumer> consumer) { + consumer.accept(this.placeholders); + return self(); + } + + /** + * Set the placeholders for the text. + * + * @param values The values to replace the {@link #params(String...)}. + * @return the current {@link ContentHandler} instance + */ + public SELF placeholders(@Nullable Object... values) { + return placeholders(map -> map.putAll(buildParams(this.paramFormatter, 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 ContentHandler} instance + */ + public SELF placeholder(@NotNull String key, @Nullable Object value) { + this.placeholders.put(paramFormatter.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 ContentHandler} 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 linesSupplier to supply the lines to insert + * @return the current {@link ContentHandler} instance + */ + public SELF insert(@NotNull String id, @NotNull Function> linesSupplier) { + this.insertion.put(id, linesSupplier); + 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 ContentHandler} instance + */ + public SELF insert(@NotNull String id, @NotNull String... lines) { + return insert(id, Arrays.asList(lines)); + } + + /** + * 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 ContentHandler} instance + */ + public SELF insert(@NotNull String id, @NotNull List lines) { + return insert(id, receiver -> lines); + } + + /** + * Set the parser for the text. + * + * @param parser The parser + * @return The current {@link ContentHandler} instance + */ + public SELF parser(@NotNull BiFunction parser) { + this.parser = parser; + return self(); + } + + /** + * 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 parse(@Nullable RECEIVER receiver, @NotNull String text) { + return this.parser.apply(receiver, setPlaceholders(text, this.placeholders)); + } + + public void handle(@NotNull TextContents contents, @Nullable RECEIVER receiver, + @NotNull Consumer lineConsumer) { + if (contents.isEmpty()) return; // Nothing to parse + + if (this.disableInsertion) { + contents.lines().forEach(line -> lineConsumer.accept(parse(receiver, line))); + return; // Simple parsed + } + + for (String line : contents.lines()) { + Matcher matcher = INSERT_PATTERN.matcher(line); + if (!matcher.matches()) { + lineConsumer.accept(parse(receiver, line)); + continue; + } + + String id = matcher.group("id"); + List values = Optional.ofNullable(this.insertion.get(id)) + .map(f -> f.apply(receiver)) + .orElse(null); + if (values == null || values.isEmpty()) 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 -> parse(receiver, p)).orElse(""); + if (original) { + values.stream().map(value -> prefixContent + value).forEach(lineConsumer); + } else { + values.stream().map(value -> prefixContent + parse(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/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 index 549ad80..0c36c88 100644 --- 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 @@ -1,6 +1,5 @@ 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; @@ -11,10 +10,6 @@ public abstract class TextCompiler compiler = (receiver, value) -> null; - protected TextCompiler(@NotNull TextContents texts, @NotNull String... params) { - super(texts, params); - } - /** * Set the text 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 index 9e15b6b..3014e9d 100644 --- 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 @@ -13,10 +13,6 @@ public abstract class TextDispatcher> dispatcher = (receiver, msg) -> { }; - protected TextDispatcher(@NotNull TextContents texts, @NotNull String... params) { - super(texts, params); - } - /** * Set the dispatcher to send the message to the receiver * 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 index 7c0e3d3..423f6d2 100644 --- 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 @@ -4,205 +4,16 @@ 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.ArrayList; +import java.util.List; +import java.util.Optional; import java.util.function.BiFunction; import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.UnaryOperator; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.stream.IntStream; -public abstract class TextParser> { +public abstract class TextParser> + extends ContentHandler { - /** - * 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 to replace the {@link #params(String...)}. - * @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 linesSupplier to supply the lines to insert - * @return the current {@link TextParser} instance - */ - public SELF insert(@NotNull String id, @NotNull Function> linesSupplier) { - this.insertion.put(id, linesSupplier); - 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) { - return insert(id, Arrays.asList(lines)); - } - - /** - * 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) { - return insert(id, receiver -> lines); - } - - /** - * 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(); - } + public abstract TextContents texts(); /** * Parse the texts for the receiver. @@ -212,7 +23,7 @@ public abstract class TextParser parse(@Nullable RECEIVER receiver) { List result = new ArrayList<>(); - handleTexts(receiver, result::add); + handle(receiver, result::add); return result; } @@ -226,7 +37,7 @@ public abstract class TextParser List parse(@Nullable RECEIVER receiver, @NotNull BiFunction compiler) { List result = new ArrayList<>(); - handleTexts(receiver, s -> result.add(compiler.apply(receiver, s))); + handle(receiver, s -> result.add(compiler.apply(receiver, s))); return result; } @@ -237,9 +48,9 @@ public abstract class TextParser builder.append(s).append(this.lineSeparator)); + handle(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 builder.toString(); @@ -256,76 +67,8 @@ public abstract class TextParser compiler.apply(receiver, s)).orElse(null); } - - /** - * 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 = Optional.ofNullable(this.insertion.get(id)) - .map(f -> f.apply(receiver)) - .orElse(null); - if (values == null || values.isEmpty()) 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; + public void handle(@Nullable RECEIVER receiver, @NotNull Consumer lineConsumer) { + handle(texts(), receiver, lineConsumer); } }