• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

carueda / tscfg / 10572324896

27 Aug 2024 05:35AM UTC coverage: 96.742% (+0.1%) from 96.633%
10572324896

push

carueda
minor code cleanup

1 of 2 new or added lines in 2 files covered. (50.0%)

1 existing line in 1 file now uncovered.

861 of 890 relevant lines covered (96.74%)

1.83 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

99.05
/src/main/scala/tscfg/generators/java/JavaGen.scala
1
package tscfg.generators.java
2

3
import tscfg.DefineCase.ExtendsDefineCase
4
import tscfg.ModelBuilder
5
import tscfg.codeDefs.javaDef
6
import tscfg.exceptions.ObjectDefinitionException
7
import tscfg.generators._
8
import tscfg.generators.java.javaUtil._
9
import tscfg.model._
10
import tscfg.ns.NamespaceMan
11
import tscfg.util.escapeString
12

13
class JavaGen(
14
    genOpts: GenOpts,
15
    rootNamespace: NamespaceMan = new NamespaceMan
16
) extends Generator(genOpts) {
17

18
  import defs._
19

20
  implicit val methodNames: MethodNames = MethodNames()
2✔
21

22
  def generate(objectType: ObjectType): GenResult = {
23
    genResults = GenResult()
2✔
24

25
    methodNames.checkUserSymbol(className)
2✔
26
    val res = generateForObj(objectType, className = className, isRoot = true)
2✔
27

28
    val packageStr = s"package ${genOpts.packageName};\n\n"
29

30
    val definition = (packageStr + res.definition).trim
2✔
31
    genResults.copy(code = definition)
2✔
32
  }
33

34
  private def generate(
35
      typ: Type,
36
      classNamePrefixOpt: Option[String],
37
      className: String,
38
      annTypeForAbstractClassName: Option[AnnType] = None
39
  ): Res = typ match {
40

41
    case et: EnumObjectType =>
42
      generateForEnum(et, classNamePrefixOpt, className)
1✔
43

44
    case ot: ObjectType =>
45
      generateForObj(
2✔
46
        ot,
47
        classNamePrefixOpt,
48
        className,
49
        annTypeForAbstractClassName = annTypeForAbstractClassName
50
      )
51

52
    case aot: AbstractObjectType =>
53
      generateForObj(
2✔
54
        aot,
55
        classNamePrefixOpt,
56
        className,
57
        annTypeForAbstractClassName = annTypeForAbstractClassName
58
      );
59

60
    case ort: ObjectRefType => generateForObjRef(ort)
2✔
61

62
    case lt: ListType => generateForList(lt, classNamePrefixOpt, className)
2✔
63

64
    case bt: BasicType => generateForBasic(bt)
2✔
65
  }
66

67
  private def generateForObj(
68
      ot: ObjectRealType,
69
      classNamePrefixOpt: Option[String] = None,
70
      className: String,
71
      isRoot: Boolean = false,
72
      annTypeForAbstractClassName: Option[AnnType] = None
73
  ): Res = {
74

75
    val classNameAdjusted = adjustClassName(className)
2✔
76
    genResults =
2✔
77
      genResults.copy(classNames = genResults.classNames + classNameAdjusted)
2✔
78

79
    val symbols = ot.members.keys.toList.sorted
2✔
80
    symbols.foreach(methodNames.checkUserSymbol)
2✔
81

82
    val results = symbols.map { symbol =>
2✔
83
      val a = ot.members(symbol)
2✔
84
      val res = generate(
2✔
85
        a.t,
2✔
86
        classNamePrefixOpt = Some(classNameAdjusted + "."),
2✔
87
        className = javaUtil.getClassName(symbol),
2✔
88
        annTypeForAbstractClassName = Some(a)
2✔
89
      )
90
      (symbol, res, a)
2✔
91
    }
92

93
    val classDeclMembers = results.map { case (symbol, res, a) =>
2✔
94
      val memberType = res.javaType
2✔
95
      val typeFull = if (a.optional && a.default.isEmpty) {
2✔
96
        val typeActual = a.t match {
2✔
97
          case ObjectRefType(_, simpleName) => simpleName
2✔
98
          case _                            => toObjectType(memberType).toString
2✔
99
        }
100

101
        if (genOpts.useOptionals)
2✔
102
          s"java.util.Optional<$typeActual>"
2✔
103
        else typeActual
2✔
104
      }
105
      else {
106
        a.t match {
2✔
107
          case ObjectRefType(_, simpleName) => simpleName
2✔
108
          case _                            => memberType.toString
2✔
109
        }
110
      }
111
      val javaId = javaIdentifier(symbol)
2✔
112
      (typeFull, javaId, a)
2✔
113
    }
114

115
    val classDeclMembersStr = classDeclMembers
116
      .flatMap { case (type_, javaId, a) =>
117
        if (a.isDefine) None
2✔
118
        else
119
          Some {
2✔
120
            genResults = genResults
2✔
121
              .copy(fields = genResults.fields + (javaId -> type_))
2✔
122
            if (genOpts.genRecords)
2✔
123
              s"$type_ $javaId" + dbg("<decl>")
2✔
124
            else
125
              s"public final $type_ $javaId;" + dbg("<decl>")
2✔
126
          }
127
      }
128
      .mkString(if (genOpts.genRecords) ",\n  " else "\n  ")
2✔
129

130
    val classMemberGettersStr = if (genOpts.genGetters) {
2✔
131
      classDeclMembers
132
        .filterNot(_._3.isDefine)
133
        .map { case (typ, javaId, _) =>
134
          val getter = s"get${javaId.capitalize}"
135
          genResults = genResults
136
            .copy(getters = genResults.getters + (getter -> typ))
137
          s"public final $typ $getter() { return $javaId; }"
138
        }
139
        .mkString("\n  ", "\n  ", "")
2✔
140
    }
141
    else ""
2✔
142

143
    val membersStr = {
144
      val defined = results.map(_._2.definition).filter(_.nonEmpty)
2✔
145
      if (defined.isEmpty) ""
2✔
146
      else {
147
        defined.map(_.replaceAll("\n", "\n  ")).mkString("\n  ")
2✔
148
      }
149
    }
150

151
    implicit val listAccessors =
152
      collection.mutable.LinkedHashMap[String, String]()
2✔
153

154
    val ctorMembersStr = if (genOpts.genRecords) {
2✔
155
      results
156
        .flatMap { case (symbol, res, a) =>
157
          a.defineCase match {
158
            case Some(dc) if dc.isAbstract =>
159
              throw ObjectDefinitionException(
160
                s"record cannot be abstract: $symbol"
161
              )
162
            case Some(_: ExtendsDefineCase) =>
163
              throw ObjectDefinitionException(
164
                s"record cannot extend classes: $symbol"
165
              )
166
            case Some(_) =>
167
              None
168
            case None =>
169
              Some(
170
                instance(a, res, path = escapeString(symbol), isRoot = isRoot)
171
              )
172
          }
173
        }
174
        .mkString("this(\n      ", ",\n      ", "\n    );")
2✔
175
    }
176
    else {
177
      results
178
        .flatMap { case (symbol, res, a) =>
179
          if (a.isDefine) None
180
          else
181
            Some {
182
              val javaId = javaIdentifier(symbol)
183
              "this." + javaId + " = " + instance(
184
                a,
185
                res,
186
                path = escapeString(symbol)
187
              ) + ";"
188
            }
189
        }
190
        .mkString("\n    ")
2✔
191
    }
192

193
    val elemAccessorsStr = {
194
      val objOnes =
195
        if (listAccessors.isEmpty) ""
2✔
196
        else {
197
          "\n" + listAccessors.keys.toList.sorted
2✔
198
            .map { methodName =>
199
              listAccessors(methodName)
200
            }
201
            .mkString("\n")
2✔
202
        }
203
      val rootOnes =
204
        if (!isRoot) ""
2✔
205
        else {
206
          if (rootListAccessors.isEmpty) ""
2✔
207
          else {
208
            "\n\n" + rootListAccessors.keys.toList.sorted
2✔
209
              .map { methodName =>
210
                rootListAccessors(methodName)
211
              }
212
              .mkString("\n")
2✔
213
          }
214
        }
215
      objOnes + rootOnes
2✔
216
    }
217

218
    val rootAuxClasses = if (isRoot) {
219
      javaDef("$TsCfgValidator")
2✔
220
    }
221
    else ""
2✔
222

223
    val (ctorParams, errHandlingDecl, errHandlingDispatch) = if (isRoot) {
2✔
224
      (
225
        "com.typesafe.config.Config c",
226
        if (genOpts.genRecords)
227
          ""
228
        else
229
          """final $TsCfgValidator $tsCfgValidator = new $TsCfgValidator();
230
            |    final java.lang.String parentPath = "";
231
            |    """.stripMargin,
232
        s"""
233
             |    $$tsCfgValidator.validate();""".stripMargin
234
      )
235
    }
236
    else
237
      (
238
        "com.typesafe.config.Config c, java.lang.String parentPath, $TsCfgValidator $tsCfgValidator",
239
        "",
240
        ""
241
      )
242

243
    val (extendsString, superString) = {
2✔
244
      annTypeForAbstractClassName match {
245
        case None =>
246
          ("", "")
247

248
        case Some(annType) =>
249
          annType.nameIsImplementsIsExternal match {
250
            case Some((cn, isImplements, isExternal)) =>
251
              if (isImplements)
252
                (s"implements $cn ", "")
253
              else if (isExternal)
254
                (s"extends $cn ", "")
255
              else
256
                (
257
                  s"extends $cn ",
258
                  "\n    super(c, parentPath, $tsCfgValidator);"
259
                )
260

261
            case None =>
262
              ("", "")
263
          }
264
      }
265
    }
266

267
    val classStr =
268
      ot match {
269
        case _: ObjectType if genOpts.genRecords =>
2✔
270
          val beginningBody =
271
            if (isRoot)
272
              "\n  static final $TsCfgValidator $tsCfgValidator = new $TsCfgValidator();\n"
1✔
273
            else ""
2✔
274

275
          s"""public ${if (isRoot) "" else "static "}record $classNameAdjusted(
276
             |  $classDeclMembersStr$classMemberGettersStr
277
             |) $extendsString{$beginningBody
278
             |  $membersStr
279
             |  public $classNameAdjusted($ctorParams) {$superString
280
             |    $errHandlingDecl$ctorMembersStr$errHandlingDispatch
281
             |  }$elemAccessorsStr
282
             |$rootAuxClasses}
283
             |""".stripMargin
2✔
284

285
        case _: ObjectType =>
2✔
286
          val staticStr = if (isRoot) "" else "static "
2✔
287
          s"""public ${staticStr}class $classNameAdjusted $extendsString{
288
             |  $classDeclMembersStr$classMemberGettersStr
289
             |  $membersStr
290
             |  public $classNameAdjusted($ctorParams) {$superString
291
             |    $errHandlingDecl$ctorMembersStr$errHandlingDispatch
292
             |  }$elemAccessorsStr
293
             |$rootAuxClasses}
294
             |""".stripMargin
2✔
295

296
        case _: AbstractObjectType =>
297
          s"""public abstract static class $classNameAdjusted $extendsString{
298
             |  $classDeclMembersStr$classMemberGettersStr
299
             |  $membersStr
300
             |  public $classNameAdjusted($ctorParams) {$superString
301
             |    $errHandlingDecl$ctorMembersStr$errHandlingDispatch
302
             |  }$elemAccessorsStr
303
             |}
304
             |""".stripMargin
2✔
305
      }
306

307
    val baseType =
308
      classNamePrefixOpt.getOrElse("") + classNameAdjusted + dbg("<Y>")
2✔
309
    Res(ot, javaType = BaseJavaType(baseType), definition = classStr)
2✔
310
  }
311

312
  private def generateForObjRef(ort: ObjectRefType): Res = {
313
    val className = ort.simpleName
2✔
314
    genResults = genResults.copy(classNames = genResults.classNames + className)
2✔
315

316
    Res(ort, javaType = BaseJavaType(className + dbg("<X>")))
2✔
317
  }
318

319
  private def generateForList(
320
      lt: ListType,
321
      classNamePrefixOpt: Option[String],
322
      className: String
323
  ): Res = {
324
    val className2 =
325
      className + (if (className.endsWith("$Elm")) "" else "$Elm")
2✔
326
    val elem        = generate(lt.t, classNamePrefixOpt, className2)
2✔
327
    val elemRefType = toObjectType(elem.javaType)
2✔
328
    Res(lt, javaType = ListJavaType(elemRefType), definition = elem.definition)
2✔
329
  }
330

331
  private def generateForBasic(b: BasicType): Res = {
332
    Res(
2✔
333
      b,
334
      javaType = BaseJavaType(name = b match {
2✔
335
        case STRING  => "java.lang.String"
2✔
336
        case INTEGER => "int"
2✔
337
        case LONG    => "long"
2✔
338
        case DOUBLE  => "double"
2✔
339
        case BOOLEAN => "boolean"
2✔
340
        case SIZE    => "long"
2✔
341
        case DURATION(_) =>
342
          if (genOpts.useDurations) "java.time.Duration" else "long"
2✔
343
      })
344
    )
345
  }
346

347
  private def generateForEnum(
348
      et: EnumObjectType,
349
      classNamePrefixOpt: Option[String] = None,
350
      className: String,
351
  ): Res = {
352

353
    val classNameAdjusted = adjustClassName(className)
1✔
354
    val members           = et.members.mkString("", ",\n  ", ";")
1✔
355
    val str =
356
      s"""|public enum $classNameAdjusted {
357
         |  $members
358
         |}""".stripMargin
1✔
359

360
    val baseType = classNamePrefixOpt.getOrElse("") + classNameAdjusted
1✔
361
    Res(et, javaType = BaseJavaType(baseType), definition = str)
1✔
362
  }
363

364
  private val rootListAccessors =
365
    collection.mutable.LinkedHashMap[String, String]()
2✔
366

367
  /** Avoids duplicate class names as they are not supported by Java (the
368
    * namespaces implied by nesting are not enough to distinguish inner classes
369
    * with the same name)
370
    */
371
  private def adjustClassName(className: String): String = {
372
    classNameCounter.get(className) match {
2✔
373
      case None =>
2✔
374
        classNameCounter.put(className, 1)
2✔
375
        className
376
      case Some(counter) =>
2✔
377
        classNameCounter.put(className, counter + 1)
2✔
378
        className + (counter + 1)
2✔
379
    }
380
  }
381

382
  private val classNameCounter = collection.mutable.HashMap[String, Int]()
2✔
383

384
  private def toObjectType(javaType: JavaType): JavaType = javaType match {
385
    case BaseJavaType(name) =>
386
      name match {
2✔
387
        case "int"     => BaseJavaType("java.lang.Integer")
2✔
388
        case "long"    => BaseJavaType("java.lang.Long")
2✔
389
        case "double"  => BaseJavaType("java.lang.Double")
2✔
390
        case "boolean" => BaseJavaType("java.lang.Boolean")
2✔
391
        case _         => javaType
2✔
392
      }
393
    case other => other
2✔
394
  }
395

396
  private def instance(
397
      a: AnnType,
398
      res: Res,
399
      path: String,
400
      isRoot: Boolean = false
401
  )(implicit
402
      listAccessors: collection.mutable.LinkedHashMap[String, String]
403
  ): String = {
404

405
    val objRefResolution: Option[String] = a.t match {
2✔
406
      case ort: ObjectRefType => objectRefInstance(ort, res, path)
2✔
407
      case _                  => None
2✔
408
    }
409

410
    objRefResolution.getOrElse {
2✔
411
      a.t match {
2✔
412
        case bt: BasicType    => basicInstance(a, bt, path)
2✔
413
        case _: ObjectAbsType => objectInstance(a, res, path, isRoot)
2✔
414
        case lt: ListType     => listInstance(a, lt, res, path)
2✔
415
      }
416
    }
417
  }
418

419
  private def objectRefInstance(
420
      ort: ObjectRefType,
421
      res: Res,
422
      path: String
423
  ): Option[String] = {
424
    val namespace = rootNamespace.resolve(ort.namespace)
2✔
425
    namespace.getDefine(ort.simpleName) flatMap { t =>
2✔
426
      t match {
427
        case _: EnumObjectType => Some(enumInstance(res, path))
1✔
428
        case _                 => None
2✔
429
      }
430
    }
431
  }
432

433
  private def enumInstance(res: Res, path: String): String = {
434
    val className = res.javaType.toString
1✔
435
    // for now, for java, just let `.valueOf` complain if the value is invalid:
436
    s"""$className.valueOf(c.getString("$path"))"""
437
  }
438

439
  private def objectInstance(
440
      a: AnnType,
441
      res: Res,
442
      path: String,
443
      isRoot: Boolean
444
  )(implicit
445
      listAccessors: collection.mutable.Map[String, String]
446
  ): String = {
447
    val className = res.javaType.toString
2✔
448

449
    val ppArg =
450
      if (isRoot)
451
        s""", "$path.", $$tsCfgValidator"""
1✔
452
      else
453
        s""", parentPath + "$path.", $$tsCfgValidator"""
2✔
454

455
    def reqConfigCall = {
456
      val methodName = "$_reqConfig"
1✔
457
      listAccessors += methodName -> javaDef(methodName)
1✔
458
      s"""$methodName(parentPath, c, "$path", $$tsCfgValidator)"""
459
    }
460

461
    if (genOpts.assumeAllRequired)
2✔
462
      s"""new $className($reqConfigCall$ppArg)"""
1✔
463
    else if (a.optional) {
2✔
464
      if (genOpts.useOptionals) {
2✔
465
        s"""c.$hasPath("$path") ? java.util.Optional.of(new $className(c.getConfig("$path")$ppArg)) : java.util.Optional.empty()"""
2✔
466
      }
467
      else {
468
        s"""c.$hasPath("$path") ? new $className(c.getConfig("$path")$ppArg) : null"""
2✔
469
      }
470
    }
471
    else {
472
      // TODO revisit #33 handling of object as always optional
473
      s"""c.$hasPath("$path") ? new $className(c.getConfig("$path")$ppArg) : new $className(com.typesafe.config.ConfigFactory.parseString("$path{}")$ppArg)"""
2✔
474
    }
475
  }
476

477
  private def listInstance(a: AnnType, lt: ListType, res: Res, path: String)(
478
      implicit listAccessors: collection.mutable.Map[String, String]
479
  ): String = {
480
    val javaType = res.javaType.asInstanceOf[ListJavaType]
2✔
481
    val base     = listMethodName(javaType, lt, path)
2✔
482
    if (a.optional) {
2✔
483
      if (genOpts.useOptionals) {
2✔
484
        s"""c.$hasPath("$path") ? java.util.Optional.of($base) : java.util.Optional.empty()"""
2✔
485
      }
486
      else {
487
        s"""c.$hasPath("$path") ? $base : null"""
2✔
488
      }
489
    }
490
    else base
2✔
491
  }
492

493
  private def basicInstance(a: AnnType, bt: BasicType, path: String)(implicit
494
      listAccessors: collection.mutable.Map[String, String]
495
  ): String = {
496
    val getter = tsConfigUtil.basicGetter(bt, path, genOpts.useDurations)
2✔
497

498
    a.default match {
2✔
499
      case Some(v) =>
2✔
500
        val value = tsConfigUtil.basicValue(a.t, v, genOpts.useDurations)
2✔
501
        (bt, value) match {
502
          case (BOOLEAN, "true")  => s"""!c.$hasPath("$path") || c.$getter"""
503
          case (BOOLEAN, "false") => s"""c.$hasPath("$path") && c.$getter"""
504
          case (DURATION(_), duration) if genOpts.useDurations =>
2✔
505
            s"""c.$hasPath("$path") ? c.$getter : java.time.Duration.parse("$duration")"""
506
          case _ => s"""c.$hasPath("$path") ? c.$getter : $value"""
507
        }
508

509
      case None if a.optional && genOpts.useOptionals =>
2✔
510
        s"""c.$hasPath("$path") ? java.util.Optional.of(c.$getter) : java.util.Optional.empty()"""
2✔
511
      case None if a.optional =>
2✔
512
        s"""c.$hasPath("$path") ? c.$getter : null"""
2✔
513

514
      case _ =>
515
        bt match {
2✔
516
          case DURATION(_) => s"""c.$getter"""
2✔
517
          case _ =>
2✔
518
            val (methodName, methodCall) =
2✔
519
              tsConfigUtil.basicRequiredGetter(bt, path)
520
            listAccessors += methodName -> javaDef(methodName)
2✔
521
            methodCall
522
        }
523
    }
524
  }
525

526
  private def listMethodName(
527
      javaType: ListJavaType,
528
      lt: ListType,
529
      path: String
530
  )(implicit
531
      listAccessors: collection.mutable.Map[String, String],
532
      methodNames: MethodNames
533
  ): String = {
534

535
    val (_, methodName) = rec(javaType, lt, "")
2✔
536
    methodName + s"""(c.getList("$path"), parentPath, $$tsCfgValidator)"""
2✔
537
  }
538

539
  private def rec(ljt: ListJavaType, lt: ListType, prefix: String)(implicit
540
      listAccessors: collection.mutable.Map[String, String],
541
      methodNames: MethodNames
542
  ): (Boolean, String) = {
543

544
    val (isBasic, elemMethodName) = ljt.jt match {
2✔
545
      case bst: BaseJavaType =>
546
        val basic      = lt.t.isInstanceOf[BasicType]
547
        val methodName = baseName(lt.t, bst.toString)
548
        if (basic) {
549
          rootListAccessors += methodName -> methodNames
550
            .basicElemAccessDefinition(methodName)
551
          rootListAccessors += methodNames.expE -> methodNames.expEDef
552
        }
553
        (basic, methodName)
554

555
      case lst: ListJavaType =>
556
        rec(lst, lt.t.asInstanceOf[ListType], prefix + methodNames.listPrefix)
557
    }
558

559
    val (methodName, methodBody) =
2✔
560
      listMethodDefinition(elemMethodName, ljt.jt, lt)
561

562
    if (isBasic)
563
      rootListAccessors += methodName -> methodBody
2✔
564
    else
565
      listAccessors += methodName -> methodBody
2✔
566

567
    (isBasic, methodName)
2✔
568
  }
569

570
  private def baseName(t: Type, name: String)(implicit
571
      methodNames: MethodNames
572
  ): String = t match {
573
    case STRING      => methodNames.strA
2✔
574
    case INTEGER     => methodNames.intA
2✔
575
    case LONG        => methodNames.lngA
2✔
576
    case DOUBLE      => methodNames.dblA
2✔
577
    case BOOLEAN     => methodNames.blnA
2✔
578
    case SIZE        => methodNames.sizA
2✔
NEW
579
    case DURATION(_) => methodNames.durA
×
580

581
    case _: ObjectAbsType => name.replace('.', '_')
2✔
582

583
    case _: ListType => throw new AssertionError()
×
584
  }
585

586
  private def listMethodDefinition(
587
      elemMethodName: String,
588
      javaType: JavaType,
589
      lt: ListType
590
  )(implicit methodNames: MethodNames): (String, String) = {
591

592
    val elem = if (elemMethodName.startsWith(methodNames.listPrefix)) {
2✔
593
      s"$elemMethodName((com.typesafe.config.ConfigList)cv, parentPath, $$tsCfgValidator)"
2✔
594
    }
595
    else if (elemMethodName.startsWith("$")) {
2✔
596
      s"$elemMethodName(cv)"
2✔
597
    }
598
    else {
2✔
599
      val adjusted = elemMethodName.replace("_", ".")
2✔
600
      val objRefResolution = lt.t match {
2✔
601
        case ort: ObjectRefType =>
2✔
602
          val namespace = rootNamespace.resolve(ort.namespace)
2✔
603
          namespace.getDefine(ort.simpleName) flatMap { t =>
2✔
604
            t match {
605
              case _: EnumObjectType =>
606
                Some(s"""$adjusted.valueOf(cv.unwrapped().toString())""")
1✔
607
              case _ => None
2✔
608
            }
609
          }
610

611
        case _ => None
2✔
612
      }
613
      objRefResolution.getOrElse {
2✔
614
        s"new $adjusted(((com.typesafe.config.ConfigObject)cv).toConfig(), parentPath, $$tsCfgValidator)"
615
      }
616
    }
617

618
    val methodName = methodNames.listPrefix + elemMethodName
2✔
619
    val methodDef =
620
      s"""  private static java.util.List<$javaType> $methodName(com.typesafe.config.ConfigList cl, java.lang.String parentPath, $$TsCfgValidator $$tsCfgValidator) {
621
         |    java.util.ArrayList<$javaType> al = new java.util.ArrayList<>();
622
         |    for (com.typesafe.config.ConfigValue cv: cl) {
623
         |      al.add($elem);
624
         |    }
625
         |    return java.util.Collections.unmodifiableList(al);
626
         |  }""".stripMargin
2✔
627
    (methodName, methodDef)
2✔
628
  }
629
}
630

