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

carueda / tscfg / 13003738073

28 Jan 2025 04:39AM UTC coverage: 96.323% (-0.4%) from 96.715%
13003738073

push

carueda
improved/fix comment handling

13 of 13 new or added lines in 2 files covered. (100.0%)

4 existing lines in 4 files now uncovered.

943 of 979 relevant lines covered (96.32%)

1.83 hits per line

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

98.23
/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
    methodNames.checkUserSymbol(className)
2✔
25
    var res = generateForObj(objectType, className = className, isRoot = true)
2✔
26

27
    if (genOpts.genRecords) {
2✔
28
      val doc = docUtil.getRootClassDoc(objectType, genOpts, javaIdentifier)
2✔
29
      if (doc.nonEmpty) {
1✔
30
        res = res.copy(definition = doc + res.definition)
2✔
31
      }
32
    }
33

34
    val packageStr = s"package ${genOpts.packageName};\n\n"
35
    val definition = (packageStr + res.definition).trim
2✔
36
    genResults.copy(code = definition)
2✔
37
  }
38

39
  private def generate(
40
      typ: Type,
41
      classNamePrefixOpt: Option[String],
42
      className: String,
43
      annTypeForAbstractClassName: Option[AnnType] = None
44
  ): Res = typ match {
45

46
    case et: EnumObjectType =>
47
      generateForEnum(
1✔
48
        et,
49
        classNamePrefixOpt,
50
        className,
51
        annTypeForAbstractClassName
52
      )
53

54
    case ot: ObjectType =>
55
      generateForObj(
2✔
56
        ot,
57
        classNamePrefixOpt,
58
        className,
59
        annTypeForAbstractClassName = annTypeForAbstractClassName
60
      )
61

62
    case aot: AbstractObjectType =>
63
      generateForObj(
2✔
64
        aot,
65
        classNamePrefixOpt,
66
        className,
67
        annTypeForAbstractClassName = annTypeForAbstractClassName
68
      );
69

70
    case ort: ObjectRefType => generateForObjRef(ort)
2✔
71

72
    case lt: ListType => generateForList(lt, classNamePrefixOpt, className)
2✔
73

74
    case bt: BasicType => generateForBasic(bt)
2✔
75
  }
76

