Skip to content

Commit

Permalink
Add support for preprocessing and postprocessing of template
Browse files Browse the repository at this point in the history
  • Loading branch information
rchomczyk committed Nov 24, 2024
1 parent 834a662 commit 2b405dd
Show file tree
Hide file tree
Showing 8 changed files with 63 additions and 93 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import dev.shiza.honey.placeholder.resolver.PlaceholderResolver;
import dev.shiza.honey.placeholder.sanitizer.PlaceholderSanitizer;
import dev.shiza.honey.placeholder.sanitizer.PlaceholderSanitizer.SanitizedPlaceholder;
import dev.shiza.honey.processor.ProcessorPhase;
import dev.shiza.honey.processor.ProcessorRegistry;
import java.util.List;
import java.util.Set;
Expand Down Expand Up @@ -52,14 +53,19 @@ protected MessageBaseFormatter(
*/
@Override
public T format(final Message message) {
final Set<Placeholder> placeholders = placeholderResolver.resolve(message.content());
final String preprocessedContent =
processorRegistry.process(ProcessorPhase.PREPROCESS, message.content());
final Message preprocessedMessage = new Message(preprocessedContent, message.context());

final Set<Placeholder> placeholders = placeholderResolver.resolve(preprocessedContent);
if (placeholders.isEmpty()) {
return compile(message, List.of());
return compile(preprocessedMessage, List.of());
}

final List<SanitizedPlaceholder> sanitizedPlaceholders =
placeholderProcessor.process(message.context().merge(placeholderContext), placeholders);
return compile(message, sanitizedPlaceholders);
placeholderProcessor.process(
preprocessedMessage.context().merge(placeholderContext), placeholders);
return compile(preprocessedMessage, sanitizedPlaceholders);
}

/**
Expand Down Expand Up @@ -89,7 +95,8 @@ public CompletableFuture<T> formatAsync(final Message message) {
* @return The compiled message of type T.
*/
private T compile(final Message message, final List<SanitizedPlaceholder> placeholders) {
final String processedContent = processorRegistry.preprocess(message.content());
final String processedContent =
processorRegistry.process(ProcessorPhase.POSTPROCESS, message.content());
final String sanitizedContent =
placeholderSanitizer.getSanitizedContent(processedContent, placeholders);
return messageCompiler.compile(sanitizedContent, placeholders);
Expand Down
14 changes: 0 additions & 14 deletions honey-common/src/dev/shiza/honey/processor/Processor.java
Original file line number Diff line number Diff line change
@@ -1,21 +1,7 @@
package dev.shiza.honey.processor;

/**
* Functional interface for processing content.
*
* <p>This interface is designed for operations that take a single {@link String} argument and
* return a processed {@link String} result. It is marked as a {@link FunctionalInterface} to ensure
* that it can be used in contexts where lambda expressions and method references are expected, such
* as with streams or in custom functional programming constructs.
*/
@FunctionalInterface
public interface Processor {

/**
* Processes the specified content and returns the processed result.
*
* @param content the content to process
* @return the processed result
*/
String process(final String content);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package dev.shiza.honey.processor;

public enum ProcessorPhase {
PREPROCESS,
POSTPROCESS
}
29 changes: 2 additions & 27 deletions honey-common/src/dev/shiza/honey/processor/ProcessorRegistry.java
Original file line number Diff line number Diff line change
@@ -1,33 +1,8 @@
package dev.shiza.honey.processor;

/**
* The {@code ProcessorRegistry} interface provides methods for registering a preprocessor and
* preprocessing content. Implementations of this interface are expected to maintain a registry of
* {@link Processor} instances and apply these processors to content.
*
* <p>This interface is typically used to modify or enhance content before it is processed in its
* final form.
*/
public interface ProcessorRegistry {

/**
* Registers the given processor as a preprocessor. The registered processor will be applied in
* the preprocessing stage, and it can modify or enhance the input content.
*
* @param processor the {@link Processor} to register as a preprocessor
* @return the current instance of {@code ProcessorRegistry} to allow method chaining
* @throws IllegalArgumentException if the processor is null
*/
ProcessorRegistry preprocessor(final Processor processor);
ProcessorRegistry processor(final ProcessorPhase phase, final Processor processor);

/**
* Executes preprocessing on the provided content using all registered preprocessors. The method
* processes the content sequentially through each preprocessor and returns the final processed
* output.
*
* @param content the original content to process
* @return the processed content after applying all registered preprocessors
* @throws IllegalArgumentException if the content is null
*/
String preprocess(final String content);
String process(final ProcessorPhase phase, final String content);
}
Original file line number Diff line number Diff line change
@@ -1,32 +1,18 @@
package dev.shiza.honey.processor;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import java.util.List;
import java.util.Map;

/**
* Factory for creating instances of {@link ProcessorRegistry}. This class provides static factory
* methods to instantiate {@link ProcessorRegistry} with or without initial processors.
*/
public final class ProcessorRegistryFactory {

private ProcessorRegistryFactory() {}

/**
* Creates a {@link ProcessorRegistry} with the specified list of preprocessors.
*
* @param preprocessors the list of processors to include in the registry
* @return an instance of {@link ProcessorRegistry} initialized with the given preprocessors
*/
public static ProcessorRegistry create(final List<Processor> preprocessors) {
public static ProcessorRegistry create(final Map<ProcessorPhase, List<Processor>> preprocessors) {
return new ProcessorRegistryImpl(preprocessors);
}

/**
* Creates a {@link ProcessorRegistry} with no initial preprocessors.
*
* @return an instance of {@link ProcessorRegistry} with no initial processors
*/
public static ProcessorRegistry create() {
return create(ImmutableList.of());
return create(ImmutableMap.of());
}
}
Original file line number Diff line number Diff line change
@@ -1,41 +1,35 @@
package dev.shiza.honey.processor;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
* An implementation of the {@link ProcessorRegistry} interface which allows for keeping track and
* manipulating a list of preprocessors.
*/
final class ProcessorRegistryImpl implements ProcessorRegistry {

private final List<Processor> preprocessors;
private final Map<ProcessorPhase, List<Processor>> preprocessors;

ProcessorRegistryImpl(final List<Processor> preprocessors) {
this.preprocessors = preprocessors;
ProcessorRegistryImpl(final Map<ProcessorPhase, List<Processor>> preprocessors) {
this.preprocessors = ImmutableMap.copyOf(preprocessors);
}

/**
* Appends a new preprocessor to the registry and returns a new instance of the registry.
*
* @param processor the preprocessor to be added to the registry
* @return a new instance of {@link ProcessorRegistry} with the added preprocessor
*/
@Override
public ProcessorRegistry preprocessor(final Processor processor) {
public ProcessorRegistry processor(final ProcessorPhase phase, final Processor processor) {
return new ProcessorRegistryImpl(
ImmutableList.<Processor>builder().addAll(preprocessors).add(processor).build());
ImmutableMap.<ProcessorPhase, List<Processor>>builder()
.putAll(preprocessors)
.put(phase, Collections.singletonList(processor))
.build());
}

/**
* Processes a string content by applying all the preprocessors registered.
*
* @param content the content to be processed
* @return the processed content after all preprocessors have been applied
*/
@Override
public String preprocess(final String content) {
return preprocessors.stream()
public String process(final ProcessorPhase phase, final String content) {
final List<Processor> processors = preprocessors.get(phase);
if (processors == null || processors.isEmpty()) {
return content;
}

return processors.stream()
.reduce(
content,
(processedContent, processor) -> processor.process(processedContent),
Expand Down
14 changes: 11 additions & 3 deletions honey-test-plugin/src/dev/shiza/honey/ExampleListener.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import dev.shiza.honey.adventure.message.dispatcher.AdventureMessageDispatcher;
import dev.shiza.honey.adventure.message.formatter.AdventureMessageFormatter;
import java.time.Duration;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.Bukkit;
Expand Down Expand Up @@ -33,15 +34,22 @@ public void onPlayerJoin(final PlayerJoinEvent event) {
AdventureMessageDispatcher.createTitle()
.viewer(event.getPlayer())
.times(2, 4, 2)
.title(it -> it.template(defaultMessageFormatter, "Hello {{number}}!"))
.title(it -> it.template(defaultMessageFormatter, "Hello {{duration}}!"))
.subtitle(
it ->
it.template(
defaultMessageFormatter,
"It is a pleasure to see you there {{number}} {{player}}")
.placeholders(
mapping -> mapping.replace("player", event.getPlayer().getName())))
.placeholders(mapping -> mapping.replace("number", 15))
mapping ->
mapping
.replace("number", 5)
.replace("player", event.getPlayer().getName())))
.placeholders(
mapping ->
mapping.replace(
"duration",
Duration.ofDays(2).plus(Duration.ofHours(12).plus(Duration.ofMinutes(30)))))
.dispatch();

// 2) Using the reflective message formatter
Expand Down
12 changes: 10 additions & 2 deletions honey-test-plugin/src/dev/shiza/honey/ExamplePlugin.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import dev.shiza.honey.adventure.message.formatter.AdventureMessageFormatterFactory;
import dev.shiza.honey.adventure.placeholder.sanitizer.AdventurePlaceholderSanitizerFactory;
import dev.shiza.honey.conversion.ImplicitConversion;
import dev.shiza.honey.conversion.ImplicitConversionUnit;
import dev.shiza.honey.message.compiler.MessageCompiler;
import dev.shiza.honey.placeholder.PlaceholderContext;
import dev.shiza.honey.placeholder.evaluator.PlaceholderEvaluator;
Expand All @@ -15,10 +16,12 @@
import dev.shiza.honey.placeholder.resolver.PlaceholderResolver;
import dev.shiza.honey.placeholder.resolver.PlaceholderResolverFactory;
import dev.shiza.honey.placeholder.sanitizer.PlaceholderSanitizer;
import dev.shiza.honey.processor.ProcessorPhase;
import dev.shiza.honey.processor.ProcessorRegistry;
import dev.shiza.honey.processor.ProcessorRegistryFactory;
import eu.okaeri.configs.ConfigManager;
import eu.okaeri.configs.yaml.snakeyaml.YamlSnakeYamlConfigurer;
import java.time.Duration;
import net.kyori.adventure.text.Component;
import org.bukkit.plugin.java.JavaPlugin;

Expand Down Expand Up @@ -50,7 +53,9 @@ public void onEnable() {

private AdventureMessageFormatter createReflectMessageFormatter() {
final MessageCompiler<Component> messageCompiler = AdventureMessageCompilerFactory.create();
final ImplicitConversion implicitConversion = ImplicitConversion.create();
final ImplicitConversion implicitConversion =
ImplicitConversion.create(
ImplicitConversionUnit.unchecked(Duration.class, String.class, Duration::toString));
final PlaceholderContext placeholderContext = PlaceholderContext.create();
final PlaceholderResolver placeholderResolver = PlaceholderResolverFactory.create();
final PlaceholderSanitizer placeholderSanitizer =
Expand All @@ -60,7 +65,10 @@ private AdventureMessageFormatter createReflectMessageFormatter() {
final PlaceholderProcessor placeholderProcessor =
PlaceholderProcessorFactory.create(
placeholderEvaluator, placeholderSanitizer, implicitConversion);
final ProcessorRegistry processorRegistry = ProcessorRegistryFactory.create();
final ProcessorRegistry processorRegistry =
ProcessorRegistryFactory.create()
.processor(ProcessorPhase.PREPROCESS, content -> content + " {{player.getName}}")
.processor(ProcessorPhase.POSTPROCESS, content -> content + " {{player.getUniqueId}}");
return AdventureMessageFormatterFactory.create(
messageCompiler,
placeholderContext,
Expand Down

0 comments on commit 2b405dd

Please sign in to comment.