631
object JavaGen {
632

633
  import tscfg.{model, util}
634

635
  import _root_.java.io.{File, FileWriter, PrintWriter}
636

637
  // $COVERAGE-OFF$
638
  def generate(
639
      filename: String,
640
      showOut: Boolean = false,
641
      assumeAllRequired: Boolean = false,
642
      genGetters: Boolean = false,
643
      genRecords: Boolean = false,
644
      useOptionals: Boolean = false,
645
      useDurations: Boolean = false
646
  ): GenResult = {
647
    val file   = new File("src/main/tscfg/" + filename)
648
    val source = io.Source.fromFile(file).mkString.trim
649

650
    if (showOut)
651
      println("source:\n  |" + source.replaceAll("\n", "\n  |"))
652

653
    val className = "Java" + {
654
      val noPath = filename.substring(filename.lastIndexOf('/') + 1)
655
      val noDef  = noPath.replaceAll("""^def\.""", "")
656
      val symbol = noDef.substring(0, noDef.indexOf('.'))
657
      util.upperFirst(symbol) + "Cfg"
658
    }
659

660
    val rootNamespace = new NamespaceMan
661

662
    val buildResult =
663
      ModelBuilder(rootNamespace, source, assumeAllRequired = assumeAllRequired)
664
    val objectType = buildResult.objectType
665
    if (showOut) {
666
      println(
667
        "\nobjectType:\n  |" + model.util
668
          .format(objectType)
669
          .replaceAll("\n", "\n  |")
670
      )
671
      if (buildResult.warnings.nonEmpty) {
672
        println("warnings:")
673
        buildResult.warnings.foreach(w =>
674
          println(s"   line ${w.line}: ${w.source}: ${w.message}")
675
        )
676
      }
677
    }
678

679
    val genOpts = GenOpts(
680
      "tscfg.example",
681
      className,
682
      genGetters = genGetters,
683
      genRecords = genRecords,
684
      useOptionals = useOptionals,
685
      assumeAllRequired = assumeAllRequired,
686
      useDurations = useDurations
687
    )
688

689
    val generator = new JavaGen(genOpts, rootNamespace)
690

691
    val results = generator.generate(objectType)
692

693
    // println("\n" + results.code)
694

695
    val destFilename = s"src/test/java/tscfg/example/$className.java"
696
    val destFile     = new File(destFilename)
697
    val out          = new PrintWriter(new FileWriter(destFile), true)
698
    out.println(results.code)
699
    out.close()
700
    results
701
  }
702

703
  def main(args: Array[String]): Unit = {
704
    val filename = args.headOption.getOrElse("example/example.conf")
705
    val results  = generate(filename, showOut = true)
706
    println(s"""classNames: ${results.classNames}
707
         |fields    : ${results.fields}
708
      """.stripMargin)
709
  }
710

711
  // $COVERAGE-ON$
712

713
}
714