77
  private def generateForObj(
78
      ot: ObjectRealType,
79
      classNamePrefixOpt: Option[String] = None,
80
      className: String,
81
      isRoot: Boolean = false,
82
      annTypeForAbstractClassName: Option[AnnType] = None
83
  ): Res = {
84

85
    val classNameAdjusted = adjustClassName(className)
2✔
86
    genResults =
2✔
87
      genResults.copy(classNames = genResults.classNames + classNameAdjusted)
2✔
88

89
    val symbols = ot.members.keys.toList.sorted
2✔
90
    symbols.foreach(methodNames.checkUserSymbol)
2✔
91

92
    val results = symbols.map { symbol =>
2✔
93
      val a   = ot.members(symbol)
2✔
94
      val doc = docUtil.getDoc(a, genOpts, javaIdentifier)
2✔
95
      var res = generate(
2✔
96
        a.t,
2✔
97
        classNamePrefixOpt = Some(classNameAdjusted + "."),
2✔
98
        className = javaUtil.getClassName(symbol),
2✔
99
        annTypeForAbstractClassName = Some(a)
2✔
100
      )
101
      res = res.copy(definition = doc + res.definition)
2✔
102
      (symbol, res, a)
2✔
103
    }
104

105
    val classDeclMembers = results.map { case (symbol, res, a) =>
2✔
106
      val memberType = res.javaType
2✔
107
      val typeFull = if (a.optional && a.default.isEmpty) {
2✔
108
        val typeActual = a.t match {
2✔
109
          case ObjectRefType(_, simpleName) => simpleName
2✔
110
          case _                            => toObjectType(memberType).toString
2✔
111
        }
112

113
        if (genOpts.useOptionals)
2✔
114
          s"java.util.Optional<$typeActual>"
2✔
115
        else typeActual
2✔
116
      }
117
      else {
118
        a.t match {
2✔
119
          case ObjectRefType(_, simpleName) => simpleName
2✔
120
          case _                            => memberType.toString
2✔
121
        }
122
      }
123
      val javaId = javaIdentifier(symbol)
2✔
124
      (typeFull, javaId, a)
2✔
125
    }
126

127
    val classDeclMembersStr = classDeclMembers
128
      .flatMap { case (type_, javaId, a) =>
129
        if (a.isDefine) None
2✔
130
        else
131
          Some {
2✔
132
            genResults = genResults
2✔
133
              .copy(fields = genResults.fields + (javaId -> type_))
2✔
134
            if (genOpts.genRecords)
2✔
135
              s"$type_ $javaId" + dbg("<decl>")
2✔
136
            else {
2✔
137
              val doc =
138
                docUtil.getDoc(
2✔
139
                  a,
140
                  genOpts,
2✔
141
                  javaIdentifier,
×
142
                  onlyField = true,
2✔
143
                  indent = "  "
2✔
144
                )
145
              doc + s"public final $type_ $javaId;" + dbg("<decl>")
2✔
146
            }
147
          }
148
      }
149
      .mkString(if (genOpts.genRecords) ",\n  " else "\n  ")
2✔
150

151
    val classMemberGettersStr = if (genOpts.genGetters) {
2✔
152
      classDeclMembers
153
        .filterNot(_._3.isDefine)
154
        .map { case (typ, javaId, _) =>
155
          val getter = s"get${javaId.capitalize}"
156
          genResults = genResults
157
            .copy(getters = genResults.getters + (getter -> typ))
158
          s"public final $typ $getter() { return $javaId; }"
159
        }
160
        .mkString("\n  ", "\n  ", "")
2✔
161
    }
162
    else ""
2✔
163

164
    val membersStr = {
165
      val defined = results.map(_._2.definition).filter(_.nonEmpty)
2✔
166
      if (defined.isEmpty) ""
2✔
167
      else {
168
        defined.map(_.replaceAll("\n", "\n  ")).mkString("\n  ")
2✔
169
      }
170
    }
171

172
    implicit val listAccessors =
173
      collection.mutable.LinkedHashMap[String, String]()
2✔
174

175
    val ctorMembersStr = if (genOpts.genRecords) {
2✔
176
      results
177
        .flatMap { case (symbol, res, a) =>
178
          a.defineCase match {
179
            case Some(dc) if dc.isAbstract =>
180
              throw ObjectDefinitionException(
181
                s"record cannot be abstract: $symbol"
182
              )
183
            case Some(_: ExtendsDefineCase) =>
184
              throw ObjectDefinitionException(
185
                s"record cannot extend classes: $symbol"
186
              )
187
            case Some(_) =>
188
              None
189
            case None =>
190
              Some(
191
                instance(a, res, path = escapeString(symbol), isRoot = isRoot)
192
              )
193
          }
194
        }
195
        .mkString("this(\n      ", ",\n      ", "\n    );")
2✔
196
    }
197
    else {
198
      results
199
        .flatMap { case (symbol, res, a) =>
200
          if (a.isDefine) None
201
          else
202
            Some {
203
              val javaId = javaIdentifier(symbol)
204
              "this." + javaId + " = " + instance(
205
                a,
206
                res,
207
                path = escapeString(symbol)
208
              ) + ";"
209
            }
210
        }
211
        .mkString("\n    ")
2✔
212
    }
213

214
    val elemAccessorsStr = {
215
      val objOnes =
216
        if (listAccessors.isEmpty) ""
2✔
217
        else {
218
          "\n" + listAccessors.keys.toList.sorted
2✔
219
            .map { methodName =>
220
              listAccessors(methodName)
221
            }
222
            .mkString("\n")
2✔
223
        }
224
      val rootOnes =
225
        if (!isRoot) ""
2✔
226
        else {
227
          if (rootListAccessors.isEmpty) ""
2✔
228
          else {
229
            "\n\n" + rootListAccessors.keys.toList.sorted
2✔
230
              .map { methodName =>
231
                rootListAccessors(methodName)
232
              }
233
              .mkString("\n")
2✔
234
          }
235
        }
236
      objOnes + rootOnes
2✔
237
    }
238

239
    val rootAuxClasses = if (isRoot) {
240
      javaDef("$TsCfgValidator")
2✔
241
    }
242
    else ""
2✔
243

244
    val (ctorParams, errHandlingDecl, errHandlingDispatch) = if (isRoot) {
2✔
245
      (
246
        "com.typesafe.config.Config c",
247
        if (genOpts.genRecords)
248
          ""
249
        else
250
          """final $TsCfgValidator $tsCfgValidator = new $TsCfgValidator();
251
            |    final java.lang.String parentPath = "";
252
            |    """.stripMargin,
253
        s"""
254
             |    $$tsCfgValidator.validate();""".stripMargin
255
      )
256
    }
257
    else
258
      (
259
        "com.typesafe.config.Config c, java.lang.String parentPath, $TsCfgValidator $tsCfgValidator",
260
        "",
261
        ""
262
      )
263

264
    val (extendsString, superString) = {
2✔
265
      annTypeForAbstractClassName match {
266
        case None =>
267
          ("", "")
268

269
        case Some(annType) =>
270
          annType.nameIsImplementsIsExternal match {
271
            case Some((cn, isImplements, isExternal)) =>
272
              if (isImplements)
273
                (s"implements $cn ", "")
274
              else if (isExternal)
275
                (s"extends $cn ", "")
276
              else
277
                (
278
                  s"extends $cn ",
279
                  "\n    super(c, parentPath, $tsCfgValidator);"
280
                )
281

282
            case None =>
283
              ("", "")
284
          }
285
      }
286
    }
287

288
    val classStr =
289
      ot match {
290
        case _: ObjectType if genOpts.genRecords =>
2✔
291
          val beginningBody =
292
            if (isRoot)
293
              "\n  static final $TsCfgValidator $tsCfgValidator = new $TsCfgValidator();\n"
2✔
294
            else ""
2✔
295

296
          s"""public record $classNameAdjusted(
297
             |  $classDeclMembersStr$classMemberGettersStr
298
             |) $extendsString{$beginningBody
299
             |  $membersStr
300
             |  public $classNameAdjusted($ctorParams) {$superString
301
             |    $errHandlingDecl$ctorMembersStr$errHandlingDispatch
302
             |  }$elemAccessorsStr
303
             |$rootAuxClasses}
304
             |""".stripMargin
2✔
305

306
        case _: ObjectType =>
2✔
307
          val staticStr = if (isRoot) "" else "static "
2✔
308
          s"""public ${staticStr}class $classNameAdjusted $extendsString{
309
             |  $classDeclMembersStr$classMemberGettersStr
310
             |  $membersStr
311
             |  public $classNameAdjusted($ctorParams) {$superString
312
             |    $errHandlingDecl$ctorMembersStr$errHandlingDispatch
313
             |  }$elemAccessorsStr
314
             |$rootAuxClasses}
315
             |""".stripMargin
2✔
316

317
        case _: AbstractObjectType =>
318
          s"""public abstract static class $classNameAdjusted $extendsString{
319
             |  $classDeclMembersStr$classMemberGettersStr
320
             |  $membersStr
321
             |  public $classNameAdjusted($ctorParams) {$superString
322
             |    $errHandlingDecl$ctorMembersStr$errHandlingDispatch
323
             |  }$elemAccessorsStr
324
             |}
325
             |""".stripMargin
2✔
326
      }
327

328
    val baseType =
329
      classNamePrefixOpt.getOrElse("") + classNameAdjusted + dbg("<Y>")
2✔
330
    Res(ot, javaType = BaseJavaType(baseType), definition = classStr)
2✔
331
  }
