Fix linter errors and switch from Apache Velocity to Google Escapevelocity for templating (#163)

Internal tooling complains about any new Velocity usage
diff --git a/WORKSPACE b/WORKSPACE
index f2a36f7..3ac9188 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -31,9 +31,9 @@
 maven_install(
     artifacts = [
         "com.beust:jcommander:1.82",
+        "com.google.escapevelocity:escapevelocity:1.1",
         "com.google.guava:guava:31.1-jre",
         "com.google.truth:truth:1.1.3",
-        "org.apache.velocity:velocity:1.7",
         "junit:junit:4.13.2",
         # Artifacts below this line are only needed for building @io_bazel for
         # stardoc_binary.jar integration tests. They should be removed once we
@@ -61,6 +61,7 @@
         "commons-collections:commons-collections:3.2.2",
         "commons-lang:commons-lang:2.6",
         "org.apache.tomcat:tomcat-annotations-api:8.0.5",
+        "org.apache.velocity:velocity:1.7",
         "org.checkerframework:checker-qual:3.19.0",
     ],
     fail_if_repin_required = True,
diff --git a/maven_install.json b/maven_install.json
index 0ed8554..015c173 100644
--- a/maven_install.json
+++ b/maven_install.json
@@ -1,8 +1,8 @@
 {
     "dependency_tree": {
         "__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": "THERE_IS_NO_DATA_ONLY_ZUUL",
-        "__INPUT_ARTIFACTS_HASH": -568257785,
-        "__RESOLVED_ARTIFACTS_HASH": -1924638958,
+        "__INPUT_ARTIFACTS_HASH": -2046468587,
+        "__RESOLVED_ARTIFACTS_HASH": 1700352167,
         "conflict_resolution": {},
         "dependencies": [
             {
@@ -238,6 +238,30 @@
                 "url": "https://repo1.maven.org/maven2/com/google/errorprone/error_prone_type_annotations/2.18.0/error_prone_type_annotations-2.18.0.jar"
             },
             {
+                "coord": "com.google.escapevelocity:escapevelocity:1.1",
+                "dependencies": [
+                    "com.google.code.findbugs:jsr305:3.0.2",
+                    "com.google.errorprone:error_prone_annotations:2.18.0",
+                    "com.google.guava:failureaccess:1.0.1",
+                    "com.google.guava:guava:31.1-jre",
+                    "com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava",
+                    "com.google.j2objc:j2objc-annotations:1.3",
+                    "org.checkerframework:checker-qual:3.19.0"
+                ],
+                "directDependencies": [
+                    "com.google.guava:guava:31.1-jre"
+                ],
+                "file": "v1/https/repo1.maven.org/maven2/com/google/escapevelocity/escapevelocity/1.1/escapevelocity-1.1.jar",
+                "mirror_urls": [
+                    "https://repo1.maven.org/maven2/com/google/escapevelocity/escapevelocity/1.1/escapevelocity-1.1.jar"
+                ],
+                "packages": [
+                    "com.google.escapevelocity"
+                ],
+                "sha256": "37e76e4466836dedb864fb82355cd01c3bd21325ab642d89a0f759291b171231",
+                "url": "https://repo1.maven.org/maven2/com/google/escapevelocity/escapevelocity/1.1/escapevelocity-1.1.jar"
+            },
+            {
                 "coord": "com.google.flogger:flogger-system-backend:0.5.1",
                 "dependencies": [
                     "com.google.flogger:flogger:0.5.1",
diff --git a/src/main/java/com/google/devtools/build/skydoc/renderer/RendererMain.java b/src/main/java/com/google/devtools/build/skydoc/renderer/RendererMain.java
index 55cdd84..29ceb92 100644
--- a/src/main/java/com/google/devtools/build/skydoc/renderer/RendererMain.java
+++ b/src/main/java/com/google/devtools/build/skydoc/renderer/RendererMain.java
@@ -28,6 +28,7 @@
 import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.ProviderInfo;
 import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.RuleInfo;
 import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.StarlarkFunctionInfo;
+import com.google.protobuf.ExtensionRegistry;
 import com.google.protobuf.InvalidProtocolBufferException;
 import java.io.FileInputStream;
 import java.io.IOException;
@@ -40,13 +41,12 @@
  *
  * <p>This Renderer takes in raw stardoc_proto protos as input and produces rich markdown output.
  */
-public class RendererMain {
+public final class RendererMain {
 
   public static void main(String[] args) throws IOException {
 
     RendererOptions rendererOptions = new RendererOptions();
-    JCommander jcommander =
-        JCommander.newBuilder().addObject(rendererOptions).build();
+    JCommander jcommander = JCommander.newBuilder().addObject(rendererOptions).build();
     jcommander.setProgramName("renderer");
     jcommander.parse(args);
     if (rendererOptions.printHelp) {
@@ -77,7 +77,9 @@
             write("\n");
           }
         }) {
-      ModuleInfo moduleInfo = ModuleInfo.parseFrom(new FileInputStream(inputPath));
+      ModuleInfo moduleInfo =
+          ModuleInfo.parseFrom(
+              new FileInputStream(inputPath), ExtensionRegistry.getEmptyRegistry());
       printWriter.println(renderer.renderMarkdownHeader(moduleInfo));
       printRuleInfos(printWriter, renderer, moduleInfo.getRuleInfoList());
       printProviderInfos(printWriter, renderer, moduleInfo.getProviderInfoList());
@@ -181,4 +183,6 @@
       printWriter.println();
     }
   }
+
+  private RendererMain() {}
 }
diff --git a/src/main/java/com/google/devtools/build/skydoc/rendering/BUILD b/src/main/java/com/google/devtools/build/skydoc/rendering/BUILD
index 92c9021..0153417 100644
--- a/src/main/java/com/google/devtools/build/skydoc/rendering/BUILD
+++ b/src/main/java/com/google/devtools/build/skydoc/rendering/BUILD
@@ -21,7 +21,7 @@
     ),
     deps = [
         "//stardoc/proto:stardoc_output_java_proto",
+        "@maven//:com_google_escapevelocity_escapevelocity",
         "@maven//:com_google_guava_guava",
-        "@maven//:org_apache_velocity_velocity",
     ],
 )
diff --git a/src/main/java/com/google/devtools/build/skydoc/rendering/MarkdownRenderer.java b/src/main/java/com/google/devtools/build/skydoc/rendering/MarkdownRenderer.java
index 3a2085c..7f6abeb 100644
--- a/src/main/java/com/google/devtools/build/skydoc/rendering/MarkdownRenderer.java
+++ b/src/main/java/com/google/devtools/build/skydoc/rendering/MarkdownRenderer.java
@@ -16,27 +16,23 @@
 
 import static java.nio.charset.StandardCharsets.UTF_8;
 
+import com.google.common.collect.ImmutableMap;
 import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.AspectInfo;
 import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.ModuleInfo;
 import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.ProviderInfo;
 import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.RuleInfo;
 import com.google.devtools.build.skydoc.rendering.proto.StardocOutputProtos.StarlarkFunctionInfo;
+import com.google.escapevelocity.EvaluationException;
+import com.google.escapevelocity.ParseException;
+import com.google.escapevelocity.Template;
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.Reader;
-import java.io.StringWriter;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
-import org.apache.velocity.VelocityContext;
-import org.apache.velocity.app.VelocityEngine;
-import org.apache.velocity.exception.MethodInvocationException;
-import org.apache.velocity.exception.ParseErrorException;
-import org.apache.velocity.exception.ResourceNotFoundException;
-import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;
-import org.apache.velocity.runtime.resource.loader.JarResourceLoader;
 
 /** Produces skydoc output in markdown form. */
 public class MarkdownRenderer {
@@ -47,8 +43,6 @@
   private final String functionTemplateFilename;
   private final String aspectTemplateFilename;
 
-  private final VelocityEngine velocityEngine;
-
   public MarkdownRenderer(
       String headerTemplate,
       String ruleTemplate,
@@ -60,18 +54,6 @@
     this.providerTemplateFilename = providerTemplate;
     this.functionTemplateFilename = functionTemplate;
     this.aspectTemplateFilename = aspectTemplate;
-
-    this.velocityEngine = new VelocityEngine();
-    velocityEngine.setProperty("resource.loader", "classpath, jar");
-    velocityEngine.setProperty(
-        "classpath.resource.loader.class", ClasspathResourceLoader.class.getName());
-    velocityEngine.setProperty("jar.resource.loader.class", JarResourceLoader.class.getName());
-    velocityEngine.setProperty("input.encoding", "UTF-8");
-    velocityEngine.setProperty("output.encoding", "UTF-8");
-    velocityEngine.setProperty("runtime.references.strict", true);
-
-    // Ensure formatting is the same on Velocity 1.7 and 2.x.
-    velocityEngine.setProperty("parser.space_gobbling", "bc");
   }
 
   /**
@@ -79,18 +61,15 @@
    * summary for the input Starlark module.
    */
   public String renderMarkdownHeader(ModuleInfo moduleInfo) throws IOException {
-    VelocityContext context = new VelocityContext();
-    context.put("util", new MarkdownUtil());
-    context.put("moduleDocstring", moduleInfo.getModuleDocstring());
-
-    StringWriter stringWriter = new StringWriter();
+    ImmutableMap<String, Object> vars =
+        ImmutableMap.of(
+            "util", new MarkdownUtil(), "moduleDocstring", moduleInfo.getModuleDocstring());
     Reader reader = readerFromPath(headerTemplateFilename);
     try {
-      velocityEngine.evaluate(context, stringWriter, headerTemplateFilename, reader);
-    } catch (ResourceNotFoundException | ParseErrorException | MethodInvocationException e) {
+      return Template.parseFrom(reader).evaluate(vars);
+    } catch (ParseException | EvaluationException e) {
       throw new IOException(e);
     }
-    return stringWriter.toString();
   }
 
   /**
@@ -98,19 +77,14 @@
    * the given rule name.
    */
   public String render(String ruleName, RuleInfo ruleInfo) throws IOException {
-    VelocityContext context = new VelocityContext();
-    context.put("util", new MarkdownUtil());
-    context.put("ruleName", ruleName);
-    context.put("ruleInfo", ruleInfo);
-
-    StringWriter stringWriter = new StringWriter();
+    ImmutableMap<String, Object> vars =
+        ImmutableMap.of("util", new MarkdownUtil(), "ruleName", ruleName, "ruleInfo", ruleInfo);
     Reader reader = readerFromPath(ruleTemplateFilename);
     try {
-      velocityEngine.evaluate(context, stringWriter, ruleTemplateFilename, reader);
-    } catch (ResourceNotFoundException | ParseErrorException | MethodInvocationException e) {
+      return Template.parseFrom(reader).evaluate(vars);
+    } catch (ParseException | EvaluationException e) {
       throw new IOException(e);
     }
-    return stringWriter.toString();
   }
 
   /**
@@ -118,19 +92,15 @@
    * object with the given name.
    */
   public String render(String providerName, ProviderInfo providerInfo) throws IOException {
-    VelocityContext context = new VelocityContext();
-    context.put("util", new MarkdownUtil());
-    context.put("providerName", providerName);
-    context.put("providerInfo", providerInfo);
-
-    StringWriter stringWriter = new StringWriter();
+    ImmutableMap<String, Object> vars =
+        ImmutableMap.of(
+            "util", new MarkdownUtil(), "providerName", providerName, "providerInfo", providerInfo);
     Reader reader = readerFromPath(providerTemplateFilename);
     try {
-      velocityEngine.evaluate(context, stringWriter, providerTemplateFilename, reader);
-    } catch (ResourceNotFoundException | ParseErrorException | MethodInvocationException e) {
+      return Template.parseFrom(reader).evaluate(vars);
+    } catch (ParseException | EvaluationException e) {
       throw new IOException(e);
     }
-    return stringWriter.toString();
   }
 
   /**
@@ -138,18 +108,14 @@
    * object.
    */
   public String render(StarlarkFunctionInfo functionInfo) throws IOException {
-    VelocityContext context = new VelocityContext();
-    context.put("util", new MarkdownUtil());
-    context.put("funcInfo", functionInfo);
-
-    StringWriter stringWriter = new StringWriter();
+    ImmutableMap<String, Object> vars =
+        ImmutableMap.of("util", new MarkdownUtil(), "funcInfo", functionInfo);
     Reader reader = readerFromPath(functionTemplateFilename);
     try {
-      velocityEngine.evaluate(context, stringWriter, functionTemplateFilename, reader);
-    } catch (ResourceNotFoundException | ParseErrorException | MethodInvocationException e) {
+      return Template.parseFrom(reader).evaluate(vars);
+    } catch (ParseException | EvaluationException e) {
       throw new IOException(e);
     }
-    return stringWriter.toString();
   }
 
   /**
@@ -157,20 +123,17 @@
    * with the given aspect name.
    */
   public String render(String aspectName, AspectInfo aspectInfo) throws IOException {
-    VelocityContext context = new VelocityContext();
-    context.put("util", new MarkdownUtil());
-    context.put("aspectName", aspectName);
-    context.put("aspectInfo", aspectInfo);
-
-    StringWriter stringWriter = new StringWriter();
+    ImmutableMap<String, Object> vars =
+        ImmutableMap.of(
+            "util", new MarkdownUtil(), "aspectName", aspectName, "aspectInfo", aspectInfo);
     Reader reader = readerFromPath(aspectTemplateFilename);
     try {
-      velocityEngine.evaluate(context, stringWriter, aspectTemplateFilename, reader);
-    } catch (ResourceNotFoundException | ParseErrorException | MethodInvocationException e) {
+      return Template.parseFrom(reader).evaluate(vars);
+    } catch (ParseException | EvaluationException e) {
       throw new IOException(e);
     }
-    return stringWriter.toString();
   }
+
   /**
    * Returns a reader from the given path.
    *
@@ -179,7 +142,7 @@
   private static Reader readerFromPath(String filePath) throws IOException {
     if (Files.exists(Paths.get(filePath))) {
       Path path = Paths.get(filePath);
-      return Files.newBufferedReader(path);
+      return Files.newBufferedReader(path, UTF_8);
     }
 
     InputStream inputStream = MarkdownRenderer.class.getClassLoader().getResourceAsStream(filePath);
diff --git a/src/main/java/com/google/devtools/build/skydoc/rendering/MarkdownUtil.java b/src/main/java/com/google/devtools/build/skydoc/rendering/MarkdownUtil.java
index 4dd6310..69c6e9f 100644
--- a/src/main/java/com/google/devtools/build/skydoc/rendering/MarkdownUtil.java
+++ b/src/main/java/com/google/devtools/build/skydoc/rendering/MarkdownUtil.java
@@ -14,7 +14,9 @@
 
 package com.google.devtools.build.skydoc.rendering;
 
+import static com.google.common.collect.ImmutableList.toImmutableList;
 import static java.util.Comparator.naturalOrder;
+import static java.util.stream.Collectors.joining;
 
 import com.google.common.base.Joiner;
 import com.google.common.collect.ImmutableList;
@@ -29,7 +31,6 @@
 import java.util.ArrayList;
 import java.util.List;
 import java.util.regex.Pattern;
-import java.util.stream.Collectors;
 
 /** Contains a number of utility methods for markdown rendering. */
 public final class MarkdownUtil {
@@ -116,30 +117,26 @@
   /**
    * Return a string representing the rule summary for the given rule with the given name.
    *
-   * For example: 'my_rule(foo, bar)'.
-   * The summary will contain hyperlinks for each attribute.
+   * <p>For example: 'my_rule(foo, bar)'. The summary will contain hyperlinks for each attribute.
    */
   @SuppressWarnings("unused") // Used by markdown template.
   public String ruleSummary(String ruleName, RuleInfo ruleInfo) {
     List<String> attributeNames =
-        ruleInfo.getAttributeList().stream()
-            .map(AttributeInfo::getName)
-            .collect(Collectors.toList());
+        ruleInfo.getAttributeList().stream().map(AttributeInfo::getName).collect(toImmutableList());
     return summary(ruleName, attributeNames);
   }
 
   /**
    * Return a string representing the summary for the given provider with the given name.
    *
-   * For example: 'MyInfo(foo, bar)'.
-   * The summary will contain hyperlinks for each field.
+   * <p>For example: 'MyInfo(foo, bar)'. The summary will contain hyperlinks for each field.
    */
   @SuppressWarnings("unused") // Used by markdown template.
   public String providerSummary(String providerName, ProviderInfo providerInfo) {
     List<String> fieldNames =
         providerInfo.getFieldInfoList().stream()
             .map(field -> field.getName())
-            .collect(Collectors.toList());
+            .collect(toImmutableList());
     return summary(providerName, fieldNames);
   }
 
@@ -153,7 +150,7 @@
     List<String> attributeNames =
         aspectInfo.getAttributeList().stream()
             .map(AttributeInfo::getName)
-            .collect(Collectors.toList());
+            .collect(toImmutableList());
     return summary(aspectName, attributeNames);
   }
 
@@ -167,7 +164,7 @@
     List<String> paramNames =
         funcInfo.getParameterList().stream()
             .map(FunctionParamInfo::getName)
-            .collect(Collectors.toList());
+            .collect(toImmutableList());
     return summary(funcInfo.getFunctionName(), paramNames);
   }
 
@@ -178,7 +175,7 @@
       String paramLinksLine =
           params.stream()
               .map(param -> String.format("<a href=\"#%s-%s\">%s</a>", functionName, param, param))
-              .collect(Collectors.joining(", "));
+              .collect(joining(", "));
       paramLinksLines.add(paramLinksLine);
     }
     String paramList =