Skip to content

Commit

Permalink
capture java and scala wrapper supporting methods in proper classes
Browse files Browse the repository at this point in the history
to facilitate validation at compile time
  • Loading branch information
carueda committed Jul 28, 2019
1 parent 4204db6 commit 6f7ec70
Show file tree
Hide file tree
Showing 10 changed files with 274 additions and 165 deletions.
1 change: 1 addition & 0 deletions .gitignore
@@ -1,3 +1,4 @@
src/main/resources/codeTemplates/
### Scala template
*.class
*.log
Expand Down
14 changes: 13 additions & 1 deletion build.sbt
@@ -1,4 +1,4 @@
lazy val tscfgVersion = setVersion("0.9.91")
lazy val tscfgVersion = setVersion("0.9.92")

organization := "com.github.carueda"
name := "tscfg"
Expand All @@ -25,6 +25,18 @@ coverageMinimum := 80
coverageFailOnMinimum := false
coverageHighlighting := { scalaBinaryVersion.value == "2.11" }

lazy val codeTemplates = taskKey[Unit]("Copies CodeTemplate sources to resources/")
codeTemplates := {
println(s"Copying code templates")
IO.write(file("src/main/resources/codeTemplates/JavaCodeTemplates"),
IO.read(file("src/main/java/tscfg/codeTemplates/JavaCodeTemplates.java"))
)
IO.write(file("src/main/resources/codeTemplates/ScalaCodeTemplates"),
IO.read(file("src/main/scala/tscfg/codeTemplates/ScalaCodeTemplates.scala"))
)
}
(compile in Compile) <<= (compile in Compile) dependsOn codeTemplates

lazy val genCode = taskKey[Unit]("Generate classes for tests")
fullRunTask(genCode, Compile, "tscfg.gen4tests")
fork in genCode := true
Expand Down
5 changes: 5 additions & 0 deletions changelog.md
@@ -1,3 +1,8 @@
2019-07-27 - 0.9.92

- capture java and scala wrapper supporting methods in proper classes
to facilitate validation at compile time.

2019-07-22 - 0.9.91

- set openjdk8 for travis ci
Expand Down
79 changes: 79 additions & 0 deletions src/main/java/tscfg/codeTemplates/JavaCodeTemplates.java
@@ -0,0 +1,79 @@
package tscfg.codeTemplates;

/**
* Captures various snippets to be included in the generated wrapper.
* This is not used as compile code in the generator itself but the
* text of this file is retrieved as a resource at runtime.
* Capturing this as code helps with validation at compile time.
*/
public class JavaCodeTemplates {

///////////////////////////////////////////////////////////////////////
// definition of methods used to access list's elements of basic type:

//<$_bln>
private static java.lang.Boolean $_bln(com.typesafe.config.ConfigValue cv) {
java.lang.Object u = cv.unwrapped();
if (cv.valueType() != com.typesafe.config.ConfigValueType.BOOLEAN ||
!(u instanceof java.lang.Boolean)) throw $_expE(cv, "boolean");
return (java.lang.Boolean) u;
}
//</$_bln>

//<$_dbl>
private static java.lang.Double $_dbl(com.typesafe.config.ConfigValue cv) {
java.lang.Object u = cv.unwrapped();
if (cv.valueType() != com.typesafe.config.ConfigValueType.NUMBER ||
!(u instanceof java.lang.Number)) throw $_expE(cv, "double");
return ((java.lang.Number) u).doubleValue();
}
//</$_dbl>

//<$_int>
private static java.lang.Integer $_int(com.typesafe.config.ConfigValue cv) {
java.lang.Object u = cv.unwrapped();
if (cv.valueType() != com.typesafe.config.ConfigValueType.NUMBER ||
!(u instanceof java.lang.Integer)) throw $_expE(cv, "integer");
return (java.lang.Integer) u;
}
//</$_int>

//<$_lng>
private static java.lang.Long $_lng(com.typesafe.config.ConfigValue cv) {
java.lang.Object u = cv.unwrapped();
if (cv.valueType() != com.typesafe.config.ConfigValueType.NUMBER ||
!(u instanceof java.lang.Long) && !(u instanceof java.lang.Integer)) throw $_expE(cv, "long");
return ((java.lang.Number) u).longValue();
}
//</$_lng>

//<$_str>
private static java.lang.String $_str(com.typesafe.config.ConfigValue cv) {
return java.lang.String.valueOf(cv.unwrapped());
}
//</$_str>

// $_siz: since there's no something like cv.getBytes() nor is SimpleConfig.parseBytes visible,
// use ConfigFactory.parseString:
//<$_siz>
private static java.lang.Long $_siz(com.typesafe.config.ConfigValue cv) {
java.lang.Object u = cv.unwrapped();
if (cv.valueType() == com.typesafe.config.ConfigValueType.NUMBER ||
(u instanceof java.lang.Long) || (u instanceof java.lang.Integer))
return ((java.lang.Number) u).longValue();
if (cv.valueType() == com.typesafe.config.ConfigValueType.STRING) {
return com.typesafe.config.ConfigFactory.parseString("s = " + '"' + u + '"').getBytes("s");
}
throw $_expE(cv, "size");
}
//</$_siz>

//<$_expE>
private static java.lang.RuntimeException $_expE(com.typesafe.config.ConfigValue cv, java.lang.String exp) {
java.lang.Object u = cv.unwrapped();
return new java.lang.RuntimeException(cv.origin().lineNumber()
+ ": expecting: " +exp + " got: " + (u instanceof java.lang.String ? "\"" +u+ "\"" : u));
}
//</$_expE>

}
2 changes: 1 addition & 1 deletion src/main/resources/application.conf
@@ -1 +1 @@
tscfg.version = 0.9.91
tscfg.version = 0.9.92
88 changes: 88 additions & 0 deletions src/main/scala/tscfg/codeTemplates/ScalaCodeTemplates.scala
@@ -0,0 +1,88 @@
package tscfg.codeTemplates