332

333
  private def generateForObjRef(ort: ObjectRefType): Res = {
334
    val className = ort.simpleName
2✔
335
    genResults = genResults.copy(classNames = genResults.classNames + className)
2✔
336

337
    Res(ort, javaType = BaseJavaType(className + dbg("<X>")))
2✔
338
  }
339

340
  private def generateForList(
341
      lt: ListType,
342
      classNamePrefixOpt: Option[String],
343
      className: String
344
  ): Res = {
345
    val className2 =
346
      className + (if (className.endsWith("$Elm")) "" else "$Elm")
2✔
347
    val elem        = generate(lt.t, classNamePrefixOpt, className2)
2✔
348
    val elemRefType = toObjectType(elem.javaType)
2✔
349
    Res(lt, javaType = ListJavaType(elemRefType), definition = elem.definition)
2✔
350
  }
351

352
  private def generateForBasic(b: BasicType): Res = {
353
    Res(
2✔
354
      b,
355
      javaType = BaseJavaType(name = b match {
2✔
356
        case STRING  => "java.lang.String"
2✔
357
        case INTEGER => "int"
2✔
358
        case LONG    => "long"
2✔
359
        case DOUBLE  => "double"
2✔
360
        case BOOLEAN => "boolean"
2✔
361
        case SIZE    => "long"
2✔
362
        case DURATION(_) =>
363
          if (genOpts.useDurations) "java.time.Duration" else "long"
2✔
364
      })
365
    )
366
  }
