home / connvers / src / main / java / avividi / connvers / protocol / BasicMessageEncoder.java

BasicMessageEncoder.java



package avividi.connvers.protocol;

import java.time.Instant;

import static avividi.connvers.Connvers.MAX_LENGTH_USER_NAME;
import static avividi.connvers.Connvers.MAX_MESSAGE_LENGTH;
import static avividi.connvers.Connvers.SERVER_USER;
import static avividi.connvers.Connvers.SYSTEM_USER;

public class BasicMessageEncoder implements MessageEncoder {

  public static String PROTOCOL_NAME = "connvers-protocol-v1";

  @Override
  public String encode(Message message) {
    String template = """
        %s
        user=%s
        time=%s
        topic=%s
        %s""";

    return String.format(
        template,
        PROTOCOL_NAME,
        message.user(),
        message.time(),
        message.topic(),
        message.content() == null ? "" : message.content());
  }

  @Override
  public Message decode(String raw) throws ValidationException {
    String[] lines = raw.split("\n");
    if (lines.length < 4) {
      throw new ValidationException();
    }
    String protocolLine = lines[0];
    ValidationException.require(protocolLine.equals(PROTOCOL_NAME), () -> "invalid protocol");

    String userLine = lines[1];
    ValidationException.require(userLine.startsWith("user="), () -> "invalid user");

    String user = userLine.substring("user=".length());


    String timestampLine = lines[2];
    ValidationException.require(timestampLine.startsWith("time="), () -> "invalid time");
    Instant time;
    try {
      time =Instant.parse(timestampLine.substring("time=".length()));
    } catch (Exception e) {
      throw new ValidationException("invalid time");
    }

    String topicLine = lines[3];
    ValidationException.require(topicLine.startsWith("topic="), () -> "invalid topic");
    String topicString = lines[3].substring("topic=".length());
    MessageTopic topic;
    try {
      topic = MessageTopic.valueOf(topicString);
    } catch (Exception e) {
      throw new ValidationException("invalid topic");
    }
    if (topic == MessageTopic.welcome || topic == MessageTopic.error) {
      ValidationException.require(user.equals(SERVER_USER), () -> String.format("invalid user name <%s>", user));
    } else if (topic == MessageTopic.join || topic == MessageTopic.heartbeat) {
      ValidationException.require(!user.equals(SERVER_USER), () -> "invalid user name");
      ValidationException.require(!user.equals(SYSTEM_USER), () -> "invalid user name");
    }

    String content = null;
    if (topic == MessageTopic.say || topic == MessageTopic.error) {
      ValidationException.require(lines.length >= 5, () -> "missing content ");
      content = lines[4];

      ValidationException.require(content.length() <= MAX_MESSAGE_LENGTH,
          () -> String.format("invalid message (longer than %d)", MAX_MESSAGE_LENGTH));
    }

    return new Message(user, time, topic, content);
  }

  public static void validateUserName(String user) throws ValidationException {
    ValidationException.require(user.replaceAll("\\w","").isEmpty(),
        () -> "invalid user name (only alphanumerics allowed)");
    ValidationException.require(user.length() <= MAX_LENGTH_USER_NAME,
        () -> String.format("invalid user name (longer than %d)", MAX_LENGTH_USER_NAME));
    ValidationException.require(user.length() > 1,
        () -> "invalid user name (too short)");

  }
}