2017-08-15 1 views
-2

Utiliser Kotlin Je souhaite définir un DSL pour initialiser une structure. Comme vous pouvez le voir, je veux créer un ensemble de modules ayant chacun un certain nombre de champs.Comment associer élégamment les étiquettes parent et enfant dans une définition Kotlin DSL

class DSL : ModuleDSL() { 

    init { 

     module(Module("myModule")) { 
      field("Test", "Test") 
      field("Test", "Test") 
      field("Test", "Test") 
     } 

     module(Module("myOtherModule")) { 
      field("Test", "Test") 
      field("Test", "Test") 
      field("Test", "Test") 
     } 
    } 
} 

Chaque zone doit être connectée au module. Whit dans la fermeture contenant les définitions de champ, afaik je n'ai pas de référence au module. Quelle est la manière la plus élégante de créer une connexion entre le module et ses champs?

Pour être complet, ceux-ci sont le reste des classes

open class ModuleDSL { 

    fun field(fieldname: String, attributename: String) { 
     println("${fieldname} is named ${attributename}") 
    } 

    fun module(module: Module, function:() -> Unit) { 
     function.invoke() 
    } 

    fun createModel() { 
     println("Create my model") 
    } 

} 

class Module(name: String) { 

    init { 
     println("Create entity ${name}") 
    } 
} 

fun main(args: Array<String>) { 

    val myModuleDsl = DSL() 

    myModuleDsl.createModel() 

} 
+1

Aviez-vous lu les documents? https://kotlinlang.org/docs/reference/type-safe-builders.html –

+0

Oui, c'est un bon. Sur la base de cet exemple, j'ai extrait un exemple que j'ai posté. Voir ci-dessous. –

Répondre

0

Après quelques expériences, j'ai trouvé une solution élégante raisonnable, extraite d'un exemple sur Internet.

La configuration est dans la classe de configuration. Ensemble avec le principal amusant, il ressemble à ceci:

class MyModuleConfigurator : ModuleConfigurator() { 

    fun bld() = 
     module("MyModule1") { 
      +"Text" 
      field { 
       +"Fieldtext 1" 
      } 
      field { 
       +"Fieldtext 2a" 
       +"Fieldtext 2b" 
       form("form in field 2") 
      } 
      form("Form 1") 
     } 

} 

fun main(args: Array<String>) { 
    val myHTML = MyModuleConfigurator() 
    myHTML.bld() 
} 

La classe de configuration est dérivée d'une classe maître qui implémente les balises de configuration. Voir les commentaires pour plus d'informations:

open class ModuleConfigurator { 

    // Triggers on 'module' keyword 
    protected fun module(name: String, init: Module.() -> Unit) { 
     val module = Module(name) 
     module.init() 

     // Add your post DSL processing operation here 
     println(module.toString()) 
    } 

    class Module(val name: String) { 

     // Triggers on 'field' keyword in the 'module' 
     fun field(init: Field.() -> Unit) = initTag(this, init) 

     // Triggers on 'form' keyword 
     fun form(name: String) { 
      println("form with name ${name} in module ${this.name}") 
     } 

     // Triggers on + keyword 
     operator fun String.unaryPlus() { 
      val textElement = ModuleConfigurator.TextElement(this) 
      println("Module unary plus (name = ${textElement})") 
     } 

     // Other module internals 

     private fun initTag(module: Module, init: Field.() -> Unit) { 
      val myField = Field(module) 
      module.add(myField) 
      myField.init() 
     } 

     private fun add(myField: Field) { 
      // Hook to add field to module 
     } 

     override fun toString(): String { 
      return "Module(${name})" 
     } 

    } 

    // Helper to unwrap the string in the Unary Plus 
    class TextElement(val text: String) { 
     override fun toString(): String { 
      return "$text" 
     } 
    } 

    class Field(val module: ModuleConfigurator.Module) { 

     operator fun String.unaryPlus() { 
      val textElement = ModuleConfigurator.TextElement(this) 
      println("Field unary plus in module ${module.toString()}, text = '${textElement}'") 
     } 

    } 

} 

Enjoy!