367

368
  private def generateForEnum(
369
      et: EnumObjectType,
370
      classNamePrefixOpt: Option[String] = None,
371
      className: String,
372
      annTypeForParentClassName: Option[AnnType],
373
  ): Res = {
374

375
    val classNameAdjusted = adjustClassName(className)
1✔
376
    val membersStr =
377
      et.members
378
        .map { m =>
379
          docUtil.getEnumDoc(m.comments, indent = "  ") + m.name
380
        }
381
        .mkString("", ",\n  ", ";")
1✔
382

383
    val str = {
384
      val doc = docUtil.getEnumDoc(
1✔
UNCOV
385
        annTypeForParentClassName.map(_.docComments).getOrElse(Nil)
×
386
      )
387
      doc + s"""|public enum $classNameAdjusted {
1✔
388
          |  $membersStr
389
          |}""".stripMargin
1✔
390
    }
391
    val baseType = classNamePrefixOpt.getOrElse("") + classNameAdjusted
1✔
392
    Res(et, javaType = BaseJavaType(baseType), definition = str)
1✔
393
  }
394

395
  private val rootListAccessors =
396
    collection.mutable.LinkedHashMap[String, String]()
2✔
397

398
  /** Avoids duplicate class names as they are not supported by Java (the
399
    * namespaces implied by nesting are not enough to distinguish inner classes
400
    * with the same name)
401
    */
402
  private def adjustClassName(className: String): String = {
403
    classNameCounter.get(className) match {
2✔
404
      case None =>
2✔
405
        classNameCounter.put(className, 1)
2✔
406
        className
407
      case Some(counter) =>
2✔
408
        classNameCounter.put(className, counter + 1)
2✔
409
        className + (counter + 1)
2✔
410
    }
411
  }
412

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

415
  private def toObjectType(javaType: JavaType): JavaType = javaType match {
416
    case BaseJavaType(name) =>
417
      name match {
2✔
418
        case "int"     => BaseJavaType("java.lang.Integer")
2✔
419
        case "long"    => BaseJavaType("java.lang.Long")
2✔
420
        case "double"  => BaseJavaType("java.lang.Double")
2✔
421
        case "boolean" => BaseJavaType("java.lang.Boolean")
2✔
422
        case _         => javaType
2✔
423
      }
424
    case other => other
2✔
425
  }
426

427
  private def instance(
428
      a: AnnType,
429
      res: Res,
430
      path: String,
431
      isRoot: Boolean = false
432
  )(implicit
433
      listAccessors: collection.mutable.LinkedHashMap[String, String]
434
  ): String = {
435

436
    val objRefResolution: Option[String] = a.t match {
2✔
437
      case ort: ObjectRefType => objectRefInstance(ort, res, path)
2✔
438
      case _                  => None
2✔
439
    }
440

441
    objRefResolution.getOrElse {
2✔
442
      a.t match {
2✔
443
        case bt: BasicType    => basicInstance(a, bt, path)
2✔
444
        case _: ObjectAbsType => objectInstance(a, res, path, isRoot)
2✔
445
        case lt: ListType     => listInstance(a, lt, res, path)
2✔
446
      }
447
    }
448
  }
449

450
  private def objectRefInstance(
451
      ort: ObjectRefType,
452
      res: Res,
453
      path: String
454
  ): Option[String] = {
455
    val namespace = rootNamespace.resolve(ort.namespace)
2✔
456
    namespace.getDefine(ort.simpleName) flatMap { t =>
2✔
457
      t match {
458
        case _: EnumObjectType => Some(enumInstance(res, path))
1✔
459
        case _                 => None
2✔
460
      }
461
    }
462
  }
463

464
  private def enumInstance(res: Res, path: String): String = {
465
    val className = res.javaType.toString
1✔
466
    // for now, for java, just let `.valueOf` complain if the value is invalid:
467
    s"""$className.valueOf(c.getString("$path"))"""
468
  }
469