/**
* Captures various snippets to be included in the generated wrapper.
* This is not used as compile code in the generator itself but the
* text of this file is retrieved as a resource at runtime.
* Capturing this as code helps with validation at compile time.
*/
object ScalaCodeTemplates {

///////////////////////////////////////////////////////////////////////
// definition of methods used to access list's elements of basic type:

//<$_bln>
private def $_bln(cv:com.typesafe.config.ConfigValue): scala.Boolean = {
val u: Any = cv.unwrapped
if ((cv.valueType != com.typesafe.config.ConfigValueType.BOOLEAN) ||
!u.isInstanceOf[java.lang.Boolean]) throw $_expE(cv, "boolean")
u.asInstanceOf[java.lang.Boolean].booleanValue()
}
//</$_bln>

//<$_dbl>
private def $_dbl(cv:com.typesafe.config.ConfigValue): scala.Double = {
val u: Any = cv.unwrapped
if ((cv.valueType != com.typesafe.config.ConfigValueType.NUMBER) ||
!u.isInstanceOf[java.lang.Number]) throw $_expE(cv, "double")
u.asInstanceOf[java.lang.Number].doubleValue()
}
//</$_dbl>

//<$_int>
private def $_int(cv:com.typesafe.config.ConfigValue): scala.Int = {
val u: Any = cv.unwrapped
if ((cv.valueType != com.typesafe.config.ConfigValueType.NUMBER) ||
!u.isInstanceOf[Integer]) throw $_expE(cv, "integer")
u.asInstanceOf[Integer]
}
//</$_int>

//<$_lng>
private def $_lng(cv:com.typesafe.config.ConfigValue): scala.Long = {
val u: Any = cv.unwrapped
if ((cv.valueType != com.typesafe.config.ConfigValueType.NUMBER) ||
!u.isInstanceOf[java.lang.Integer] && !u.isInstanceOf[java.lang.Long]) throw $_expE(cv, "long")
u.asInstanceOf[java.lang.Number].longValue()
}
//</$_lng>

//<$_str>
private def $_str(cv:com.typesafe.config.ConfigValue) = {
java.lang.String.valueOf(cv.unwrapped())
}
//</$_str>

//<$_siz>
private def $_siz(cv:com.typesafe.config.ConfigValue): scala.Long = {
val u: Any = cv.unwrapped
if (cv.valueType() == com.typesafe.config.ConfigValueType.NUMBER ||
u.isInstanceOf[scala.Long] || u.isInstanceOf[scala.Int])
u.asInstanceOf[java.lang.Number].longValue()
else if (cv.valueType() == com.typesafe.config.ConfigValueType.STRING) {
com.typesafe.config.ConfigFactory.parseString("s = " + '"' + u + '"').getBytes("s")
}
else throw $_expE(cv, "size")
}
//</$_siz>

//<$_expE>
private def $_expE(cv:com.typesafe.config.ConfigValue, exp:java.lang.String) = {
val u: Any = cv.unwrapped
new java.lang.RuntimeException(cv.origin.lineNumber +
": expecting: " + exp + " got: " +
(if (u.isInstanceOf[java.lang.String]) "\"" + u + "\"" else u))
}
//</$_expE>

//<$_require>
def $_require[T](parentPath: java.lang.String, path: java.lang.String)(get: T): T = {
import com.typesafe.config.ConfigException.Missing
try get
catch {
case e: Missing throw if (parentPath.isEmpty) e else new Missing(parentPath + path, e)
}
}
//</$_require>

}
47 changes: 47 additions & 0 deletions src/main/scala/tscfg/codeTemplates/package.scala
@@ -0,0 +1,47 @@
package tscfg

