From 54f3f2e86306d87dcd0ee35392c1d2c4c18e53b3 Mon Sep 17 00:00:00 2001 From: Peijun Ma Date: Fri, 10 Apr 2020 00:12:21 -0400 Subject: [PATCH] Upgrade stringtemplate to version 4 - stringtemplate3 depends on a super outdated version of antlr, and is causing this issue: https://github.com/47degrees/sbt-microsites/issues/457 - the `setAttributes` method in stringtemplate3 was always broken, as it goes down a different code path compared to `setAttribute` in stringtemplate3. See [setAttributes](https://github.com/antlr/stringtemplate3/blob/e60b23544539d64d2f57a87e87a5b383cc2b0ba9/src/org/antlr/stringtemplate/StringTemplate.java#L918) and [setAttribute](https://github.com/antlr/stringtemplate3/blob/e60b23544539d64d2f57a87e87a5b383cc2b0ba9/src/org/antlr/stringtemplate/StringTemplate.java#L570). stringtemplate4 removed the `setAttributes` method, so I had to write a custom function to handle the '.' characters in the properties. --- build.sbt | 4 +-- library/src/main/scala/template.scala | 39 +++++++++++++++++++++------ 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/build.sbt b/build.sbt index fe9d70d2..92884d5a 100644 --- a/build.sbt +++ b/build.sbt @@ -61,11 +61,11 @@ lazy val knockoffDeps = Def.setting { Seq( "org.foundweekends" %% "knockoff" % "0.8.6" )} val unfilteredVersion = "0.9.1" -val stringtemplateVersion = "3.2.1" +val stringtemplateVersion = "4.3" lazy val libraryDeps = Def.setting { Seq( "ws.unfiltered" %% "unfiltered-filter" % unfilteredVersion, "ws.unfiltered" %% "unfiltered-jetty" % unfilteredVersion, - "org.antlr" % "stringtemplate" % stringtemplateVersion + "org.antlr" % "ST4" % stringtemplateVersion )} val launcherInterfaceVersion = "1.1.2" val servletApiVersion = "3.1.0" diff --git a/library/src/main/scala/template.scala b/library/src/main/scala/template.scala index 8b17ede7..07136976 100644 --- a/library/src/main/scala/template.scala +++ b/library/src/main/scala/template.scala @@ -3,8 +3,10 @@ package pamflet import java.io.{ File,FileInputStream,InputStreamReader,StringReader} import java.nio.charset.Charset -import org.antlr.stringtemplate.{StringTemplate => STImpl} +import org.stringtemplate.v4.ST import collection.immutable.Map +import collection.JavaConverters._ +import scala.collection.immutable.Nil trait Template { /** Replace template values in input stream with bound properties */ @@ -25,25 +27,46 @@ case class StringTemplate(files: Seq[File], extra: Map[AnyRef, AnyRef]) extends Template { def apply(input: CharSequence) = if (!files.isEmpty) { - import collection.JavaConverters._ - val st = new STImpl - st.setTemplate(input.toString) - st.setAttributes((properties.asScala ++ extra).asJava) - st.toString + val st = new ST(input.toString, '$', '$') + (properties.asScala ++ extra).foreach { + case (key, value) => + pairToAttribute(key.toString(), value).fold( + pair => st.add(pair._1, pair._2), pair => st.add(pair._1, pair._2) + ) + } + st.render() } else input + private def pairToAttribute(key: String, value: Object): Either[(String, Object), (String, Map[String, Object])] = { + def pairToAttributeMap(first: String, rest: List[String], value: Object): Map[String, Object] = { + rest match { + case Nil => Map(first -> value) + case head :: tail => Map(first -> pairToAttributeMap(head, tail, value)) + } + } + + key.split('.').toList match { + case first :: second :: tail => Right(first -> pairToAttributeMap(second, tail, value)) + case _ => Left(key -> value) + } + } + private def properties = { val p = new java.util.Properties for (f <- files) { val q = new java.util.Properties q.load(new InputStreamReader(new FileInputStream(f), Charset.forName("UTF-8"))) - p.putAll(q) + q.asScala.foreach { + case (key, value) => p.put(key, value) + } } for (s <- str) { val q = new java.util.Properties q.load(new StringReader(s)) - p.putAll(q) + q.asScala.foreach { + case (key, value) => p.put(key, value) + } } p }