470
  private def objectInstance(
471
      a: AnnType,
472
      res: Res,
473
      path: String,
474
      isRoot: Boolean
475
  )(implicit
476
      listAccessors: collection.mutable.Map[String, String]
477
  ): String = {
478
    val className = res.javaType.toString
2✔
479

480
    val ppArg =
481
      if (isRoot)
482
        s""", "$path.", $$tsCfgValidator"""
2✔
483
      else
484
        s""", parentPath + "$path.", $$tsCfgValidator"""
2✔
485

486
    def reqConfigCall = {
487
      val methodName = "$_reqConfig"
1✔
488
      listAccessors += methodName -> javaDef(methodName)
1✔
489
      s"""$methodName(parentPath, c, "$path", $$tsCfgValidator)"""
490
    }
491

492
    if (genOpts.assumeAllRequired)
2✔
493
      s"""new $className($reqConfigCall$ppArg)"""
1✔
494
    else if (a.optional) {
2✔
495
      if (genOpts.useOptionals) {
2✔
496
        s"""c.$hasPath("$path") ? java.util.Optional.of(new $className(c.getConfig("$path")$ppArg)) : java.util.Optional.empty()"""
2✔
497
      }
498
      else {
499
        s"""c.$hasPath("$path") ? new $className(c.getConfig("$path")$ppArg) : null"""
2✔
500
      }
501
    }
502
    else {
503
      // TODO revisit #33 handling of object as always optional
504
      s"""c.$hasPath("$path") ? new $className(c.getConfig("$path")$ppArg) : new $className(com.typesafe.config.ConfigFactory.parseString("$path{}")$ppArg)"""
2✔
505
    }
506
  }
507

508
  private def listInstance(a: AnnType, lt: ListType, res: Res, path: String)(
509
      implicit listAccessors: collection.mutable.Map[String, String]
510
  ): String = {
511
    val javaType = res.javaType.asInstanceOf[ListJavaType]
2✔
512
    val base     = listMethodName(javaType, lt, path)
2✔
513
    if (a.optional) {
2✔
514
      if (genOpts.useOptionals) {
2✔
515
        s"""c.$hasPath("$path") ? java.util.Optional.of($base) : java.util.Optional.empty()"""
2✔
516
      }
517
      else {
518
        s"""c.$hasPath("$path") ? $base : null"""
2✔
519
      }
520
    }
521
    else base
2✔
522
  }
523

524
  private def basicInstance(a: AnnType, bt: BasicType, path: String)(implicit
525
      listAccessors: collection.mutable.Map[String, String]
526
  ): String = {
527
    val getter = tsConfigUtil.basicGetter(bt, path, genOpts.useDurations)
2✔
528

529
    a.default match {
2✔
530
      case Some(v) =>
2✔
531
        val value = tsConfigUtil.basicValue(a.t, v, genOpts.useDurations)
2✔
532
        (bt, value) match {
533
          case (BOOLEAN, "true")  => s"""!c.$hasPath("$path") || c.$getter"""
534
          case (BOOLEAN, "false") => s"""c.$hasPath("$path") && c.$getter"""
535
          case (DURATION(_), duration) if genOpts.useDurations =>
2✔
536
            s"""c.$hasPath("$path") ? c.$getter : java.time.Duration.parse("$duration")"""
537
          case _ => s"""c.$hasPath("$path") ? c.$getter : $value"""
538
        }
539

540
      case None if a.optional && genOpts.useOptionals =>
2✔
541
        s"""c.$hasPath("$path") ? java.util.Optional.of(c.$getter) : java.util.Optional.empty()"""
2✔
542
      case None if a.optional =>
2✔
543
        s"""c.$hasPath("$path") ? c.$getter : null"""
2✔
544

545
      case _ =>
546
        bt match {
2✔
547
          case DURATION(_) => s"""c.$getter"""
2✔
548
          case _ =>
2✔
549
            val (methodName, methodCall) =
2✔
550
              tsConfigUtil.basicRequiredGetter(bt, path)
551
            listAccessors += methodName -> javaDef(methodName)
2✔
552
            methodCall
553
        }
554
    }
555
  }
556

557
  private def listMethodName(
558
      javaType: ListJavaType,
559
      lt: ListType,
560
      path: String
561
  )(implicit
562
      listAccessors: collection.mutable.Map[String, String],
563
      methodNames: MethodNames
564
  ): String = {
565

566
    val (_, methodName) = rec(javaType, lt, "")
2✔
567
    methodName + s"""(c.getList("$path"), parentPath, $$tsCfgValidator)"""
2✔
568
  }