import java.util.regex.Pattern

import scala.util.control.NonFatal

package object codeTemplates {
private val beginTemplatePattern = Pattern.compile("\\s*//<([^>]+)>.*$")

private val javaMap = getMap("codeTemplates/JavaCodeTemplates")
private val scalaMap = getMap("codeTemplates/ScalaCodeTemplates")

def getJavaTemplate(key: String): String = javaMap(key)

def getScalaTemplate(key: String): String = scalaMap(key)

private def getMap(templateName: String): Map[String, String] = try {
//println(s"codeTemplates.getMap $templateName")
val map = collection.mutable.HashMap[String, String]()
val is = getClass.getClassLoader.getResourceAsStream(templateName)
assert(is != null)
val source = io.Source.fromInputStream(is, "utf-8")
assert(source != null)
var key: String = null
val template = new StringBuilder
for (line source.getLines()) {
if (key == null) {
val m = beginTemplatePattern.matcher(line)
if (m.find) {
key = m.group(1)
}
}
else if (line.contains("//</" + key + ">")) {
map.update(key, template.toString)
key = null
template.setLength(0)
}
else template.append(line).append("\n")
}
is.close()
map.toMap
}
catch {
case NonFatal(ex)
throw new RuntimeException("Unexpected exception. Please report this bug.", ex)
}
}
4 changes: 2 additions & 2 deletions src/main/scala/tscfg/gen4tests.scala
Expand Up @@ -13,7 +13,7 @@ object gen4tests {
}

