PlaceHolderManager.java

package dev.aura.bungeechat.api.placeholder;

import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import lombok.experimental.UtilityClass;

@UtilityClass
public class PlaceHolderManager {
  public static final char placeholderChar = '%';
  private static final List<BungeeChatPlaceHolder> placeholders = new LinkedList<>();

  public static Stream<BungeeChatPlaceHolder> getPlaceholderStream() {
    return placeholders.stream();
  }

  public static Stream<BungeeChatPlaceHolder> getApplicableStream(BungeeChatContext context) {
    return getPlaceholderStream().filter(placeholder -> placeholder.isContextApplicable(context));
  }

  public static String processMessage(String message, BungeeChatContext context) {
    final StringBuilder builder = new StringBuilder();
    final List<BungeeChatPlaceHolder> placeholders =
        getApplicableStream(context).collect(Collectors.toList());

    processMessageInternal(message, context, builder, placeholders);

    return builder.toString();
  }

  private static void processMessageInternal(
      String message,
      BungeeChatContext context,
      StringBuilder builder,
      List<BungeeChatPlaceHolder> placeholders) {
    boolean encounteredPlaceholder = false;
    StringBuilder placeholderName = null;

    for (char c : message.toCharArray()) {
      if (c == placeholderChar) {
        if (encounteredPlaceholder) {
          // We only need to do stuff if we are finding the end of the placeholder
          if (placeholderName == null) {
            // We just found the delimiter twice (this is an escape sequence). Add it to the buffer
            // once
            builder.append(placeholderChar);
          } else {
            // Render the placeholderName and delete the builder
            final String placeholderNameStr = placeholderName.toString();
            placeholderName = null;

            Optional<BungeeChatPlaceHolder> placeholder =
                placeholders.stream().filter(p -> p.matchesName(placeholderNameStr)).findFirst();

            if (placeholder.isPresent()) {
              // Apply the placeholder
              final String placeholderReplacement =
                  placeholder.get().getReplacement(placeholderNameStr, context);

              // Apply placeholders to that (note that appends any normal string parts)
              processMessageInternal(placeholderReplacement, context, builder, placeholders);
            } else {
              // Placeholder not found, let's add it to the output verbatim (with the delimiter
              // surrounding it)
              builder.append(placeholderChar).append(placeholderNameStr).append(placeholderChar);
            }
          }
        }

        // Toggle the state
        encounteredPlaceholder = !encounteredPlaceholder;
      } else {
        if (encounteredPlaceholder) {
          // We're parsing the name of the placeholder
          if (placeholderName == null) {
            // Create the instance for the builder if necessary
            placeholderName = new StringBuilder();
          }

          placeholderName.append(c);
        } else {
          // Just a normal char. Append it to the buffer
          builder.append(c);
        }
      }
    }

    if (encounteredPlaceholder) {
      builder.append(placeholderChar);

      if (placeholderName != null) {
        builder.append(placeholderName);
      }
    }
  }

  public static void registerPlaceholder(BungeeChatPlaceHolder... placeholder) {
    for (BungeeChatPlaceHolder p : placeholder) {
      registerPlaceholder(p);
    }
  }

  public static void registerPlaceholder(BungeeChatPlaceHolder placeholder) {
    if (placeholders.contains(placeholder))
      throw new IllegalArgumentException(
          "Placeholder " + placeholder.getName() + " has already been registered!");

    placeholders.add(placeholder);
  }

  public static void clear() {
    placeholders.clear();
  }
}