569

570
  private def rec(ljt: ListJavaType, lt: ListType, prefix: String)(implicit
571
      listAccessors: collection.mutable.Map[String, String],
572
      methodNames: MethodNames
573
  ): (Boolean, String) = {
574

575
    val (isBasic, elemMethodName) = ljt.jt match {
2✔
576
      case bst: BaseJavaType =>
577
        val basic      = lt.t.isInstanceOf[BasicType]
578
        val methodName = baseName(lt.t, bst.toString)
579
        if (basic) {
580
          rootListAccessors += methodName -> methodNames
581
            .basicElemAccessDefinition(methodName)
582
          rootListAccessors += methodNames.expE -> methodNames.expEDef
583
        }
584
        (basic, methodName)
585

586
      case lst: ListJavaType =>
587
        rec(lst, lt.t.asInstanceOf[ListType], prefix + methodNames.listPrefix)
588
    }
589

590
    val (methodName, methodBody) =
2✔
591
      listMethodDefinition(elemMethodName, ljt.jt, lt)
592

593
    if (isBasic)
594
      rootListAccessors += methodName -> methodBody
2✔
595
    else
596
      listAccessors += methodName -> methodBody
2✔
597

598
    (isBasic, methodName)
2✔
599
  }
600

601
  private def baseName(t: Type, name: String)(implicit
602
      methodNames: MethodNames
603
  ): String = t match {
604
    case STRING      => methodNames.strA
2✔
605
    case INTEGER     => methodNames.intA
2✔
606
    case LONG        => methodNames.lngA
2✔
607
    case DOUBLE      => methodNames.dblA
2✔
608
    case BOOLEAN     => methodNames.blnA
2✔
609
    case SIZE        => methodNames.sizA
2✔
610
    case DURATION(_) => methodNames.durA
×
611

612
    case _: ObjectAbsType => name.replace('.', '_')
2✔
613

614
    case _: ListType => throw new AssertionError()
×
615
  }
616

617
  private def listMethodDefinition(
618
      elemMethodName: String,
619
      javaType: JavaType,
620
      lt: ListType
621
  )(implicit methodNames: MethodNames): (String, String) = {
622

623
    val elem = if (elemMethodName.startsWith(methodNames.listPrefix)) {
2✔
624
      s"$elemMethodName((com.typesafe.config.ConfigList)cv, parentPath, $$tsCfgValidator)"
2✔
625
    }
626
    else if (elemMethodName.startsWith("$")) {
2✔
627
      s"$elemMethodName(cv)"
2✔
628
    }
629
    else {
2✔
630
      val adjusted = elemMethodName.replace("_", ".")
2✔
631
      val objRefResolution = lt.t match {
2✔
632
        case ort: ObjectRefType =>
2✔
633
          val namespace = rootNamespace.resolve(ort.namespace)
2✔
634
          namespace.getDefine(ort.simpleName) flatMap { t =>
2✔
635
            t match {
636
              case _: EnumObjectType =>
637
                Some(s"""$adjusted.valueOf(cv.unwrapped().toString())""")
1✔
638
              case _ => None
2✔
639
            }
640
          }
641

642
        case _ => None
2✔
643
      }
644
      objRefResolution.getOrElse {
2✔
645
        s"new $adjusted(((com.typesafe.config.ConfigObject)cv).toConfig(), parentPath, $$tsCfgValidator)"
646
      }
647
    }
648

649
    val methodName = methodNames.listPrefix + elemMethodName
2✔
650
    val methodDef =
651
      s"""  private static java.util.List<$javaType> $methodName(com.typesafe.config.ConfigList cl, java.lang.String parentPath, $$TsCfgValidator $$tsCfgValidator) {
652
         |    java.util.ArrayList<$javaType> al = new java.util.ArrayList<>();
653
         |    for (com.typesafe.config.ConfigValue cv: cl) {
654
         |      al.add($elem);
655
         |    }
656
         |    return java.util.Collections.unmodifiableList(al);
657
         |  }""".stripMargin
2✔
658
    (methodName, methodDef)
2✔
659
  }
660
}
661

