An experiment using a custom Annotation to repurpose Javadocs as a multiline String.
Copyright © 2023 Taeber Rapczak <taeber@rapczak.com>. License: MIT.
make
Building protobuf messages in Java is a bit tedious, especially when the message is complex and the values you're setting are few.
If I was given this template and asked to implement it:
subject: {
  id: ${agentId}
}
predicate: KNOWS
object: {
  name: ${fullname}
}
I'd probably write something like:
Expression expr =
  Expression.newBuilder()
    .setSubject(Thing.newBuilder().setId(agentId))
    .setPredicate(Predicate.KNOWS)
    .setObject(Thing.newBuilder().setName(fullname))
  .build();Honestly, not bad once you get used to it and if you have an ML-assisted editor, you probably wouldn't think twice.
I've dealt with much more complex messages and wished that I could just use
String.format with the template I was given, but alas there was no multiline
string literal support in Java before Java 13
(JEP-355), so it becomes:
String msg = String.format(
  "subject: {      " +
  "  id: %d        " +
  "}               " +
  "predicate: KNOWS" +
  "object: {       " +
  "  name: %s      " +
  "}               ",
  006, "James Bond");With the Annotation ProtobufTemplate and a custom Processor, you could
instead write:
/**
  subject: {
   id: ${agentId}
  }
  predicate: KNOWS
  object: {
    name: "${fullname}"
  }
 */
@ProtobufTemplate("AgentKnowsAgentTemplate")
private static Expression buildAgentKnowsAgent(long agentId, String fullname) {
  return AgentKnowsAgentTemplate.format(agentId, fullname);
}I originally had thought to generate the Builder code, but so far I'm still
using com.google.protobuf.TextFormat.
Here's some sample generated output:
// THIS FILE WAS GENERATED by ProtobufTemplateProcessor.
package com.rapczak.taeber.protobuf;
import com.google.protobuf.TextFormat;
final class AgentKnowsAgentTemplate {
  private static String msg = "subject: {\n       id: ${agentId}\n     }\n     predicate: KNOWS\n     object: {\n       name: \"${fullname}\"\n     }";
  public static com.rapczak.taeber.protobuf.Expression format(long agentId,java.lang.String fullname) {
    var builder = com.rapczak.taeber.protobuf.Expression.newBuilder();
    String[] placeholders = {"agentId","fullname"};
    String[] replacements = {String.valueOf(agentId),fullname.toString()};
    var txt = msg;
    for (var i = 0; i < placeholders.length; i++) {
      txt = txt.replace("${" + placeholders[i] + "}",                        replacements[i]);
    }
    try {TextFormat.getParser().merge(txt, builder);}
    catch (Exception e) {throw new RuntimeException(e);}
    return builder.build();
  }
  private AgentKnowsAgentTemplate() {}
}