<commit_before>package tscfg.generators.java

import tscfg.generators.java.javaUtil._
import tscfg.generators._
import tscfg.model._
import tscfg.util.escapeString
import tscfg.codeDefs.javaDef


class JavaGen(genOpts: GenOpts) extends Generator(genOpts) {

  import defs._

  implicit val methodNames = MethodNames()

  def generate(objectType: ObjectType): GenResult = {
    genResults = GenResult()

    methodNames.checkUserSymbol(className)
    val res = generateForObj(objectType, className = className, isRoot = true)

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

    val definition = (packageStr + res.definition).trim
    res.copy(definition = res.definition.trim)
    genResults.copy(code = definition)
  }

  private def generate(typ: Type,
                       classNamePrefixOpt: Option[String],
                       className: String,
                       abstractClassName: Option[String] = None
                      ): Res = typ match {

    case ot: ObjectType => generateForObj(ot, classNamePrefixOpt, className, abstractClassName = abstractClassName)
    case aot: AbstractObjectType =>
      generateForObj(aot, classNamePrefixOpt, className);
    case ot: ObjectRefType => generateForObjRef(ot)
    case lt: ListType => generateForList(lt, classNamePrefixOpt, className)
    case bt: BasicType => generateForBasic(bt)
  }


  private def generateForObj(ot: ObjectRealType,
                             classNamePrefixOpt: Option[String] = None,
                             className: String,
                             isRoot: Boolean = false,
                             abstractClassName: Option[String] = None
                            ): Res = {

    val classNameAdjusted = adjustClassName(className)
    genResults = genResults.copy(classNames = genResults.classNames + classNameAdjusted)

    val symbols = ot.members.keys.toList.sorted
    symbols.foreach(methodNames.checkUserSymbol)

    val results = symbols.map { symbol =>
      val a = ot.members(symbol)
      val res = generate(a.t,
        classNamePrefixOpt = Some(classNameAdjusted + "."),
        className = javaUtil.getClassName(symbol),
        abstractClassName = a.abstractClass
      )
      (symbol, res, a)
    }

    val classDeclMembers = results.map { case (symbol, res, a) =>
      val memberType = res.javaType
      val typ = if (a.optional && a.default.isEmpty) {
        if (genOpts.useOptionals) s"java.util.Optional<${toObjectType(memberType)}>" else s"${toObjectType(memberType)}"
      }
      else {
        memberType
      }
      val javaId = javaIdentifier(symbol)
      (typ, javaId, a)
    }

    val classDeclMembersStr = classDeclMembers.flatMap { case (typ, javaId, a) =>
      if (a.isDefine) None
      else Some {
        val type_ = a.t match {
          case ObjectRefType(ns, simpleName) => simpleName
          case _ => typ
        }
        genResults = genResults.copy(fields = genResults.fields + (javaId -> type_.toString))
        s"public final $type_ $javaId;" + dbg("<decl>")
      }
    }.mkString("\n  ")

    val classMemberGettersStr = if (genOpts.genGetters) {
      classDeclMembers.map { case (typ, javaId, _) =>
        val getter = s"get${javaId.capitalize}"
        genResults = genResults.copy(getters = genResults.getters + (getter -> typ.toString))
        s"public final $typ $getter() { return $javaId; }"
      }.mkString("\n  ", "\n  ", "")
    }
    else ""

    val membersStr = {
      val defined = results.map(_._2.definition).filter(_.nonEmpty)
      if (defined.isEmpty) "" else {
        defined.map(_.replaceAll("\n", "\n  ")).mkString("\n  ")
      }
    }

    implicit val listAccessors = collection.mutable.LinkedHashMap[String, String]()

    val ctorMembersStr = results.flatMap { case (symbol, res, a) =>
      if (a.isDefine) None
      else Some {
        val javaId = javaIdentifier(symbol)
        "this." + javaId + " = " + instance(a, res, path = escapeString(symbol)) + ";"
      }
    }.mkString("\n    ")

    val elemAccessorsStr = {
      val objOnes = if (listAccessors.isEmpty) "" else {
        "\n" + listAccessors.keys.toList.sorted.map { methodName =>
          listAccessors(methodName)
        }.mkString("\n")
      }
      val rootOnes = if (!isRoot) "" else {
        if (rootListAccessors.isEmpty) "" else {
          "\n\n" + rootListAccessors.keys.toList.sorted.map { methodName =>
            rootListAccessors(methodName)
          }.mkString("\n")
        }
      }
      objOnes + rootOnes
    }

    val rootAuxClasses = if (isRoot) {
      javaDef("$TsCfgValidator")
    }
    else ""

    val (ctorParams, errHandlingDecl, errHandlingDispatch) = if (isRoot) {
      ("com.typesafe.config.Config c",
        """final $TsCfgValidator $tsCfgValidator = new $TsCfgValidator();
          |    final java.lang.String parentPath = "";
          |    """.stripMargin,

        s"""
           |    $$tsCfgValidator.validate();""".stripMargin
      )
    }
    else (
      "com.typesafe.config.Config c, java.lang.String parentPath, $TsCfgValidator $tsCfgValidator",
      "",
      ""
    )

    val extendsString = abstractClassName.map("extends " + _).getOrElse("")
    val superString = abstractClassName.map(_ => "super(c, parentPath, $tsCfgValidator);").getOrElse("") // if parent class name is defined a super call is needed

    val classStr =
      ot match {
        case _: ObjectType =>
          s"""public ${
            if (isRoot) "" else "static "
          }class $classNameAdjusted $extendsString {
             |  $classDeclMembersStr$classMemberGettersStr
             |  $membersStr
             |  public $classNameAdjusted($ctorParams) {
             |    $superString
             |    $errHandlingDecl$ctorMembersStr$errHandlingDispatch
             |  }$elemAccessorsStr
             |$rootAuxClasses}
             |""".stripMargin

        case _: AbstractObjectType =>
          s"""public abstract static class $classNameAdjusted {
             |  $classDeclMembersStr$classMemberGettersStr
             |  $membersStr
             |  public $classNameAdjusted($ctorParams) {
             |    $errHandlingDecl$ctorMembersStr$errHandlingDispatch
             |  }$elemAccessorsStr
             |}
             |""".stripMargin
      }

    val baseType = classNamePrefixOpt.getOrElse("") + classNameAdjusted + dbg("<Y>")<commit_msg>addressing review comments on generated java class string format<commit_after>package tscfg.generators.java

import tscfg.generators.java.javaUtil._
import tscfg.generators._
import tscfg.model._
import tscfg.util.escapeString
import tscfg.codeDefs.javaDef


class JavaGen(genOpts: GenOpts) extends Generator(genOpts) {

  import defs._

  implicit val methodNames = MethodNames()

  def generate(objectType: ObjectType): GenResult = {
    genResults = GenResult()

    methodNames.checkUserSymbol(className)
    val res = generateForObj(objectType, className = className, isRoot = true)

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

    val definition = (packageStr + res.definition).trim
    res.copy(definition = res.definition.trim)
    genResults.copy(code = definition)
  }

  private def generate(typ: Type,
                       classNamePrefixOpt: Option[String],
                       className: String,
                       abstractClassName: Option[String] = None
                      ): Res = typ match {

    case ot: ObjectType => generateForObj(ot, classNamePrefixOpt, className, abstractClassName = abstractClassName)
    case aot: AbstractObjectType =>
      generateForObj(aot, classNamePrefixOpt, className);
    case ot: ObjectRefType => generateForObjRef(ot)
    case lt: ListType => generateForList(lt, classNamePrefixOpt, className)
    case bt: BasicType => generateForBasic(bt)
  }


  private def generateForObj(ot: ObjectRealType,
                             classNamePrefixOpt: Option[String] = None,
                             className: String,
                             isRoot: Boolean = false,
                             abstractClassName: Option[String] = None
                            ): Res = {

    val classNameAdjusted = adjustClassName(className)
    genResults = genResults.copy(classNames = genResults.classNames + classNameAdjusted)

    val symbols = ot.members.keys.toList.sorted
    symbols.foreach(methodNames.checkUserSymbol)

    val results = symbols.map { symbol =>
      val a = ot.members(symbol)
      val res = generate(a.t,
        classNamePrefixOpt = Some(classNameAdjusted + "."),
        className = javaUtil.getClassName(symbol),
        abstractClassName = a.abstractClass
      )
      (symbol, res, a)
    }

    val classDeclMembers = results.map { case (symbol, res, a) =>
      val memberType = res.javaType
      val typ = if (a.optional && a.default.isEmpty) {
        if (genOpts.useOptionals) s"java.util.Optional<${toObjectType(memberType)}>" else s"${toObjectType(memberType)}"
      }
      else {
        memberType
      }
      val javaId = javaIdentifier(symbol)
      (typ, javaId, a)
    }

    val classDeclMembersStr = classDeclMembers.flatMap { case (typ, javaId, a) =>
      if (a.isDefine) None
      else Some {
        val type_ = a.t match {
          case ObjectRefType(ns, simpleName) => simpleName
          case _ => typ
        }
        genResults = genResults.copy(fields = genResults.fields + (javaId -> type_.toString))
        s"public final $type_ $javaId;" + dbg("<decl>")
      }
    }.mkString("\n  ")

    val classMemberGettersStr = if (genOpts.genGetters) {
      classDeclMembers.map { case (typ, javaId, _) =>
        val getter = s"get${javaId.capitalize}"
        genResults = genResults.copy(getters = genResults.getters + (getter -> typ.toString))
        s"public final $typ $getter() { return $javaId; }"
      }.mkString("\n  ", "\n  ", "")
    }
    else ""

    val membersStr = {
      val defined = results.map(_._2.definition).filter(_.nonEmpty)
      if (defined.isEmpty) "" else {
        defined.map(_.replaceAll("\n", "\n  ")).mkString("\n  ")
      }
    }

    implicit val listAccessors = collection.mutable.LinkedHashMap[String, String]()

    val ctorMembersStr = results.flatMap { case (symbol, res, a) =>
      if (a.isDefine) None
      else Some {
        val javaId = javaIdentifier(symbol)
        "this." + javaId + " = " + instance(a, res, path = escapeString(symbol)) + ";"
      }
    }.mkString("\n    ")

    val elemAccessorsStr = {
      val objOnes = if (listAccessors.isEmpty) "" else {
        "\n" + listAccessors.keys.toList.sorted.map { methodName =>
          listAccessors(methodName)
        }.mkString("\n")
      }
      val rootOnes = if (!isRoot) "" else {
        if (rootListAccessors.isEmpty) "" else {
          "\n\n" + rootListAccessors.keys.toList.sorted.map { methodName =>
            rootListAccessors(methodName)
          }.mkString("\n")
        }
      }
      objOnes + rootOnes
    }

    val rootAuxClasses = if (isRoot) {
      javaDef("$TsCfgValidator")
    }
    else ""

    val (ctorParams, errHandlingDecl, errHandlingDispatch) = if (isRoot) {
      ("com.typesafe.config.Config c",
        """final $TsCfgValidator $tsCfgValidator = new $TsCfgValidator();
          |    final java.lang.String parentPath = "";
          |    """.stripMargin,

        s"""
           |    $$tsCfgValidator.validate();""".stripMargin
      )
    }
    else (
      "com.typesafe.config.Config c, java.lang.String parentPath, $TsCfgValidator $tsCfgValidator",
      "",
      ""
    )

    val extendsString = abstractClassName.map(cn => s"extends $cn ").getOrElse("")
    val superString = abstractClassName.map(_ => "\n    super(c, parentPath, $tsCfgValidator);").getOrElse("") // if parent class name is defined a super call is needed

    val classStr =
      ot match {
        case _: ObjectType =>
          s"""public ${
            if (isRoot) "" else "static"
          }class $classNameAdjusted $extendsString {
             |  $classDeclMembersStr$classMemberGettersStr
             |  $membersStr
             |  public $classNameAdjusted($ctorParams) {$superString
             |    $errHandlingDecl$ctorMembersStr$errHandlingDispatch
             |  }$elemAccessorsStr
             |$rootAuxClasses}
             |""".stripMargin

        case _: AbstractObjectType =>
          s"""public abstract static class $classNameAdjusted {
             |  $classDeclMembersStr$classMemberGettersStr
             |  $membersStr
             |  public $classNameAdjusted($ctorParams) {
             |    $errHandlingDecl$ctorMembersStr$errHandlingDispatch
             |  }$elemAccessorsStr
             |}
             |""".stripMargin
      }

    val baseType = classNamePrefixOpt.getOrElse("") + classNameAdjusted + dbg("<Y>")
{'fraction_non_alphanumeric': 0.11549867332604963, 'fraction_numerical': 0.00015607928827844546, 'mean_word_length': 2.351202928870293, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 48, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2135485', 'n_tokens_mistral': 3723, 'n_tokens_neox': 3646, 'n_words': 776}