private def generate(confFile: File): Unit = {
println(s"gen4tests: confFile=$confFile")
//println(s"gen4tests: confFile=$confFile")
val source = io.Source.fromFile(confFile).mkString.trim

val baseGenOpts: GenOpts = {
Expand Down Expand Up @@ -56,7 +56,7 @@ object gen4tests {
val fileName = className + "." + lang.toLowerCase
val targetFile = new File(targetScalaDir, fileName)
// $COVERAGE-OFF$
if (true || confFile.lastModified >= targetFile.lastModified) {
if (confFile.lastModified >= targetFile.lastModified) {
val genOpts = baseGenOpts.copy(className = className)
println(s"generating for $name -> $fileName")
val generator: Generator = lang match {
Expand Down
96 changes: 18 additions & 78 deletions src/main/scala/tscfg/generators/java/JavaGen.scala
Expand Up @@ -404,95 +404,35 @@ private[java] object defs {
definition: String = "")
}

private[java] case class MethodNames(prefix: String = "$_") {
val strA = prefix + "str"
val intA = prefix + "int"
val lngA = prefix + "lng"
val dblA = prefix + "dbl"
val blnA = prefix + "bln"
val durA = prefix + "dur"
val sizA = prefix + "siz"
val expE = prefix + "expE"
val listPrefix = prefix + "L"
private[java] case class MethodNames() {
val strA = "$_str"
val intA = "$_int"
val lngA = "$_lng"
val dblA = "$_dbl"
val blnA = "$_bln"
val durA = "$_dur" // TODO review this one as it's not actually used
val sizA = "$_siz"
val expE = "$_expE"
val listPrefix = "$_L"

def checkUserSymbol(symbol: String): Unit = {
if (symbol.startsWith(prefix))
if (symbol.startsWith("$_"))
println(
s"""
|WARNING: Symbol $symbol may cause conflict with generated code.
| Avoid the $prefix prefix in your spec's identifiers.
| Avoid the $$_ prefix in your spec's identifiers.
""".stripMargin
)
}

import tscfg.codeTemplates.getJavaTemplate

// definition of methods used to access list's elements of basic type
val basicElemAccessDefinition: Map[String, String] = {
Map(
strA s"""
|private static java.lang.String $strA(com.typesafe.config.ConfigValue cv) {
| return java.lang.String.valueOf(cv.unwrapped());
|}""".stripMargin.trim,

intA s"""
|private static java.lang.Integer $intA(com.typesafe.config.ConfigValue cv) {
| java.lang.Object u = cv.unwrapped();
| if (cv.valueType() != com.typesafe.config.ConfigValueType.NUMBER ||
| !(u instanceof java.lang.Integer)) throw $expE(cv, "integer");
| return (java.lang.Integer) u;
|}
|""".stripMargin.trim,

lngA s"""
|private static java.lang.Long $lngA(com.typesafe.config.ConfigValue cv) {
| java.lang.Object u = cv.unwrapped();
| if (cv.valueType() != com.typesafe.config.ConfigValueType.NUMBER ||
| !(u instanceof java.lang.Long) && !(u instanceof java.lang.Integer)) throw $expE(cv, "long");
| return ((java.lang.Number) u).longValue();
|}
|""".stripMargin.trim,

// since there's no something like cv.getBytes() nor is SimpleConfig.parseBytes visible,
// use ConfigFactory.parseString:
sizA s"""
|private static java.lang.Long $sizA(com.typesafe.config.ConfigValue cv) {
| java.lang.Object u = cv.unwrapped();
| if (cv.valueType() == com.typesafe.config.ConfigValueType.NUMBER ||
| (u instanceof java.lang.Long) || (u instanceof java.lang.Integer))
| return ((java.lang.Number) u).longValue();
| if (cv.valueType() == com.typesafe.config.ConfigValueType.STRING) {
| return com.typesafe.config.ConfigFactory.parseString("s = " + '"' + u + '"').getBytes("s");
| }
| throw $expE(cv, "size");
|}
|""".stripMargin.trim,

dblA s"""
|private static java.lang.Double $dblA(com.typesafe.config.ConfigValue cv) {
| java.lang.Object u = cv.unwrapped();
| if (cv.valueType() != com.typesafe.config.ConfigValueType.NUMBER ||
| !(u instanceof java.lang.Number)) throw $expE(cv, "double");
| return ((java.lang.Number) u).doubleValue();
|}
|""".stripMargin.trim,

blnA s"""
|private static java.lang.Boolean $blnA(com.typesafe.config.ConfigValue cv) {
| java.lang.Object u = cv.unwrapped();
| if (cv.valueType() != com.typesafe.config.ConfigValueType.BOOLEAN ||
| !(u instanceof java.lang.Boolean)) throw $expE(cv, "boolean");
| return (java.lang.Boolean) u;
|}
|""".stripMargin.trim
)
List(strA, intA, lngA, dblA, blnA, sizA)
.map(k k getJavaTemplate(k).trim)
.toMap
}

val expEDef: String = {
s"""
|private static java.lang.RuntimeException $expE(com.typesafe.config.ConfigValue cv, java.lang.String exp) {
| java.lang.Object u = cv.unwrapped();
| return new java.lang.RuntimeException(cv.origin().lineNumber()
| + ": expecting: " +exp + " got: " + (u instanceof java.lang.String ? "\\"" +u+ "\\"" : u));
|}
|""".stripMargin.trim
}
val expEDef: String = getJavaTemplate("$_expE").trim
}

0 comments on commit 6f7ec70

Please sign in to comment.