662
object JavaGen {
663

664
  import tscfg.{model, util}
665

666
  import _root_.java.io.{File, FileWriter, PrintWriter}
667

668
  // $COVERAGE-OFF$
669
  def generate(
670
      filename: String,
671
      genDoc: Boolean = true,
672
      showOut: Boolean = false,
673
      assumeAllRequired: Boolean = false,
674
      genGetters: Boolean = false,
675
      genRecords: Boolean = false,
676
      useOptionals: Boolean = false,
677
      useDurations: Boolean = false
678
  ): GenResult = {
679
    val file   = new File("src/main/tscfg/" + filename)
680
    val source = io.Source.fromFile(file).mkString.trim
681

682
    if (showOut)
683
      println("source:\n  |" + source.replaceAll("\n", "\n  |"))
684

685
    val className = "Java" + {
686
      val noPath = filename.substring(filename.lastIndexOf('/') + 1)
687
      val noDef  = noPath.replaceAll("""^def\.""", "")
688
      val symbol = noDef.substring(0, noDef.indexOf('.'))
689
      util.upperFirst(symbol) + "Cfg"
690
    }
691

692
    val rootNamespace = new NamespaceMan
693

694
    val buildResult =
695
      ModelBuilder(rootNamespace, source, assumeAllRequired = assumeAllRequired)
696
    val objectType = buildResult.objectType
697
    if (showOut) {
698
      println(
699
        "\nobjectType:\n  |" + model.util
700
          .format(objectType)
701
          .replaceAll("\n", "\n  |")
702
      )
703
      if (buildResult.warnings.nonEmpty) {
704
        println("warnings:")
705
        buildResult.warnings.foreach(w =>
706
          println(s"   line ${w.line}: ${w.source}: ${w.message}")
707
        )
708
      }
709
    }
710

711
    val genOpts = GenOpts(
712
      "tscfg.example",
713
      className,
714
      genDoc = genDoc,
715
      genGetters = genGetters,
716
      genRecords = genRecords,
717
      useOptionals = useOptionals,
718
      assumeAllRequired = assumeAllRequired,
719
      useDurations = useDurations
720
    )
721

722
    val generator = new JavaGen(genOpts, rootNamespace)
723

724
    val results = generator.generate(objectType)
725

726
    // println("\n" + results.code)
727

728
    val destFilename = s"src/test/java/tscfg/example/$className.java"
729
    val destFile     = new File(destFilename)
730
    val out          = new PrintWriter(new FileWriter(destFile), true)
731
    out.println(results.code)
732
    out.close()
733
    results
734
  }
735

736
  def main(args: Array[String]): Unit = {
737
    val filename = args.headOption.getOrElse("example/example.conf")
738
    val results  = generate(filename, showOut = true)
739
    println(s"""classNames: ${results.classNames}
740
         |fields    : ${results.fields}
741
      """.stripMargin)
742
  }
743

744
  // $COVERAGE-ON$
745

746
}
747

748
private[java] object defs {
749

750
  abstract sealed class JavaType
751

752
  case class BaseJavaType(name: String) extends JavaType {
753
    override def toString: String = name
2✔
754
  }
755

756
  case class ListJavaType(jt: JavaType) extends JavaType {
757
    override def toString: String = s"java.util.List<$jt>"
758
  }
759

760
  case class Res(typ: Type, javaType: JavaType, definition: String = "")
761

762
}
763

764
private[java] case class MethodNames() {
765
  val strA       = "$_str"
2✔
766
  val intA       = "$_int"
2✔
767
  val lngA       = "$_lng"
2✔
768
  val dblA       = "$_dbl"
2✔
769
  val blnA       = "$_bln"
2✔
770
  val durA       = "$_dur" // TODO review this one as it's not actually used
2✔
771
  val sizA       = "$_siz"
2✔
772
  val expE       = "$_expE"
2✔
773
  val listPrefix = "$_L"
2✔
774

775
  def checkUserSymbol(symbol: String): Unit = {
776
    if (symbol.startsWith("$_"))
2✔
777
      println(
1✔
778
        s"""
779
           |WARNING: Symbol $symbol may cause conflict with generated code.
780
           |         Avoid the $$_ prefix in your spec's identifiers.
781
         """.stripMargin
1✔
782
      )
783
  }
784

785
  // definition of methods used to access list's elements of basic type
786
  val basicElemAccessDefinition: Map[String, String] = {
787
    List(strA, intA, lngA, dblA, blnA, sizA)
2✔
788
      .map(k => k -> javaDef(k))
2✔
789
      .toMap
2✔
790
  }
791

792
  val expEDef: String = javaDef(expE)
2✔
793
}
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