715
private[java] object defs {
716

717
  abstract sealed class JavaType
718

719
  case class BaseJavaType(name: String) extends JavaType {
720
    override def toString: String = name
2✔
721
  }
722

723
  case class ListJavaType(jt: JavaType) extends JavaType {
724
    override def toString: String = s"java.util.List<$jt>"
725
  }
726

727
  case class Res(typ: Type, javaType: JavaType, definition: String = "")
728

729
}
730

731
private[java] case class MethodNames() {
732
  val strA       = "$_str"
2✔
733
  val intA       = "$_int"
2✔
734
  val lngA       = "$_lng"
2✔
735
  val dblA       = "$_dbl"
2✔
736
  val blnA       = "$_bln"
2✔
737
  val durA       = "$_dur" // TODO review this one as it's not actually used
2✔
738
  val sizA       = "$_siz"
2✔
739
  val expE       = "$_expE"
2✔
740
  val listPrefix = "$_L"
2✔
741

742
  def checkUserSymbol(symbol: String): Unit = {
743
    if (symbol.startsWith("$_"))
2✔
744
      println(
1✔
745
        s"""
746
           |WARNING: Symbol $symbol may cause conflict with generated code.
747
           |         Avoid the $$_ prefix in your spec's identifiers.
748
         """.stripMargin
1✔
749
      )
750
  }
751

752
  // definition of methods used to access list's elements of basic type
753
  val basicElemAccessDefinition: Map[String, String] = {
754
    List(strA, intA, lngA, dblA, blnA, sizA)
2✔
755
      .map(k => k -> javaDef(k))
2✔
756
      .toMap
2✔
757
  }
758

759
  val expEDef: String = javaDef(expE)
2✔
760
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc