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 index 607ce6b..0c1b955 100644 --- 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 @@ -1,54 +1,27 @@ package cc.carm.lib.configuration.value.text.function; import cc.carm.lib.configuration.value.text.data.TextContents; -import cc.carm.lib.configuration.value.text.function.modifier.ContentReplacer; +import cc.carm.lib.configuration.value.text.function.common.AppendLineInserter; +import cc.carm.lib.configuration.value.text.function.common.OptionalLineInserter; +import cc.carm.lib.configuration.value.text.function.common.ParamReplacer; +import cc.carm.lib.configuration.value.text.function.inserter.ContentInserter; +import cc.carm.lib.configuration.value.text.function.inserter.Insertable; +import cc.carm.lib.configuration.value.text.function.replacer.ContentReplacer; +import cc.carm.lib.configuration.value.text.function.replacer.Replaceable; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; 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} - *
original lines will not be parsed - *
example: - *

- */ - public static final @NotNull Pattern INSERT_PATTERN = Pattern.compile( - "^(?:\\{(?.*)})?(?[#@])(?.*)[#@](?:\\{(?-?\\d+)(?:,(?-?\\d+))?})?$" - ); - - /** - * Used to match the message which can be inserted - *

- * format: - *
- ?[id]Message content - *
example: - *

    - *
  • ?[click]Click to use this item!
  • - *
- */ - public static final @NotNull Pattern ENABLE_PATTERN = Pattern.compile( - "^\\?\\[(?.+)](?.*)$" - ); - - public static final @NotNull Function - public static final @NotNull UnaryOperator DEFAULT_PARAM_BUILDER = s -> "%(" + s + ")"; +public abstract class ContentHandler> + implements Replaceable, Insertable { protected BiFunction parser = (receiver, value) -> value; protected String lineSeparator = System.lineSeparator(); @@ -56,19 +29,24 @@ public abstract class ContentHandler placeholders = new HashMap<>(); - protected @NotNull UnaryOperator paramFormatter = DEFAULT_PARAM_BUILDER; + protected @NotNull ParamReplacer paramReplacer = ParamReplacer.defaults(); protected @NotNull String[] params; - protected @NotNull List replacers = new ArrayList<>(); - protected @NotNull List inserters = new ArrayList<>(); + protected @NotNull List> replacers = new ArrayList<>(); + protected @NotNull List> inserters = new ArrayList<>(); /** * Used to store the insertion of the message */ - protected @NotNull Map>> insertion = new HashMap<>(); + protected final @NotNull Map>> insertion = new HashMap<>(); protected boolean disableInsertion = false; + public ContentHandler() { + inserters.add(new AppendLineInserter<>(0)); + inserters.add(new OptionalLineInserter<>(0)); + } + + public abstract SELF self(); /** @@ -92,6 +70,11 @@ public abstract class ContentHandler placeholders) { - this.placeholders = placeholders; + this.paramReplacer.set(placeholders); return self(); } @@ -122,7 +105,7 @@ public abstract class ContentHandler> consumer) { - consumer.accept(this.placeholders); + consumer.accept(this.paramReplacer.placeholders()); return self(); } @@ -133,7 +116,8 @@ public abstract class ContentHandler map.putAll(buildParams(this.paramFormatter, this.params, values))); + this.paramReplacer.putAll(this.params, values); + return self(); } /** @@ -144,7 +128,7 @@ public abstract class ContentHandler> replacers() { + return this.replacers; + } + + @Override + public SELF replacer(@NotNull ContentReplacer replacer) { + this.replacers.add(replacer); + this.replacers.sort(ContentReplacer::compareTo); 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); + @Override + public List> inserters() { + return this.inserters; + } + + @Override + public SELF inserter(@NotNull ContentInserter inserter) { + this.inserters.add(inserter); + this.inserters.sort(ContentInserter::compareTo); 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)); + @Override + public SELF insert(@NotNull String id, @Nullable Function> supplier) { + this.insertion.put(id, supplier); + 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 List lines) { - return insert(id, receiver -> lines); + @Override + public boolean inserting(@NotNull String id) { + return this.insertion.keySet().stream().anyMatch(key -> key.equalsIgnoreCase(id)); + } + + @Override + public @Nullable List getInsertion(@NotNull String id, @Nullable RECEIVER receiver) { + Function> function = this.insertion.get(id); + if (function == null) return null; + return function.apply(receiver); + } + + @Override + public SELF removeInsert(@NotNull String id) { + this.insertion.remove(id); + return self(); } /** @@ -224,73 +211,41 @@ public abstract class ContentHandler lineConsumer) { if (contents.isEmpty()) return; // Nothing to parse - if (this.disableInsertion) { + if (this.disableInsertion || noneInsertion()) { contents.lines().forEach(line -> lineConsumer.accept(parse(receiver, line))); return; // Simple parsed } + // Set the default insertion of the text + for (Map.Entry>> entry : this.insertion.entrySet()) { + if (entry.getValue() != null) continue; + List lines = contents.optionalLines().get(entry.getKey()); + if (lines == null) continue; + entry.setValue(r -> lines); + } + + lines: for (String line : contents.lines()) { - Matcher insertMatcher = INSERT_PATTERN.matcher(line); - if (insertMatcher.matches()) { - doInsert(insertMatcher, receiver, lineConsumer); - continue; - } - - Matcher enableMatcher = ENABLE_PATTERN.matcher(line); - if (enableMatcher.matches()) { - if (this.insertion.containsKey(enableMatcher.group("id"))) { - lineConsumer.accept(parse(receiver, enableMatcher.group("content"))); + for (ContentInserter inserter : inserters) { + List inserted = inserter.handle(receiver, line, this); + if (inserted != null) { // Found the insertion + inserted.forEach(l -> lineConsumer.accept(parse(receiver, l))); + continue lines; // Go to the next line } - continue; } - lineConsumer.accept(parse(receiver, line)); } } - private void doInsert(Matcher matcher, @Nullable RECEIVER receiver, - @NotNull Consumer lineConsumer) { - 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()) return; // No values to insert - - 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<>(); diff --git a/features/text/src/main/java/cc/carm/lib/configuration/value/text/function/common/AppendLineInserter.java b/features/text/src/main/java/cc/carm/lib/configuration/value/text/function/common/AppendLineInserter.java new file mode 100644 index 0000000..71684f3 --- /dev/null +++ b/features/text/src/main/java/cc/carm/lib/configuration/value/text/function/common/AppendLineInserter.java @@ -0,0 +1,67 @@ +package cc.carm.lib.configuration.value.text.function.common; + +import cc.carm.lib.configuration.value.text.function.inserter.ContentInserter; +import cc.carm.lib.configuration.value.text.function.inserter.Insertable; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.IntStream; + +public class AppendLineInserter extends ContentInserter { + + /** + * 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} + *
original lines will not be parsed + *
example: + *

    + *
  • {- }#content-id#{1,1}
  • + *
  • @content-id@{1,1}
  • + *
+ */ + public static final @NotNull Pattern APPEND_PATTERN = Pattern.compile( + "^(?:\\{(?.*)})?#(?.*)#(?:\\{(?-?\\d+)(?:,(?-?\\d+))?})?$" + ); + + public AppendLineInserter(int priority) { + super(priority, APPEND_PATTERN); + } + + @Override + protected @Nullable String extractID(@NotNull Matcher matcher) { + return matcher.group("id"); + } + + @Override + protected @NotNull List get(@Nullable RECEIVER receiver, @NotNull Matcher matcher, + @NotNull Insertable insertion) { + String id = extractID(matcher); + List values = insertion.getInsertion(id, receiver); + if (values == null || values.isEmpty()) return Collections.emptyList(); // No values to insert + + String prefix = Optional.ofNullable(matcher.group("prefix")).orElse(""); + 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 + + List contents = new ArrayList<>(); + + IntStream.range(0, Math.max(0, offsetAbove)).mapToObj(i -> "").forEach(contents::add); + values.stream().map(value -> prefix + value).forEach(contents::add); + IntStream.range(0, Math.max(0, offsetDown)).mapToObj(i -> "").forEach(contents::add); + + return contents; + } + + +} diff --git a/features/text/src/main/java/cc/carm/lib/configuration/value/text/function/common/OptionalLineInserter.java b/features/text/src/main/java/cc/carm/lib/configuration/value/text/function/common/OptionalLineInserter.java new file mode 100644 index 0000000..c1da771 --- /dev/null +++ b/features/text/src/main/java/cc/carm/lib/configuration/value/text/function/common/OptionalLineInserter.java @@ -0,0 +1,46 @@ +package cc.carm.lib.configuration.value.text.function.common; + +import cc.carm.lib.configuration.value.text.function.inserter.ContentInserter; +import cc.carm.lib.configuration.value.text.function.inserter.Insertable; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collections; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class OptionalLineInserter extends ContentInserter { + + /** + * Used to match the message which can be inserted + *

+ * format: + *
- ?[id]Message content + *
example: + *

    + *
  • ?[click]Click to use this item!
  • + *
+ */ + public static final @NotNull Pattern OPTIONAL_PATTERN = Pattern.compile( + "^\\?\\[(?.+)](?.*)$" + ); + + public OptionalLineInserter(int priority) { + super(priority, OPTIONAL_PATTERN); + } + + @Override + protected @Nullable String extractID(@NotNull Matcher matcher) { + return matcher.group("id"); + } + + @Override + protected @NotNull List get(@Nullable RECEIVER receiver, @NotNull Matcher matcher, + @NotNull Insertable insertion) { + String content = matcher.group("content"); + if (content == null) return Collections.emptyList(); + return Collections.singletonList(content); + } + +} diff --git a/features/text/src/main/java/cc/carm/lib/configuration/value/text/function/common/ParamReplacer.java b/features/text/src/main/java/cc/carm/lib/configuration/value/text/function/common/ParamReplacer.java new file mode 100644 index 0000000..d6604cb --- /dev/null +++ b/features/text/src/main/java/cc/carm/lib/configuration/value/text/function/common/ParamReplacer.java @@ -0,0 +1,71 @@ +package cc.carm.lib.configuration.value.text.function.common; + +import cc.carm.lib.configuration.value.text.function.replacer.ContentReplacer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class ParamReplacer extends ContentReplacer { + + public static final @NotNull Pattern DEFAULT_PARAM_PATTERN = Pattern.compile("%\\((?[^)]+)\\)"); + + public static ParamReplacer defaults() { + return create(DEFAULT_PARAM_PATTERN, m -> m.group("id")); + } + + public static ParamReplacer create(@NotNull Pattern pattern, @NotNull Function extractor) { + return new ParamReplacer<>(0, pattern, extractor); + } + + protected final @NotNull Function extractor; + protected @NotNull Map placeholders = new HashMap<>(); + + public ParamReplacer(int priority, @NotNull Pattern pattern, @NotNull Function extractor) { + super(priority, pattern); + this.extractor = extractor; + } + + public @NotNull Map placeholders() { + return placeholders; + } + + @Override + protected @Nullable String get(@NotNull RECEIVER receiver, @NotNull Matcher matcher) { + @Nullable String id = extractor.apply(matcher); + if (id == null) return null; + Object value = placeholders.get(id); + if (value == null) return null; + return value.toString(); + } + + public void set(@NotNull Map placeholders) { + this.placeholders = placeholders; + } + + public void put(@NotNull String id, @Nullable Object value) { + placeholders.put(id, value); + } + + public void remove(@NotNull String id) { + placeholders.remove(id); + } + + public void putAll(@NotNull Map placeholders) { + this.placeholders.putAll(placeholders); + } + + public void putAll(@NotNull String[] params, @NotNull Object[] values) { + for (int i = 0; i < params.length; i++) { + placeholders.put(params[i], (values != null && values.length > i) ? values[i] : "?"); + } + } + + public void clear() { + placeholders.clear(); + } +} diff --git a/features/text/src/main/java/cc/carm/lib/configuration/value/text/function/inserter/ContentInserter.java b/features/text/src/main/java/cc/carm/lib/configuration/value/text/function/inserter/ContentInserter.java new file mode 100644 index 0000000..2b90025 --- /dev/null +++ b/features/text/src/main/java/cc/carm/lib/configuration/value/text/function/inserter/ContentInserter.java @@ -0,0 +1,51 @@ +package cc.carm.lib.configuration.value.text.function.inserter; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collections; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public abstract class ContentInserter implements Comparable> { + + protected final int priority; + protected @NotNull Pattern pattern; + + public ContentInserter(int priority, @NotNull Pattern pattern) { + this.priority = priority; + this.pattern = pattern; + } + + public int priority() { + return priority; + } + + public void setPattern(@NotNull Pattern pattern) { + this.pattern = pattern; + } + + public @NotNull Matcher matcher(@NotNull String text) { + return pattern.matcher(text); + } + + protected abstract @Nullable String extractID(@NotNull Matcher matcher); + + protected abstract @NotNull List get(@Nullable RECEIVER receiver, @NotNull Matcher matcher, + @NotNull Insertable insertions); + + public @Nullable List handle(@Nullable RECEIVER receiver, @NotNull String line, + @NotNull Insertable insertions) { + Matcher matcher = matcher(line); + if (!matcher.matches()) return null; + if (!insertions.inserting(extractID(matcher))) return Collections.emptyList(); + return get(receiver, matcher, insertions); + } + + @Override + public int compareTo(@NotNull ContentInserter o) { + return Integer.compare(o.priority, priority); + } + +} diff --git a/features/text/src/main/java/cc/carm/lib/configuration/value/text/function/inserter/Insertable.java b/features/text/src/main/java/cc/carm/lib/configuration/value/text/function/inserter/Insertable.java new file mode 100644 index 0000000..d2a3488 --- /dev/null +++ b/features/text/src/main/java/cc/carm/lib/configuration/value/text/function/inserter/Insertable.java @@ -0,0 +1,66 @@ +package cc.carm.lib.configuration.value.text.function.inserter; + +import cc.carm.lib.configuration.value.text.function.ContentHandler; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Arrays; +import java.util.List; +import java.util.function.Function; + +public interface Insertable { + + List> inserters(); + + SELF inserter(@NotNull ContentInserter inserter); + + boolean noneInsertion(); + + boolean inserting(@NotNull String id); + + @Nullable List getInsertion(@NotNull String id, @Nullable RECEIVER receiver); + + /** + * Insert the specific contents by the id. + * + * @param id the id of the insertion text + * @param supplier to supply the lines to insert + * @return the current {@link ContentHandler} instance + */ + SELF insert(@NotNull String id, @Nullable Function> supplier); + + /** + * Insert the specific contents by the id. + * + * @param id the id of the insertion text + * @return the current {@link ContentHandler} instance + */ + default SELF insert(@NotNull String id) { + return insert(id, (Function>) null); + } + + /** + * Insert the specific contents by the id. + * + * @param id the id of the insertion text + * @param contents the lines to insert + * @return the current {@link ContentHandler} instance + */ + default SELF insert(@NotNull String id, @NotNull List contents) { + return insert(id, receiver -> contents); + } + + /** + * Insert the specific contents by the id. + * + * @param id the id of the insertion text + * @param contents the lines to insert + * @return the current {@link ContentHandler} instance + */ + default SELF insert(@NotNull String id, @NotNull String... contents) { + return insert(id, Arrays.asList(contents)); + } + + SELF removeInsert(@NotNull String id); + +} diff --git a/features/text/src/main/java/cc/carm/lib/configuration/value/text/function/modifier/ContentReplacer.java b/features/text/src/main/java/cc/carm/lib/configuration/value/text/function/modifier/ContentReplacer.java deleted file mode 100644 index 8ebf952..0000000 --- a/features/text/src/main/java/cc/carm/lib/configuration/value/text/function/modifier/ContentReplacer.java +++ /dev/null @@ -1,82 +0,0 @@ -package cc.carm.lib.configuration.value.text.function.modifier; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.function.BiFunction; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.function.Supplier; -import java.util.regex.Pattern; - -public class ContentReplacer { - - public static ContentReplacer.Builder match(@NotNull Predicate matcher) { - return new Builder<>(matcher); - } - - public static ContentReplacer.Builder match(@NotNull String id) { - return match(text -> text.equalsIgnoreCase(id)); - } - - public static ContentReplacer.Builder match(@NotNull Pattern pattern) { - return match(text -> pattern.matcher(text).find()); - } - - protected final int priority; - protected final @NotNull Predicate matcher; - protected final BiFunction<@NotNull RECEIVER, @NotNull String, @Nullable String> supplier; - - public ContentReplacer(int priority, - @NotNull Predicate matcher, - BiFunction<@NotNull RECEIVER, @NotNull String, @Nullable String> supplier) { - this.priority = priority; - this.matcher = matcher; - this.supplier = supplier; - } - - public @NotNull Predicate matcher() { - return this.matcher; - } - - public boolean check(@NotNull String param) { - return this.matcher.test(param); - } - - public @Nullable String content(@NotNull RECEIVER receiver, @NotNull String matchedParam) { - return this.supplier == null ? null : this.supplier.apply(receiver, matchedParam); - } - - public static class Builder { - - protected final @NotNull Predicate matcher; - protected int priority = 0; - - public Builder(@NotNull Predicate matcher) { - this.matcher = matcher; - } - - public @NotNull Builder priority(int priority) { - this.priority = priority; - return this; - } - - public @NotNull ContentReplacer to(BiFunction<@NotNull R, @NotNull String, @Nullable String> supplier) { - return new ContentReplacer<>(this.priority, this.matcher, supplier); - } - - public @NotNull ContentReplacer to(Function<@NotNull R, @Nullable String> supplier) { - return to((receiver, matchedParam) -> supplier.apply(receiver)); - } - - public @NotNull ContentReplacer to(Supplier<@Nullable String> supplier) { - return to((receiver, matchedParam) -> supplier.get()); - } - - public @NotNull ContentReplacer to(@NotNull String content) { - return to(() -> content); - } - - } - -} diff --git a/features/text/src/main/java/cc/carm/lib/configuration/value/text/function/modifier/ParamHandler.java b/features/text/src/main/java/cc/carm/lib/configuration/value/text/function/modifier/ParamHandler.java deleted file mode 100644 index 0567bd2..0000000 --- a/features/text/src/main/java/cc/carm/lib/configuration/value/text/function/modifier/ParamHandler.java +++ /dev/null @@ -1,26 +0,0 @@ -package cc.carm.lib.configuration.value.text.function.modifier; - -import java.util.function.Function; -import java.util.regex.Pattern; - -public class ParamHandler { - - // %() - public static final Pattern DEFAULT_PARAM_PATTERN = Pattern.compile("%\\((?[^)]+)\\)"); - public static final ParamHandler DEFAULT_PARAM = new ParamHandler( - s -> { - }, - s -> { - - } - ) - - - protected final Function extractor; - protected final Function replacer; - - public ParamHandler(Function extractor, Function replacer) { - this.extractor = extractor; - this.replacer = replacer; - } -} diff --git a/features/text/src/main/java/cc/carm/lib/configuration/value/text/function/replacer/ContentReplacer.java b/features/text/src/main/java/cc/carm/lib/configuration/value/text/function/replacer/ContentReplacer.java new file mode 100644 index 0000000..5470f98 --- /dev/null +++ b/features/text/src/main/java/cc/carm/lib/configuration/value/text/function/replacer/ContentReplacer.java @@ -0,0 +1,105 @@ +package cc.carm.lib.configuration.value.text.function.replacer; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public abstract class ContentReplacer implements Comparable> { + + public static ContentReplacer.Builder match(@NotNull Pattern pattern) { + return new Builder<>(pattern); + } + + public static ContentReplacer.Builder match(@NotNull String pattern) { + return match(Pattern.compile(pattern)); + } + + public static ContentReplacer.Builder replace(@NotNull String text) { + return match(Pattern.quote(text)); + } + + protected final int priority; + protected @NotNull Pattern pattern; + + public ContentReplacer(int priority, @NotNull Pattern pattern) { + this.priority = priority; + this.pattern = pattern; + } + + public int priority() { + return priority; + } + + public void pattern(@NotNull Pattern pattern) { + this.pattern = pattern; + } + + public @NotNull Matcher matcher(@NotNull String text) { + return pattern.matcher(text); + } + + public String replace(@NotNull String text, @Nullable RECEIVER receiver) { + Matcher matcher = matcher(text); + StringBuffer sb = new StringBuffer(); + while (matcher.find()) { + String replaced = get(receiver, matcher); + matcher.appendReplacement(sb, replaced == null ? "" : replaced); + } + matcher.appendTail(sb); + return sb.toString(); + } + + protected abstract @Nullable String get(@Nullable RECEIVER receiver, @NotNull Matcher matcher); + + @Override + public int compareTo(@NotNull ContentReplacer o) { + return Integer.compare(o.priority, this.priority); + } + + public static class Builder { + + protected final @NotNull Pattern pattern; + protected int priority = 0; + + public Builder(@NotNull Pattern pattern) { + this.pattern = pattern; + } + + public @NotNull Builder priority(int priority) { + this.priority = priority; + return this; + } + + public @NotNull ContentReplacer to(BiFunction<@Nullable R, @NotNull Matcher, @Nullable String> supplier) { + return new ContentReplacer(this.priority, this.pattern) { + @Override + protected @Nullable String get(@NotNull R receiver, @NotNull Matcher matcher) { + return supplier.apply(receiver, matcher); + } + }; + } + + public @NotNull ContentReplacer to(Function<@Nullable R, @Nullable String> supplier) { + return to((receiver, matchedParam) -> supplier.apply(receiver)); + } + + public @NotNull ContentReplacer to(Supplier<@Nullable String> supplier) { + return to((receiver, matchedParam) -> supplier.get()); + } + + public @NotNull ContentReplacer to(@NotNull String content) { + return to(() -> content); + } + + } + + @FunctionalInterface + public interface Constructor extends Function, ContentReplacer> { + } + +} diff --git a/features/text/src/main/java/cc/carm/lib/configuration/value/text/function/replacer/Replaceable.java b/features/text/src/main/java/cc/carm/lib/configuration/value/text/function/replacer/Replaceable.java new file mode 100644 index 0000000..663d0f8 --- /dev/null +++ b/features/text/src/main/java/cc/carm/lib/configuration/value/text/function/replacer/Replaceable.java @@ -0,0 +1,63 @@ +package cc.carm.lib.configuration.value.text.function.replacer; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public interface Replaceable { + + List> replacers(); + + default String applyReplacements(@Nullable RECEIVER receiver, @NotNull String text) { + for (ContentReplacer replacer : replacers()) { + text = replacer.replace(text, receiver); + } + return text; + } + + SELF replacer(@NotNull ContentReplacer replacer); + + default SELF replacer(@NotNull Pattern pattern, @NotNull ContentReplacer.Constructor constructor) { + ContentReplacer.Builder builder = ContentReplacer.match(pattern); + return replacer(constructor.apply(builder)); + } + + default SELF replace(@NotNull Pattern pattern, + @NotNull BiFunction<@Nullable RECEIVER, @NotNull Matcher, @Nullable String> supplier) { + return replacer(pattern, builder -> builder.to(supplier)); + } + + default SELF replace(@NotNull Pattern pattern, + @NotNull Function<@Nullable RECEIVER, @Nullable String> supplier) { + return replacer(pattern, builder -> builder.to(supplier)); + } + + default SELF replace(@NotNull Pattern pattern, + @NotNull Supplier<@Nullable String> supplier) { + return replacer(pattern, builder -> builder.to(supplier)); + } + + default SELF replace(@NotNull Pattern pattern, @NotNull String replacement) { + return replacer(pattern, builder -> builder.to(replacement)); + } + + default SELF replace(@NotNull String text, @NotNull String replacement) { + return replacer(Pattern.compile(Pattern.quote(text)), builder -> builder.to(replacement)); + } + + default SELF replace(@NotNull String text, @NotNull Supplier<@Nullable String> supplier) { + return replacer(Pattern.compile(Pattern.quote(text)), builder -> builder.to(supplier)); + } + + default SELF replace(@NotNull String text, + @NotNull Function<@Nullable RECEIVER, @Nullable String> supplier) { + return replacer(Pattern.compile(Pattern.quote(text)), builder -> builder.to(supplier)); + } + +} 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 index 4c5567d..a144261 100644 --- 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 @@ -11,14 +11,12 @@ import java.util.List; 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() { 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 index 3815316..42ea5a1 100644 --- 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 @@ -7,6 +7,7 @@ import org.junit.Test; import java.io.PrintStream; import java.util.*; + public class ParseTest { @@ -18,14 +19,15 @@ public class ParseTest { lines.add("#more-creating#{1}"); lines.add("This is a test message"); lines.add("#guidance#"); - lines.add("{- }#websites#{0,1}"); + lines.add("{ - }#websites#{0,1}"); lines.add("Thanks for your reading!"); + lines.add("uuid: $UUID$"); lines.add("?[click]"); lines.add("?[click]Click to see more!"); lines.add("?[hidden]This entry should be hidden!"); Map> optional = new HashMap<>(); - optional.put("guidance", Arrays.asList("To get more information for %(name), see:")); + 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); @@ -33,13 +35,17 @@ public class ParseTest { PreparedText msg = new PreparedText(textContents) .dispatcher((p, s) -> s.forEach(p::println)) .parser((p, s) -> s) - .compiler((p, s) -> s); + .compiler((p, s) -> s) + .replace( // Custom replacer, replace $UUID$ with Random UUID + "$UUID$", () -> UUID.randomUUID().toString() + ); msg.placeholder("name", "Carm") .insert("guidance") .insert("click") .insert("websites", "Baidu", "Bilibili", "Google"); + System.out.println("----------------------------"); msg.to(System.out); System.out.println("----------------------------");